trigger-build 5.72 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

35
    # Must be overridden
36 37 38 39
    def downstream_project_path
      raise NotImplementedError
    end

40
    # Must be overridden
41 42 43 44
    def ref
      raise NotImplementedError
    end

45
    # Must be overridden
46 47 48 49
    def trigger_token
      raise NotImplementedError
    end

50
    # Must be overridden
51 52 53 54
    def access_token
      raise NotImplementedError
    end

55
    # Can be overridden
56 57 58 59
    def extra_variables
      {}
    end

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 125 126 127
    def downstream_project_path
      ENV['CNG_PROJECT_PATH'] || 'gitlab-org/build/CNG-mirror'
    end

    def ref
      ENV['CNG_BRANCH'] || 'master'
    end

128 129 130 131 132 133 134 135
    def trigger_token
      ENV['BUILD_TRIGGER_TOKEN']
    end

    def access_token
      ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
    end

136 137 138 139
    def extra_variables
      edition = Trigger.ee? ? 'EE' : 'CE'

      {
140
        # Back-compatibility until https://gitlab.com/gitlab-org/build/CNG/merge_requests/189 is merged
141
        "GITLAB_#{edition}_VERSION" => ENV['CI_COMMIT_REF_NAME'],
142
        "GITLAB_VERSION" => ENV['CI_COMMIT_REF_NAME'],
143
        "GITLAB_TAG" => ENV['CI_COMMIT_TAG'],
144
        "GITLAB_ASSETS_TAG" => ENV['CI_COMMIT_REF_SLUG'],
145
        "#{edition}_PIPELINE" => 'true'
146
      }
147
    end
148

149 150 151 152 153 154
    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}"
155
      else
156
        raw_version
157 158
      end
    end
159
  end
160

161
  class CommitComment
162 163 164
    def self.post!(downstream_pipeline, access_token)
      Gitlab.private_token = access_token

165 166 167 168
      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.")
169 170 171

    rescue Gitlab::Error::Error => error
      puts "Ignoring the following error: #{error}"
172 173 174 175 176 177 178
    end
  end

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

179
    attr_reader :project, :id, :api_token
180

181
    def initialize(project, id, api_token)
182 183
      @project = project
      @id = id
184
      @api_token = api_token
185
      @start = Time.now.to_i
186 187

      # gitlab-bot's token "GitLab multi-project pipeline polling"
188
      Gitlab.private_token = api_token
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
    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
219 220 221
      Gitlab.pipeline(project, id).status.to_sym
    rescue Gitlab::Error::Error => error
      puts "Ignoring the following error: #{error}"
222 223 224
      # Ignore GitLab API hiccups. If GitLab is really down, we'll hit the job
      # timeout anyway.
      :running
225 226 227 228 229 230
    end
  end
end

case ARGV[0]
when 'omnibus'
231
  Trigger::Omnibus.new.invoke!(post_comment: true).wait!
232
when 'cng'
233
  Trigger::CNG.new.invoke!.wait!
234 235 236 237 238
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