summaryrefslogtreecommitdiff
path: root/lib/tasks/gitlab/assets.rake
blob: 54e74fd9c8b0c48e9bd723a7f2001f73be805ab8 (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
# frozen_string_literal: true

require 'fileutils'

module Tasks
  module Gitlab
    module Assets
      FOSS_ASSET_FOLDERS = %w[app/assets app/views fixtures/emojis vendor/assets/javascripts].freeze
      EE_ASSET_FOLDERS = %w[ee/app/assets ee/app/views].freeze
      JS_ASSET_PATTERNS = %w[*.js config/**/*.js].freeze
      JS_ASSET_FILES = %w[package.json yarn.lock].freeze
      MASTER_MD5_HASH_FILE = 'master-assets-hash.txt'
      HEAD_MD5_HASH_FILE = 'assets-hash.txt'
      PUBLIC_ASSETS_WEBPACK_DIR = 'public/assets/webpack'

      def self.md5_of_assets_impacting_webpack_compilation
        start_time = Time.now
        asset_files = assets_impacting_webpack_compilation
        puts "Generating the MD5 hash for #{assets_impacting_webpack_compilation.size} Webpack-related assets..."

        asset_file_md5s = asset_files.map do |asset_file|
          Digest::MD5.file(asset_file).hexdigest
        end

        Digest::MD5.hexdigest(asset_file_md5s.join).tap { |md5| puts "=> MD5 generated in #{Time.now - start_time}: #{md5}" }
      end

      def self.assets_impacting_webpack_compilation
        assets_folders = FOSS_ASSET_FOLDERS
        assets_folders += EE_ASSET_FOLDERS if ::Gitlab.ee?

        asset_files = Dir.glob(JS_ASSET_PATTERNS)
        asset_files += JS_ASSET_FILES

        assets_folders.each do |folder|
          asset_files.concat(Dir.glob(["#{folder}/**/*.*"]))
        end

        asset_files
      end
      private_class_method :assets_impacting_webpack_compilation
    end
  end
end

namespace :gitlab do
  namespace :assets do
    desc 'GitLab | Assets | Compile all frontend assets'
    task :compile do
      require_dependency 'gitlab/task_helpers'

      %w[
        yarn:check
        gettext:po_to_json
        rake:assets:precompile
        gitlab:assets:compile_webpack_if_needed
        gitlab:assets:fix_urls
        gitlab:assets:check_page_bundle_mixins_css_for_sideeffects
      ].each(&::Gitlab::TaskHelpers.method(:invoke_and_time_task))
    end

    desc 'GitLab | Assets | Compile all Webpack assets'
    task :compile_webpack_if_needed do
      FileUtils.mv(Tasks::Gitlab::Assets::HEAD_MD5_HASH_FILE, Tasks::Gitlab::Assets::MASTER_MD5_HASH_FILE, force: true)

      master_assets_md5 =
        if File.exist?(Tasks::Gitlab::Assets::MASTER_MD5_HASH_FILE)
          File.read(Tasks::Gitlab::Assets::MASTER_MD5_HASH_FILE)
        else
          'missing!'
        end

      head_assets_md5 = Tasks::Gitlab::Assets.md5_of_assets_impacting_webpack_compilation.tap do |md5|
        File.write(Tasks::Gitlab::Assets::HEAD_MD5_HASH_FILE, md5)
      end

      puts "Webpack assets MD5 for `master`: #{master_assets_md5}"
      puts "Webpack assets MD5 for `HEAD`: #{head_assets_md5}"

      public_assets_webpack_dir_exists = Dir.exist?(Tasks::Gitlab::Assets::PUBLIC_ASSETS_WEBPACK_DIR)

      if head_assets_md5 != master_assets_md5 || !public_assets_webpack_dir_exists
        FileUtils.rm_r(Tasks::Gitlab::Assets::PUBLIC_ASSETS_WEBPACK_DIR) if public_assets_webpack_dir_exists

        unless system('yarn webpack')
          abort 'Error: Unable to compile webpack production bundle.'.color(:red)
        end
      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"
        if 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 IO.binread(file)
            gz.close

            File.utime(mtime, mtime, f.path)
          end
        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
      system('./scripts/frontend/check_page_bundle_mixins_css_for_sideeffects.js')
    end
  end
end