trigger-build 4.8 KB
Newer Older
1 2
#!/usr/bin/env ruby

3 4 5 6 7 8 9 10 11
require 'gitlab'

#
# Configure credentials to be used with gitlab gem
#
Gitlab.configure do |config|
  config.endpoint      = 'https://gitlab.com/api/v4'
  config.private_token = ENV['GITLAB_QA_ACCESS_TOKEN'] # gitlab-qa bot access token
end
12 13 14 15 16 17 18 19

module Trigger
  TOKEN = ENV['BUILD_TRIGGER_TOKEN']

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

20 21 22
  class Base
    def initialize(api_token)
      Gitlab.private_token = api_token
23 24
    end

25 26 27 28 29 30
    def invoke!(post_comment: false)
      pipeline = Gitlab.run_trigger(
        downstream_project_path,
        Trigger::TOKEN,
        ref,
        variables)
31

32 33
      puts "Triggered #{pipeline.web_url}"
      puts "Waiting for downstream pipeline status"
34

35 36 37 38 39 40
      begin
        Trigger::CommitComment.post!(downstream_project_path, pipeline) if post_comment
      rescue Gitlab::Error::Error => error
        puts "Ignoring the following error: #{error}"
      end
      Trigger::Pipeline.new(downstream_project_path, pipeline.id)
41 42 43 44
    end

    private

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
    # Must be overriden
    def downstream_project_path
      raise NotImplementedError
    end

    # Must be overriden
    def ref
      raise NotImplementedError
    end

    # Can be overriden
    def extra_variables
      {}
    end

    # Can be overriden
    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 72
        'TRIGGERED_USER' => ENV['GITLAB_USER_NAME'],
        'TRIGGER_SOURCE' => ENV['CI_JOB_URL']
73 74 75
      }
    end

76 77 78 79
    # 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)
80 81 82 83
      end
    end
  end

84 85
  class Omnibus < Base
    private
86

87 88
    def downstream_project_path
      'gitlab-org/omnibus-gitlab'.freeze
89 90
    end

91 92 93 94 95 96 97 98 99 100 101 102 103 104
    def ref
      ENV['OMNIBUS_BRANCH'] || 'master'
    end

    def extra_variables
      {
        'GITLAB_VERSION' => ENV['CI_COMMIT_SHA'],
        'ALTERNATIVE_SOURCES' => 'true',
        'ee' => Trigger.ee? ? 'true' : 'false'
      }
    end
  end

  class CNG < Base
105 106
    private

107 108 109 110 111 112 113 114 115 116 117 118 119 120
    def downstream_project_path
      ENV['CNG_PROJECT_PATH'] || 'gitlab-org/build/CNG-mirror'
    end

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

    def extra_variables
      edition = Trigger.ee? ? 'EE' : 'CE'

      {
        "GITLAB_#{edition}_VERSION" => ENV['CI_COMMIT_REF_NAME'],
        "#{edition}_PIPELINE" => 'true'
121
      }
122
    end
123

124 125 126 127 128 129
    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}"
130
      else
131
        raw_version
132 133
      end
    end
134
  end
135

136 137 138 139 140 141
  class CommitComment
    def self.post!(downstream_project_path, downstream_pipeline)
      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.")
142 143 144 145 146 147 148
    end
  end

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

149 150
    attr_reader :project, :id

151
    def initialize(project, id)
152 153
      @project = project
      @id = id
154
      @start = Time.now.to_i
155 156 157

      # gitlab-bot's token "GitLab multi-project pipeline polling"
      Gitlab.private_token = ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
    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
188 189 190
      Gitlab.pipeline(project, id).status.to_sym
    rescue Gitlab::Error::Error => error
      puts "Ignoring the following error: #{error}"
191 192 193
      # Ignore GitLab API hiccups. If GitLab is really down, we'll hit the job
      # timeout anyway.
      :running
194 195 196 197 198 199
    end
  end
end

case ARGV[0]
when 'omnibus'
200
  Trigger::Omnibus.new(ENV['GITLAB_QA_ACCESS_TOKEN']).invoke!(post_comment: true).wait!
201
when 'cng'
202
  Trigger::CNG.new(ENV['GITLAB_QA_ACCESS_TOKEN']).invoke!.wait!
203 204 205 206 207
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