trigger-build 6.0 KB
Newer Older
1
#!/usr/bin/env ruby
2
# frozen_string_literal: true
3

4 5 6 7 8 9
require 'gitlab'

#
# Configure credentials to be used with gitlab gem
#
Gitlab.configure do |config|
10
  config.endpoint = 'https://gitlab.com/api/v4'
11
end
12 13 14 15 16 17

module Trigger
  def self.ee?
    ENV['CI_PROJECT_NAME'] == 'gitlab-ee' || File.exist?('CHANGELOG-EE.md')
  end

18 19 20 21
  class Base
    def invoke!(post_comment: false)
      pipeline = Gitlab.run_trigger(
        downstream_project_path,
22
        trigger_token,
23 24
        ref,
        variables)
25

26
      puts "Triggered downstream pipeline: #{pipeline.web_url}\n"
27
      puts "Waiting for downstream pipeline status"
28

29 30
      Trigger::CommitComment.post!(pipeline, access_token) if post_comment
      Trigger::Pipeline.new(downstream_project_path, pipeline.id, access_token)
31 32 33 34
    end

    private

G
George Tsiolis 已提交
35
    # Must be overridden
36 37 38 39
    def downstream_project_path
      raise NotImplementedError
    end

G
George Tsiolis 已提交
40
    # Must be overridden
41 42 43 44
    def ref
      raise NotImplementedError
    end

G
George Tsiolis 已提交
45
    # Must be overridden
46 47 48 49
    def trigger_token
      raise NotImplementedError
    end

G
George Tsiolis 已提交
50
    # Must be overridden
51 52 53 54
    def access_token
      raise NotImplementedError
    end

G
George Tsiolis 已提交
55
    # Can be overridden
56 57 58 59
    def extra_variables
      {}
    end

G
George Tsiolis 已提交
60
    # Can be overridden
61 62 63 64 65 66 67 68 69
    def version_param_value(version_file)
      File.read(version_file).strip
    end

    def variables
      base_variables.merge(extra_variables).merge(version_file_variables)
    end

    def base_variables
70
      {
71
        'GITLAB_REF_SLUG' => ENV['CI_COMMIT_TAG'] ? ENV['CI_COMMIT_REF_NAME'] : ENV['CI_COMMIT_REF_SLUG'],
72
        'TRIGGERED_USER' => ENV['TRIGGERED_USER'] || ENV['GITLAB_USER_NAME'],
73 74 75 76
        'TRIGGER_SOURCE' => ENV['CI_JOB_URL'],
        'TOP_UPSTREAM_SOURCE_PROJECT' => ENV['CI_PROJECT_PATH'],
        'TOP_UPSTREAM_SOURCE_JOB' => ENV['CI_JOB_URL'],
        'TOP_UPSTREAM_SOURCE_SHA' => ENV['CI_COMMIT_SHA']
77 78 79
      }
    end

80 81 82 83
    # Read version files from all components
    def version_file_variables
      Dir.glob("*_VERSION").each_with_object({}) do |version_file, params|
        params[version_file] = version_param_value(version_file)
84 85 86 87
      end
    end
  end

88 89
  class Omnibus < Base
    private
90

91
    def downstream_project_path
92
      'gitlab-org/omnibus-gitlab'
93 94
    end

95 96 97 98
    def ref
      ENV['OMNIBUS_BRANCH'] || 'master'
    end

99 100 101 102 103 104 105 106
    def trigger_token
      ENV['BUILD_TRIGGER_TOKEN']
    end

    def access_token
      ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
    end

107 108 109 110
    def extra_variables
      {
        'GITLAB_VERSION' => ENV['CI_COMMIT_SHA'],
        'ALTERNATIVE_SOURCES' => 'true',
111 112
        'ee' => Trigger.ee? ? 'true' : 'false',
        'QA_BRANCH' => ENV['QA_BRANCH'] || 'master'
113 114 115 116 117
      }
    end
  end

  class CNG < Base
118 119
    private

120 121 122 123 124
    def downstream_project_path
      ENV['CNG_PROJECT_PATH'] || 'gitlab-org/build/CNG-mirror'
    end

    def ref
