trigger-build 5.53 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' => 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 84 85
    def ref_slug
      return 'master' if ENV['CI_COMMIT_REF_SLUG'] =~ %r{(\Aqa[/-]|-qa\z)}

      ENV['CI_COMMIT_REF_SLUG']
    end

86 87 88 89
    # 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)
90 91 92 93
      end
    end
  end

94 95
  class Omnibus < Base
    private
96

97
    def downstream_project_path
98
      'gitlab-org/omnibus-gitlab'
99 100
    end

101 102 103 104
    def ref
      ENV['OMNIBUS_BRANCH'] || 'master'
    end

105 106 107 108 109 110 111 112
    def trigger_token
      ENV['BUILD_TRIGGER_TOKEN']
    end

    def access_token
      ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
    end

113 114 115 116
    def extra_variables
      {
        'GITLAB_VERSION' => ENV['CI_COMMIT_SHA'],
        'ALTERNATIVE_SOURCES' => 'true',
117 118
        'ee' => Trigger.ee? ? 'true' : 'false',
        'QA_BRANCH' => ENV['QA_BRANCH'] || 'master'
119 120 121 122 123
      }
    end
  end

  class CNG < Base
124 125
    private

126 127 128 129 130 131 132 133
    def downstream_project_path
      ENV['CNG_PROJECT_PATH'] || 'gitlab-org/build/CNG-mirror'
    end

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

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

    def access_token
      ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
    end

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

      {
        "GITLAB_#{edition}_VERSION" => ENV['CI_COMMIT_REF_NAME'],
        "#{edition}_PIPELINE" => 'true'
148
      }
149
    end
150

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

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

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

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

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

181
    attr_reader :project, :id, :api_token
182

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

      # gitlab-bot's token "GitLab multi-project pipeline polling"
190
      Gitlab.private_token = api_token
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 219 220
    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
221 222 223
      Gitlab.pipeline(project, id).status.to_sym
    rescue Gitlab::Error::Error => error
      puts "Ignoring the following error: #{error}"
224 225 226
      # Ignore GitLab API hiccups. If GitLab is really down, we'll hit the job
      # timeout anyway.
      :running
227 228 229 230 231 232
    end
  end
end

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