blob: 5d6d395037c23173a986f369001b6c084870b01f (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
# frozen_string_literal: true
module Tasks
module Gitlab
module Assets
FOSS_ASSET_FOLDERS = %w[app/assets fixtures/emojis vendor/assets].freeze
EE_ASSET_FOLDERS = %w[ee/app/assets].freeze
JH_ASSET_FOLDERS = %w[jh/app/assets].freeze
# In the new caching strategy, we check the assets hash sum *before* compiling
# the app/assets/javascripts/locale/**/app.js files. That means the hash sum
# must depend on locale/**/gitlab.po.
JS_ASSET_PATTERNS = %w[*.js config/**/*.js locale/**/gitlab.po].freeze
JS_ASSET_FILES = %w[
package.json
yarn.lock
babel.config.js
config/webpack.config.js
].freeze
# Ruby gems might emit assets which have an impact on compilation
# or have a direct impact on asset compilation (e.g. scss) and therefore
# we should compile when these change
RAILS_ASSET_FILES = %w[
Gemfile
Gemfile.lock
].freeze
EXCLUDE_PATTERNS = %w[
app/assets/javascripts/locale/**/app.js
].freeze
PUBLIC_ASSETS_DIR = 'public/assets'
HEAD_ASSETS_SHA256_HASH_ENV = 'GITLAB_ASSETS_HASH'
CACHED_ASSETS_SHA256_HASH_FILE = 'cached-assets-hash.txt'
def self.master_assets_sha256
@master_assets_sha256 ||=
if File.exist?(Tasks::Gitlab::Assets::CACHED_ASSETS_SHA256_HASH_FILE)
File.read(Tasks::Gitlab::Assets::CACHED_ASSETS_SHA256_HASH_FILE)
else
'missing!'
end
end
def self.head_assets_sha256
@head_assets_sha256 ||= ENV.fetch(Tasks::Gitlab::Assets::HEAD_ASSETS_SHA256_HASH_ENV) do
Tasks::Gitlab::Assets.sha256_of_assets_impacting_compilation(verbose: false)
end
end
def self.sha256_of_assets_impacting_compilation(verbose: true)
start_time = Time.now
asset_files = assets_impacting_compilation
puts "Generating the SHA256 hash for #{asset_files.size} Webpack-related assets..." if verbose
assets_sha256 = asset_files.map { |asset_file| Digest::SHA256.file(asset_file).hexdigest }.join
Digest::SHA256.hexdigest(assets_sha256).tap { |sha256| puts "=> SHA256 generated in #{Time.now - start_time}: #{sha256}" if verbose }
end
# Files listed here should match the list in:
# .assets-compilation-patterns in .gitlab/ci/rules.gitlab-ci.yml
# So we make sure that any impacting changes we do rebuild cache
def self.assets_impacting_compilation
assets_folders = FOSS_ASSET_FOLDERS
assets_folders += EE_ASSET_FOLDERS if ::Gitlab.ee?
assets_folders += JH_ASSET_FOLDERS if ::Gitlab.jh?
asset_files = Dir.glob(JS_ASSET_PATTERNS)
asset_files += JS_ASSET_FILES
asset_files += RAILS_ASSET_FILES
assets_folders.each do |folder|
asset_files.concat(Dir.glob(["#{folder}/**/*.*"]))
end
asset_files - Dir.glob(EXCLUDE_PATTERNS)
end
private_class_method :assets_impacting_compilation
end
end
end
namespace :gitlab do
namespace :assets do
desc 'GitLab | Assets | Return the hash sum of all frontend assets'
task :hash_sum do
print Tasks::Gitlab::Assets.sha256_of_assets_impacting_compilation(verbose: false)
end
desc 'GitLab | Assets | Compile all frontend assets'
task :compile do
require 'fileutils'
require_dependency 'gitlab/task_helpers'
puts "Assets SHA256 for `master`: #{Tasks::Gitlab::Assets.master_assets_sha256.inspect}"
puts "Assets SHA256 for `HEAD`: #{Tasks::Gitlab::Assets.head_assets_sha256.inspect}"
if Tasks::Gitlab::Assets.head_assets_sha256 != Tasks::Gitlab::Assets.master_assets_sha256
FileUtils.rm_rf([Tasks::Gitlab::Assets::PUBLIC_ASSETS_DIR] + Dir.glob('app/assets/javascripts/locale/**/app.js'))
# gettext:po_to_json needs to run before rake:assets:precompile because
# app/assets/javascripts/locale/**/app.js are pre-compiled by Sprockets
Gitlab::TaskHelpers.invoke_and_time_task('gettext:po_to_json')
Gitlab::TaskHelpers.invoke_and_time_task('rake:assets:precompile')
log_path = ENV['WEBPACK_COMPILE_LOG_PATH']
cmd = 'yarn webpack'
cmd += " > #{log_path}" if log_path
unless system(cmd)
abort 'Error: Unable to compile webpack production bundle.'.color(:red)
end
puts "Written webpack stdout log to #{log_path}" if log_path
puts "You can inspect the webpack log here: #{ENV['CI_JOB_URL']}/artifacts/file/#{log_path}" if log_path && ENV['CI_JOB_URL']
Gitlab::TaskHelpers.invoke_and_time_task('gitlab:assets:fix_urls')
Gitlab::TaskHelpers.invoke_and_time_task('gitlab:assets:check_page_bundle_mixins_css_for_sideeffects')
end
end
desc 'GitLab | Assets | Clean up old compiled frontend assets'
task clean: ['rake:assets:clean']
desc 'GitLab | Assets | Remove all compiled frontend assets'
task purge: ['rake:assets:clobber']
desc 'GitLab | Assets | Uninstall frontend dependencies'
task purge_modules: ['yarn:clobber']
desc 'GitLab | Assets | Fix all absolute url references in CSS'
task :fix_urls do
css_files = Dir['public/assets/*.css']
css_files.each do |file|
# replace url(/assets/*) with url(./*)
puts "Fixing #{file}"
system "sed", "-i", "-e", 's/url(\([\"\']\?\)\/assets\//url(\1.\//g', file
# rewrite the corresponding gzip file (if it exists)
gzip = "#{file}.gz"
next unless File.exist?(gzip)
puts "Fixing #{gzip}"
FileUtils.rm(gzip)
mtime = File.stat(file).mtime
File.open(gzip, 'wb+') do |f|
gz = Zlib::GzipWriter.new(f, Zlib::BEST_COMPRESSION)
gz.mtime = mtime
gz.write File.binread(file)
gz.close
File.utime(mtime, mtime, f.path)
end
end
end
desc 'GitLab | Assets | Compile vendor assets'
task :vendor do
unless system('yarn webpack-vendor')
abort 'Error: Unable to compile webpack DLL.'.color(:red)
end
end
desc 'GitLab | Assets | Check that scss mixins do not introduce any sideffects'
task :check_page_bundle_mixins_css_for_sideeffects do
unless system('./scripts/frontend/check_page_bundle_mixins_css_for_sideeffects.js')
abort 'Error: At least one CSS changes introduces an unwanted sideeffect'.color(:red)
end
end
end
end
|