125 126 127 128 129 130 131 132
      default_ref =
        if ENV['CI_COMMIT_REF_NAME'] =~ /^[\d-]+-stable(-ee)?$/
          ENV['CI_COMMIT_REF_NAME']
        else
          'master'
        end

      ENV['CNG_BRANCH'] || default_ref
133 134
    end

135 136 137 138 139 140 141 142
    def trigger_token
      ENV['BUILD_TRIGGER_TOKEN']
    end

    def access_token
      ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
    end

143 144 145 146
    def extra_variables
      edition = Trigger.ee? ? 'EE' : 'CE'

      {
147
        # Back-compatibility until https://gitlab.com/gitlab-org/build/CNG/merge_requests/189 is merged
148
        "GITLAB_#{edition}_VERSION" => ENV['CI_COMMIT_REF_NAME'],
149
        "GITLAB_VERSION" => ENV['CI_COMMIT_REF_NAME'],
150
        "GITLAB_TAG" => ENV['CI_COMMIT_TAG'],
151
        "GITLAB_ASSETS_TAG" => ENV['CI_COMMIT_TAG'] ? ENV['CI_COMMIT_REF_NAME'] : ENV['CI_COMMIT_REF_SLUG'],
152
        "FORCE_RAILS_IMAGE_BUILDS" => 'true',
153
        "#{edition}_PIPELINE" => 'true'
154
      }
155
    end
156

157 158 159 160 161 162
    def version_param_value(_version_file)
      raw_version = super

      # if the version matches semver format, treat it as a tag and prepend `v`
      if raw_version =~ Regexp.compile(/^\d+\.\d+\.\d+(-rc\d+)?(-ee)?$/)
        "v#{raw_version}"
163
      else
164
        raw_version
165 166
      end
    end
167
  end
168

169
  class CommitComment
170 171 172
    def self.post!(downstream_pipeline, access_token)
      Gitlab.private_token = access_token

173 174 175 176
      Gitlab.create_commit_comment(
        ENV['CI_PROJECT_PATH'],
        ENV['CI_COMMIT_SHA'],
        "The [`#{ENV['CI_JOB_NAME']}`](#{ENV['CI_JOB_URL']}) job from pipeline #{ENV['CI_PIPELINE_URL']} triggered #{downstream_pipeline.web_url} downstream.")
177 178 179

    rescue Gitlab::Error::Error => error
      puts "Ignoring the following error: #{error}"
180 181 182 183 184 185 186
    end
  end

  class Pipeline
    INTERVAL = 60 # seconds
    MAX_DURATION = 3600 * 3 # 3 hours

187
    attr_reader :project, :id, :api_token
188

189
    def initialize(project, id, api_token)
190 191
      @project = project
      @id = id
192
      @api_token = api_token
193
      @start = Time.now.to_i
194 195

      # gitlab-bot's token "GitLab multi-project pipeline polling"
196
      Gitlab.private_token = api_token
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
    end

    def wait!
      loop do
        raise "Pipeline timed out after waiting for #{duration} minutes!" if timeout?

        case status
        when :created, :pending, :running
          print "."
          sleep INTERVAL
        when :success
          puts "Pipeline succeeded in #{duration} minutes!"
          break
        else
          raise "Pipeline did not succeed!"
        end

        STDOUT.flush
      end
    end

    def timeout?
      Time.now.to_i > (@start + MAX_DURATION)
    end

    def duration
      (Time.now.to_i - @start) / 60
    end

    def status
227 228 229
      Gitlab.pipeline(project, id).status.to_sym
    rescue Gitlab::Error::Error => error
      puts "Ignoring the following error: #{error}"
230 231 232
      # Ignore GitLab API hiccups. If GitLab is really down, we'll hit the job
      # timeout anyway.
      :running
233 234 235 236 237 238
    end
  end
end

case ARGV[0]
when 'omnibus'
239
  Trigger::Omnibus.new.invoke!(post_comment: true).wait!
240
when 'cng'
241
  Trigger::CNG.new.invoke!.wait!
242 243 244 245 246
else
  puts "Please provide a valid option:
  omnibus - Triggers a pipeline that builds the omnibus-gitlab package
  cng - Triggers a pipeline that builds images used by the GitLab helm chart"
end