1 |
commit: b635f1ab52fa63964ff9fc01f67638810c2fd2bf |
2 |
Author: Magnus Granberg <zorry <AT> gentoo <DOT> org> |
3 |
AuthorDate: Mon Aug 22 22:34:57 2022 +0000 |
4 |
Commit: Magnus Granberg <zorry <AT> gentoo <DOT> org> |
5 |
CommitDate: Mon Aug 22 22:34:57 2022 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/tinderbox-cluster.git/commit/?id=b635f1ab |
7 |
|
8 |
Add buildbot gitlab patch |
9 |
|
10 |
Signed-off-by: Magnus Granberg <zorry <AT> gentoo.org> |
11 |
|
12 |
master.cfg | 21 +++-- |
13 |
patches/bb-gitlab.patch | 212 +++++++++++++++++++++++++++++++++++++++++++++ |
14 |
patches/secretstring.patch | 74 ---------------- |
15 |
3 files changed, 225 insertions(+), 82 deletions(-) |
16 |
|
17 |
diff --git a/master.cfg b/master.cfg |
18 |
index af50ec2..dbdee3f 100644 |
19 |
--- a/master.cfg |
20 |
+++ b/master.cfg |
21 |
@@ -12,11 +12,13 @@ worker_data = [ |
22 |
{'uuid' : 'updatedb_2', 'password' : '', 'type' : 'local', 'enable' : True, }, |
23 |
{'uuid' : 'updatedb_3', 'password' : '', 'type' : 'local', 'enable' : True, }, |
24 |
{'uuid' : 'updatedb_4', 'password' : '', 'type' : 'local', 'enable' : True, }, |
25 |
- {'uuid' : 'c89c2c1a-46e0-4ded-81dd-c51afeb7fcfa', 'password' : 'test', 'type' : 'log', 'enable' : True, }, |
26 |
- {'uuid' : 'a89c2c1a-46e0-4ded-81dd-c51afeb7fcfa', 'password' : 'test', 'type' : 'docker', 'enable' : True, }, |
27 |
- {'uuid' : 'a89c2c1a-46e0-4ded-81dd-c51afeb7fcfb', 'password' : 'test', 'type' : 'docker', 'enable' : True, }, |
28 |
- {'uuid' : 'a89c2c1a-46e0-4ded-81dd-c51afeb7fcfd', 'password' : 'test', 'type' : 'docker', 'enable' : True, }, |
29 |
- {'uuid' : 'c89c2c1a-46e0-4ded-81dd-c51afeb7fcfa', 'password' : 'test', 'type' : 'node', 'enable' : True, }, |
30 |
+ {'uuid' : 'b89c2c1a-46e0-4ded-81dd-c51afeb7fcfa', 'password' : 'X', 'type' : 'log', 'enable' : True, }, |
31 |
+ {'uuid' : 'b89c2c1a-46e0-4ded-81dd-c51afeb7fcfb', 'password' : 'X', 'type' : 'log', 'enable' : True, }, |
32 |
+ {'uuid' : 'b89c2c1a-46e0-4ded-81dd-c51afeb7fcfc', 'password' : 'X', 'type' : 'log', 'enable' : True, }, |
33 |
+ {'uuid' : 'a89c2c1a-46e0-4ded-81dd-c51afeb7fcfa', 'password' : 'X', 'type' : 'docker', 'enable' : True, }, |
34 |
+ {'uuid' : 'a89c2c1a-46e0-4ded-81dd-c51afeb7fcfb', 'password' : 'X', 'type' : 'docker', 'enable' : True, }, |
35 |
+ {'uuid' : 'a89c2c1a-46e0-4ded-81dd-c51afeb7fcfd', 'password' : 'X', 'type' : 'docker', 'enable' : True, }, |
36 |
+ {'uuid' : 'c89c2c1a-46e0-4ded-81dd-c51afeb7fcfa', 'password' : 'X', 'type' : 'node', 'enable' : True, }, |
37 |
] |
38 |
|
39 |
# This is the dictionary that the buildmaster pays attention to. We also use |
40 |
@@ -27,7 +29,8 @@ c['buildbotNetUsageData'] = None |
41 |
|
42 |
####### SECRETS |
43 |
|
44 |
-c['secretsProviders'] = [secrets.SecretInAFile(dirname="/var/lib/buildmaster/gentoo-ci-cloud/secrets/")] |
45 |
+c['secretsProviders'] = [] |
46 |
+c['secretsProviders'].append(secrets.SecretInAFile(dirname="/var/lib/buildmaster/gentoo-ci-cloud/secrets")) |
47 |
|
48 |
####### WORKERS |
49 |
|
50 |
@@ -88,7 +91,7 @@ c['titleURL'] = "https://gentoo-ci.gentoo.org" |
51 |
# the 'www' entry below, but with an externally-visible host name which the |
52 |
# buildbot cannot figure out without some help. |
53 |
|
54 |
-c['buildbotURL'] = "http://90.231.13.235:8010/" |
55 |
+c['buildbotURL'] = "http://0.0.0.0:8010/" |
56 |
|
57 |
# minimalistic config to activate new web UI |
58 |
c['www'] = dict( |
59 |
@@ -97,6 +100,8 @@ c['www'] = dict( |
60 |
change_hook_dialects={ |
61 |
'gitlab' : { |
62 |
'secret': util.Secret("WWWHookGitlabToken"), |
63 |
+ 'token': util.Secret("gitlabToken"), |
64 |
+ 'baseUrl': 'https://gitlab.gentoo.org', |
65 |
}, |
66 |
}, |
67 |
) |
68 |
@@ -116,4 +121,4 @@ c['www']['ui_default_config'] = { |
69 |
# It's easy to start with sqlite, but it's recommended to switch to a dedicated |
70 |
# database, such as PostgreSQL or MySQL, for use in production environments. |
71 |
# http://docs.buildbot.net/current/manual/configuration/global.html#database-specification |
72 |
-c['db_url'] = "postgresql://buildbot:fooo@192.168.1.9/buildbot" |
73 |
+c['db_url'] = "postgresql://buildbot:X@192.0.0.0/buildbot" |
74 |
|
75 |
diff --git a/patches/bb-gitlab.patch b/patches/bb-gitlab.patch |
76 |
new file mode 100644 |
77 |
index 0000000..f57a2ac |
78 |
--- /dev/null |
79 |
+++ b/patches/bb-gitlab.patch |
80 |
@@ -0,0 +1,212 @@ |
81 |
+--- a/buildbot/www/hooks/gitlab.py 2022-03-06 15:10:44.000000000 +0100 |
82 |
++++ b/buildbot/www/hooks/gitlab.py 2022-08-07 22:22:16.570498909 +0200 |
83 |
+@@ -19,6 +19,8 @@ import re |
84 |
+ |
85 |
+ from dateutil.parser import parse as dateparse |
86 |
+ |
87 |
++import gitlab as python_gitlab |
88 |
++ |
89 |
+ from twisted.internet.defer import inlineCallbacks |
90 |
+ from twisted.python import log |
91 |
+ |
92 |
+@@ -26,6 +28,7 @@ from buildbot.process.properties import |
93 |
+ from buildbot.util import bytes2unicode |
94 |
+ from buildbot.www.hooks.base import BaseHookHandler |
95 |
+ |
96 |
++_HOSTED_BASE_URL = 'https://gitlab.com' |
97 |
+ _HEADER_EVENT = b'X-Gitlab-Event' |
98 |
+ _HEADER_GITLAB_TOKEN = b'X-Gitlab-Token' |
99 |
+ |
100 |
+@@ -94,6 +97,35 @@ class GitLabHandler(BaseHookHandler): |
101 |
+ |
102 |
+ return changes |
103 |
+ |
104 |
++ #@inlineCallbacks |
105 |
++ def _configGitlabRest(self, token, baseURL=None): |
106 |
++ if baseURL is None: |
107 |
++ baseURL = _HOSTED_BASE_URL |
108 |
++ if baseURL.endswith('/'): |
109 |
++ baseURL = baseURL[:-1] |
110 |
++ return python_gitlab.Gitlab(url=baseURL, private_token=token) |
111 |
++ |
112 |
++ #@inlineCallbacks |
113 |
++ def _getFiles(self, attrs): |
114 |
++ """ |
115 |
++ get the files changes |
116 |
++ """ |
117 |
++ files = [] |
118 |
++ project = self.gl.projects.get(attrs['target']['id']) |
119 |
++ mr = project.mergerequests.get(attrs['iid']) |
120 |
++ changes = mr.changes() |
121 |
++ if isinstance(changes['changes'], list): |
122 |
++ for change in changes['changes']: |
123 |
++ print(change['old_path']) |
124 |
++ print(change['new_path']) |
125 |
++ print(change['new_file']) |
126 |
++ print(change['renamed_file']) |
127 |
++ print(change['deleted_file']) |
128 |
++ if change['old_path'] == change['new_path']: |
129 |
++ files.append(change['old_path']) |
130 |
++ print(f"Files: {files}") |
131 |
++ return files |
132 |
++ |
133 |
+ def _process_merge_request_change(self, payload, event, codebase=None): |
134 |
+ """ |
135 |
+ Consumes the merge_request JSON as a python object and turn it into a buildbot change. |
136 |
+@@ -126,7 +158,7 @@ class GitLabHandler(BaseHookHandler): |
137 |
+ |
138 |
+ changes = [{ |
139 |
+ 'author': f"{commit['author']['name']} <{commit['author']['email']}>", |
140 |
+- 'files': [], # @todo use rest API |
141 |
++ 'files' : self._getFiles(attrs), |
142 |
+ 'comments': f"MR#{attrs['iid']}: {attrs['title']}\n\n{attrs['description']}", |
143 |
+ 'revision': commit['id'], |
144 |
+ 'when_timestamp': when_timestamp, |
145 |
+@@ -151,6 +183,65 @@ class GitLabHandler(BaseHookHandler): |
146 |
+ changes[0]['codebase'] = codebase |
147 |
+ return changes |
148 |
+ |
149 |
++ def _process_note_addition_to_merge_request(self, payload, event, codebase=None): |
150 |
++ """ |
151 |
++ Consumes a note event JSON as a python object and turn it into a buildbot change. |
152 |
++ :arguments: |
153 |
++ payload |
154 |
++ Python Object that represents the JSON sent by GitLab Service |
155 |
++ Hook. |
156 |
++ Comments in merge_requests are send as note events by the API |
157 |
++ """ |
158 |
++ attrs = payload['object_attributes'] |
159 |
++ |
160 |
++ # handle only note events coming from merge_requests |
161 |
++ # this can be direct comments or comments added to a changeset of the MR |
162 |
++ # |
163 |
++ # editing a comment does NOT lead to an event at all |
164 |
++ if 'merge_request' not in payload: |
165 |
++ log.msg(f"Found note event (id {attrs['id']}) without corresponding MR - ignore") |
166 |
++ return [] |
167 |
++ |
168 |
++ # change handling is very similar to the method above, but |
169 |
++ commit = payload['merge_request']['last_commit'] |
170 |
++ when_timestamp = dateparse(commit['timestamp']) |
171 |
++ # @todo provide and document a way to choose between http and ssh url |
172 |
++ repo_url = payload['merge_request']['target']['git_http_url'] |
173 |
++ # project name from http headers is empty for me, so get it from |
174 |
++ # object_attributes/target/name |
175 |
++ mr = payload['merge_request'] |
176 |
++ project = mr['target']['name'] |
177 |
++ |
178 |
++ log.msg(f"Found notes on MR#{mr['iid']}: {attrs['note']}") |
179 |
++ changes = [{ |
180 |
++ 'author': f"{commit['author']['name']} <{commit['author']['email']}>", |
181 |
++ 'files': [], # not provided by rest API |
182 |
++ 'comments': f"MR#{mr['iid']}: {mr['title']}\n\n{mr['description']}", |
183 |
++ 'revision': commit['id'], |
184 |
++ 'when_timestamp': when_timestamp, |
185 |
++ 'branch': mr['target_branch'], |
186 |
++ 'repository': repo_url, |
187 |
++ 'project': project, |
188 |
++ 'category': event, |
189 |
++ 'revlink': mr['url'], |
190 |
++ 'properties': { |
191 |
++ 'source_branch': mr['source_branch'], |
192 |
++ 'source_project_id': mr['source_project_id'], |
193 |
++ 'source_repository': mr['source']['git_http_url'], |
194 |
++ 'source_git_ssh_url': mr['source']['git_ssh_url'], |
195 |
++ 'target_branch': mr['target_branch'], |
196 |
++ 'target_project_id': mr['target_project_id'], |
197 |
++ 'target_repository': mr['target']['git_http_url'], |
198 |
++ 'target_git_ssh_url': mr['target']['git_ssh_url'], |
199 |
++ 'event': event, |
200 |
++ 'comments': attrs['note'], |
201 |
++ }, |
202 |
++ }] |
203 |
++ if codebase is not None: |
204 |
++ changes[0]['codebase'] = codebase |
205 |
++ return changes |
206 |
++ |
207 |
++ |
208 |
+ @inlineCallbacks |
209 |
+ def getChanges(self, request): |
210 |
+ """ |
211 |
+@@ -160,17 +251,21 @@ class GitLabHandler(BaseHookHandler): |
212 |
+ request |
213 |
+ the http request object |
214 |
+ """ |
215 |
++ p = Properties() |
216 |
++ p.master = self.master |
217 |
+ expected_secret = isinstance(self.options, dict) and self.options.get('secret') |
218 |
+ if expected_secret: |
219 |
+ received_secret = request.getHeader(_HEADER_GITLAB_TOKEN) |
220 |
+ received_secret = bytes2unicode(received_secret) |
221 |
+- |
222 |
+- p = Properties() |
223 |
+- p.master = self.master |
224 |
+ expected_secret_value = yield p.render(expected_secret) |
225 |
+ |
226 |
+ if received_secret != expected_secret_value: |
227 |
+ raise ValueError("Invalid secret") |
228 |
++ |
229 |
++ baseUrl_value = isinstance(self.options, dict) and self.options.get('baseUrl') |
230 |
++ expected_token = isinstance(self.options, dict) and self.options.get('token') |
231 |
++ expected_token_value = yield p.render(expected_token) |
232 |
++ |
233 |
+ try: |
234 |
+ content = request.content.read() |
235 |
+ payload = json.loads(bytes2unicode(content)) |
236 |
+@@ -189,7 +284,11 @@ class GitLabHandler(BaseHookHandler): |
237 |
+ repo_url = payload['repository']['url'] |
238 |
+ changes = self._process_change( |
239 |
+ payload, user, repo, repo_url, event_type, codebase=codebase) |
240 |
++ elif event_type == 'note': |
241 |
++ changes = self._process_note_addition_to_merge_request( |
242 |
++ payload, event_type, codebase=codebase) |
243 |
+ elif event_type == 'merge_request': |
244 |
++ self.gl = self._configGitlabRest(expected_token_value, baseURL=baseUrl_value) |
245 |
+ changes = self._process_merge_request_change( |
246 |
+ payload, event_type, codebase=codebase) |
247 |
+ else: |
248 |
+--- a/buildbot/reporters/gitlab.py 2022-03-06 15:10:44.000000000 +0100 |
249 |
++++ b/buildbot/reporters/gitlab.py 2022-08-23 00:11:55.996195542 +0200 |
250 |
+@@ -95,7 +95,7 @@ class GitLabStatusPush(ReporterBase): |
251 |
+ :param branch: Branch name to create the status for. |
252 |
+ :param sha: Full sha to create the status for. |
253 |
+ :param state: one of the following 'pending', 'success', 'failed' |
254 |
+- or 'cancelled'. |
255 |
++ or 'canceled'. |
256 |
+ :param target_url: Target url to associate with this status. |
257 |
+ :param description: Short description of the status. |
258 |
+ :param context: Context of the result |
259 |
+@@ -155,10 +155,12 @@ class GitLabStatusPush(ReporterBase): |
260 |
+ SKIPPED: 'success', |
261 |
+ EXCEPTION: 'failed', |
262 |
+ RETRY: 'pending', |
263 |
+- CANCELLED: 'cancelled' |
264 |
++ CANCELLED: 'canceled' |
265 |
+ }.get(build['results'], 'failed') |
266 |
+- else: |
267 |
++ elif build.get('started_at'): |
268 |
+ state = 'running' |
269 |
++ else: |
270 |
++ state = 'pending' |
271 |
+ |
272 |
+ context = yield props.render(self.context) |
273 |
+ |
274 |
+@@ -167,8 +169,8 @@ class GitLabStatusPush(ReporterBase): |
275 |
+ # FIXME: probably only want to report status for the last commit in the changeset |
276 |
+ for sourcestamp in sourcestamps: |
277 |
+ sha = sourcestamp['revision'] |
278 |
+- if 'source_project_id' in props: |
279 |
+- proj_id = props['source_project_id'] |
280 |
++ if 'target_project_id' in props: |
281 |
++ proj_id = props['target_project_id'] |
282 |
+ else: |
283 |
+ proj_id = yield self.getProjectId(sourcestamp) |
284 |
+ if proj_id is None: |
285 |
+@@ -189,6 +191,7 @@ class GitLabStatusPush(ReporterBase): |
286 |
+ description=description |
287 |
+ ) |
288 |
+ if res.code not in (200, 201, 204): |
289 |
++ log.msg(res.code) |
290 |
+ message = yield res.json() |
291 |
+ message = message.get('message', 'unspecified error') |
292 |
+ log.msg( |
293 |
|
294 |
diff --git a/patches/secretstring.patch b/patches/secretstring.patch |
295 |
deleted file mode 100644 |
296 |
index 4e0833d..0000000 |
297 |
--- a/patches/secretstring.patch |
298 |
+++ /dev/null |
299 |
@@ -1,74 +0,0 @@ |
300 |
-diff --git a/master/buildbot/process/buildstep.py b/master/buildbot/process/buildstep.py |
301 |
-index 7ac18b086..82ff7ce07 100644 |
302 |
---- a/master/buildbot/process/buildstep.py |
303 |
-+++ b/master/buildbot/process/buildstep.py |
304 |
-@@ -850,6 +850,7 @@ class ShellMixin: |
305 |
- 'sigtermTime', |
306 |
- 'initialStdin', |
307 |
- 'decodeRC', |
308 |
-+ 'SecretString', |
309 |
- ] |
310 |
- renderables = _shellMixinArgs |
311 |
- |
312 |
-diff --git a/master/buildbot/process/remotecommand.py b/master/buildbot/process/remotecommand.py |
313 |
-index 90ec4c44d..fb3099b28 100644 |
314 |
---- a/master/buildbot/process/remotecommand.py |
315 |
-+++ b/master/buildbot/process/remotecommand.py |
316 |
-@@ -44,7 +44,7 @@ class RemoteCommand(base.RemoteCommandImpl): |
317 |
- |
318 |
- def __init__(self, remote_command, args, ignore_updates=False, |
319 |
- collectStdout=False, collectStderr=False, decodeRC=None, |
320 |
-- stdioLogName='stdio'): |
321 |
-+ stdioLogName='stdio', SecretString=False): |
322 |
- if decodeRC is None: |
323 |
- decodeRC = {0: SUCCESS} |
324 |
- self.logs = {} |
325 |
-@@ -70,6 +70,7 @@ class RemoteCommand(base.RemoteCommandImpl): |
326 |
- self.commandID = None |
327 |
- self.deferred = None |
328 |
- self.interrupted = False |
329 |
-+ self.SecretString = SecretString |
330 |
- # a lock to make sure that only one log-handling method runs at a time. |
331 |
- # This is really only a problem with old-style steps, which do not |
332 |
- # wait for the Deferred from one method before invoking the next. |
333 |
-@@ -275,6 +276,8 @@ class RemoteCommand(base.RemoteCommandImpl): |
334 |
- def cleanup(data): |
335 |
- if self.step is None: |
336 |
- return data |
337 |
-+ if self.SecretString and isinstance(self.SecretString, list) and len(self.SecretString) == 2: |
338 |
-+ data = data.replace(self.SecretString[0], '<' + self.SecretString[1] + '>') |
339 |
- return self.step.build.properties.cleanupTextFromSecrets(data) |
340 |
- |
341 |
- if self.debug: |
342 |
-@@ -358,7 +361,8 @@ class RemoteShellCommand(RemoteCommand): |
343 |
- collectStdout=False, collectStderr=False, |
344 |
- interruptSignal=None, |
345 |
- initialStdin=None, decodeRC=None, |
346 |
-- stdioLogName='stdio'): |
347 |
-+ stdioLogName='stdio', |
348 |
-+ SecretString=False): |
349 |
- if logfiles is None: |
350 |
- logfiles = {} |
351 |
- if decodeRC is None: |
352 |
-@@ -398,7 +402,8 @@ class RemoteShellCommand(RemoteCommand): |
353 |
- super().__init__("shell", args, collectStdout=collectStdout, |
354 |
- collectStderr=collectStderr, |
355 |
- decodeRC=decodeRC, |
356 |
-- stdioLogName=stdioLogName) |
357 |
-+ stdioLogName=stdioLogName, |
358 |
-+ SecretString=SecretString) |
359 |
- |
360 |
- def _start(self): |
361 |
- if self.args['usePTY'] is None: |
362 |
-diff --git a/master/buildbot/steps/shell.py b/master/buildbot/steps/shell.py |
363 |
-index d21ca7b9b..cf56a2802 100644 |
364 |
---- a/master/buildbot/steps/shell.py |
365 |
-+++ b/master/buildbot/steps/shell.py |
366 |
-@@ -183,6 +183,7 @@ class ShellCommand(buildstep.ShellMixin, buildstep.BuildStep): |
367 |
- 'decodeRC', |
368 |
- 'stdioLogName', |
369 |
- 'workdir', |
370 |
-+ 'SecretString', |
371 |
- ] + buildstep.BuildStep.parms |
372 |
- |
373 |
- invalid_args = [] |