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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
#!/usr/bin/env ruby
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
module Trigger
TOKEN = ENV['BUILD_TRIGGER_TOKEN']
def self.ee?
ENV['CI_PROJECT_NAME'] == 'gitlab-ee' || File.exist?('CHANGELOG-EE.md')
end
class Base
def initialize(api_token)
Gitlab.private_token = api_token
end
def invoke!(post_comment: false)
pipeline = Gitlab.run_trigger(
downstream_project_path,
Trigger::TOKEN,
ref,
variables)
puts "Triggered #{pipeline.web_url}"
puts "Waiting for downstream pipeline status"
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)
end
private
# 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
{
'TRIGGERED_USER' => ENV['GITLAB_USER_NAME'],
'TRIGGER_SOURCE' => ENV['CI_JOB_URL']
}
end
# 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)
end
end
end
class Omnibus < Base
private
def downstream_project_path
'gitlab-org/omnibus-gitlab'.freeze
end
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
private
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'
}
end
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}"
else
raw_version
end
end
end
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.")
end
end
class Pipeline
INTERVAL = 60 # seconds
MAX_DURATION = 3600 * 3 # 3 hours
attr_reader :project, :id
def initialize(project, id)
@project = project
@id = id
@start = Time.now.to_i
# gitlab-bot's token "GitLab multi-project pipeline polling"
Gitlab.private_token = ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
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
Gitlab.pipeline(project, id).status.to_sym
rescue Gitlab::Error::Error => error
puts "Ignoring the following error: #{error}"
# Ignore GitLab API hiccups. If GitLab is really down, we'll hit the job
# timeout anyway.
:running
end
end
end
case ARGV[0]
when 'omnibus'
Trigger::Omnibus.new(ENV['GITLAB_QA_ACCESS_TOKEN']).invoke!(post_comment: true).wait!
when 'cng'
Trigger::CNG.new(ENV['GITLAB_QA_ACCESS_TOKEN']).invoke!.wait!
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
|