Merge ~sylvain-pineau/hwcert-jenkins-tools:tf-checklist-links-to-summary-v2 into hwcert-jenkins-tools:master
- Git
- lp:~sylvain-pineau/hwcert-jenkins-tools
- tf-checklist-links-to-summary-v2
- Merge into master
Proposed by
Sylvain Pineau
Status: | Merged |
---|---|
Approved by: | Sylvain Pineau |
Approved revision: | 30f4a6da7248585b5c272ae636a13c50d4126d02 |
Merged at revision: | 85d18ed5c7fc4a1011aac041ac5c82d0db60b974 |
Proposed branch: | ~sylvain-pineau/hwcert-jenkins-tools:tf-checklist-links-to-summary-v2 |
Merge into: | hwcert-jenkins-tools:master |
Diff against target: |
437 lines (+59/-173) 2 files modified
trello-board-updater-desktop.py (+37/-99) trello-board-updater-image-testing.py (+22/-74) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Paul Larson | Approve | ||
Review via email:
|
Commit message
Description of the change
Refactor of the two new trello board updater scripts:
trello-
trello-
Both now share common code with the original trello-
Tested here:
https:/
https:/
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/trello-board-updater-desktop.py b/trello-board-updater-desktop.py |
2 | index 48fb316..d04a990 100755 |
3 | --- a/trello-board-updater-desktop.py |
4 | +++ b/trello-board-updater-desktop.py |
5 | @@ -6,6 +6,7 @@ |
6 | # |
7 | # Written by: |
8 | # Taihsiang Ho <taihsiang.ho@canonical.com> |
9 | +# Sylvain Pineau <sylvain.pineau@canonical.com> |
10 | # |
11 | # |
12 | # The script uses py-trello package 0.9.0. You may want to fetch it from |
13 | @@ -15,6 +16,7 @@ |
14 | # SUTs |
15 | # |
16 | import argparse |
17 | +import importlib |
18 | import os |
19 | import re |
20 | import requests |
21 | @@ -34,89 +36,12 @@ from trello.exceptions import ResourceUnavailable |
22 | |
23 | from unittest.mock import MagicMock |
24 | |
25 | +tbu = importlib.import_module("trello-board-updater") |
26 | + |
27 | format_str = "[ %(funcName)s() ] %(message)s" |
28 | logging.basicConfig(level=logging.INFO, format=format_str) |
29 | |
30 | |
31 | -def environ_or_required(key): |
32 | - if os.environ.get(key): |
33 | - return {'default': os.environ.get(key)} |
34 | - else: |
35 | - return {'required': True} |
36 | - |
37 | - |
38 | -def search_card(board, query, card_filter="open"): |
39 | - for card in board.get_cards(card_filter=card_filter): |
40 | - if re.match(query, card.name): |
41 | - return card |
42 | - |
43 | - |
44 | -def find_or_create_checklist(card, checklist_name, items=[]): |
45 | - existing_checklists = card.fetch_checklists() |
46 | - checklist = None |
47 | - for c in existing_checklists: |
48 | - if c.name == checklist_name: |
49 | - checklist = c |
50 | - break |
51 | - if not checklist: |
52 | - checklist = card.add_checklist(checklist_name, []) |
53 | - for item in items: |
54 | - item_msg = item + ' (NO RESULTS)' |
55 | - checklist.add_checklist_item(item_msg) |
56 | - return checklist |
57 | - |
58 | - |
59 | -def change_checklist_item(checklist, name, checked=False): |
60 | - # keep the trailing space so that we don't match the wrong thing later |
61 | - r = re.match('\[*(.*? )\(', name) |
62 | - if r: |
63 | - debname = r.group(1) |
64 | - for item in checklist.items: |
65 | - if debname in item.get('name'): |
66 | - checklist.rename_checklist_item(item.get('name'), name) |
67 | - checklist.set_checklist_item(name, checked) |
68 | - return True |
69 | - else: |
70 | - return False |
71 | - else: |
72 | - print('WARNING: Invalid name specified', name) |
73 | - |
74 | - |
75 | -def no_new_fails_or_skips(summary_data): |
76 | - """Check summary data for new fails or skips |
77 | - |
78 | - Return True if there are no new fails or skips detected and if all |
79 | - tests passed |
80 | - """ |
81 | - return ("No new failed or skipped tests" in summary_data and |
82 | - "All tests passed" in summary_data) |
83 | - |
84 | - |
85 | -def load_config(configfile): |
86 | - if not configfile: |
87 | - return [] |
88 | - try: |
89 | - data = yaml.safe_load(configfile) |
90 | - except (yaml.parser.ParserError, yaml.scanner.ScannerError): |
91 | - print('ERROR: Error parsing', configfile.name) |
92 | - sys.exit(1) |
93 | - return data |
94 | - |
95 | - |
96 | -def attach_labels(board, card, label_list): |
97 | - for labelstr in label_list: |
98 | - for label in board.get_labels(): |
99 | - if label.name == labelstr: |
100 | - labels = card.list_labels or [] |
101 | - if label not in labels: |
102 | - # Avoid crash if checking labels fails to find it |
103 | - try: |
104 | - card.add_label(label) |
105 | - except ResourceUnavailable: |
106 | - pass |
107 | - break |
108 | - |
109 | - |
110 | def run(args, board, c3_link, jenkins_link): |
111 | |
112 | kernel_stack = args.series |
113 | @@ -203,8 +128,8 @@ def run(args, board, c3_link, jenkins_link): |
114 | re.escape(kernel_stack), |
115 | re.escape(deb_kernel_image), |
116 | deb_version) |
117 | - card = search_card(board, pattern) |
118 | - config = load_config(args.config) |
119 | + card = tbu.search_card(board, pattern) |
120 | + config = tbu.load_config(args.config, None) |
121 | expected_tests = config.get(kernel_stack, {}).get('expected_tests', []) |
122 | |
123 | logging.info('SRU type: {}'.format(args.sru_type)) |
124 | @@ -241,10 +166,11 @@ def run(args, board, c3_link, jenkins_link): |
125 | summary_data = args.summary.read() |
126 | summary += summary_data |
127 | summary += '\n```\n' |
128 | - card.comment(summary) |
129 | + comment = card.comment(summary) |
130 | + comment_link = "{}#comment-{}".format(card.url, comment['id']) |
131 | else: |
132 | summary_data = "" |
133 | - checklist = find_or_create_checklist(card, 'Testflinger', expected_tests) |
134 | + checklist = tbu.find_or_create_checklist(card, 'Testflinger', expected_tests) |
135 | job_name = args.name.split('-') |
136 | cid = job_name[-2] + '-' + job_name[-1] |
137 | |
138 | @@ -265,30 +191,32 @@ def run(args, board, c3_link, jenkins_link): |
139 | print('Detected oem xenial run SUT: {}'.format(sut)) |
140 | |
141 | if args.cardonly: |
142 | - item_name = "{} ({})".format(sut, 'In progress') |
143 | + item_content = "{} ({})".format(args.name, 'In progress') |
144 | else: |
145 | - item_name = "{} ({})".format(sut, datetime.utcnow().isoformat()) |
146 | + item_content = "[{}]({}) ({})".format( |
147 | + args.name, comment_link, datetime.utcnow().isoformat()) |
148 | if jenkins_link: |
149 | - item_name += " [[JENKINS]({})]".format(jenkins_link) |
150 | + item_content += " [[JENKINS]({})]".format(jenkins_link) |
151 | if c3_link: |
152 | - item_name += " [[C3]({})]".format(c3_link) |
153 | + item_content += " [[C3]({})]".format(c3_link) |
154 | elif not args.cardonly: |
155 | # If there was no c3_link, it's because the submission failed |
156 | - attach_labels(board, card, ['TESTFLINGER CRASH']) |
157 | + tbu.attach_labels(board, card, ['TESTFLINGER CRASH']) |
158 | |
159 | # debug message |
160 | logging.info('checklist: {}'.format(checklist)) |
161 | - logging.info('item_name: {}'.format(item_name)) |
162 | - if not change_checklist_item( |
163 | - checklist, item_name, checked=no_new_fails_or_skips(summary_data)): |
164 | - checklist.add_checklist_item(item_name) |
165 | + logging.info('item_content: {}'.format(item_content)) |
166 | + if not tbu.change_checklist_item( |
167 | + checklist, args.name, item_content, |
168 | + checked=tbu.no_new_fails_or_skips(summary_data)): |
169 | + checklist.add_checklist_item(item_content) |
170 | |
171 | if not [c for c in card.fetch_checklists() if c.name == 'Sign-Off']: |
172 | - checklist = find_or_create_checklist(card, 'Sign-Off') |
173 | + checklist = tbu.find_or_create_checklist(card, 'Sign-Off') |
174 | checklist.add_checklist_item('Ready for ' + lanes[0], True) |
175 | checklist.add_checklist_item('Ready for ' + lanes[1]) |
176 | checklist.add_checklist_item('Can be Archived') |
177 | - checklist = find_or_create_checklist(card, 'Revisions') |
178 | + checklist = tbu.find_or_create_checklist(card, 'Revisions') |
179 | rev = '{} ({})'.format(deb_version, args.arch) |
180 | if rev not in [item['name'] for item in checklist.items]: |
181 | checklist.add_checklist_item(rev) |
182 | @@ -313,11 +241,11 @@ def run(args, board, c3_link, jenkins_link): |
183 | def main(): |
184 | parser = argparse.ArgumentParser() |
185 | parser.add_argument('--key', help="Trello API key", |
186 | - **environ_or_required('TRELLO_API_KEY')) |
187 | + **tbu.environ_or_required('TRELLO_API_KEY')) |
188 | parser.add_argument('--token', help="Trello OAuth token", |
189 | - **environ_or_required('TRELLO_TOKEN')) |
190 | + **tbu.environ_or_required('TRELLO_TOKEN')) |
191 | parser.add_argument('--board', help="Trello board identifier", |
192 | - **environ_or_required('TRELLO_BOARD')) |
193 | + **tbu.environ_or_required('TRELLO_BOARD')) |
194 | parser.add_argument('--config', help="Pool configuration", |
195 | type=argparse.FileType()) |
196 | parser.add_argument('-a', '--arch', help="deb architecture", |
197 | @@ -363,7 +291,7 @@ class TestTrelloUpdaterKernelDebSRU(unittest.TestCase): |
198 | |
199 | def _mock_factory(self, jenkins_job_template, card_name, packages_info): |
200 | parser = argparse.ArgumentParser() |
201 | - args = parser.parse_args() |
202 | + args = parser.parse_args([]) |
203 | args.__dict__.update(jenkins_job_template) |
204 | |
205 | self.args = args |
206 | @@ -398,6 +326,7 @@ class TestTrelloUpdaterKernelDebSRU(unittest.TestCase): |
207 | |
208 | def test_stock_xenial_4_4_kernel_stack(self): |
209 | jenkins_job_template = { |
210 | + 'cardonly': False, |
211 | 'name': 'xenial-desktop-201606-22344', |
212 | 'arch': 'amd64', |
213 | 'kernel': 'linux-generic', |
214 | @@ -418,6 +347,7 @@ class TestTrelloUpdaterKernelDebSRU(unittest.TestCase): |
215 | |
216 | def test_stock_xenial_4_15_kernel_stack(self): |
217 | jenkins_job_template = { |
218 | + 'cardonly': False, |
219 | 'name': 'xenial-hwe-desktop-201606-22344', |
220 | 'arch': 'amd64', |
221 | 'kernel': 'linux-generic-hwe-16_04', |
222 | @@ -439,6 +369,7 @@ class TestTrelloUpdaterKernelDebSRU(unittest.TestCase): |
223 | |
224 | def test_stock_bionic_4_15_kernel_stack(self): |
225 | jenkins_job_template = { |
226 | + 'cardonly': False, |
227 | 'name': 'bionic-desktop-201606-22344', |
228 | 'arch': 'amd64', |
229 | 'kernel': 'linux-generic', |
230 | @@ -459,6 +390,7 @@ class TestTrelloUpdaterKernelDebSRU(unittest.TestCase): |
231 | |
232 | def test_oem_xenial_4_4_kernel_stack(self): |
233 | jenkins_job_template = { |
234 | + 'cardonly': False, |
235 | 'name': 'xenial-desktop-201610-25144', |
236 | 'arch': 'amd64', |
237 | 'kernel': 'linux-generic', |
238 | @@ -479,6 +411,7 @@ class TestTrelloUpdaterKernelDebSRU(unittest.TestCase): |
239 | |
240 | def test_oem_xenial_4_15_kernel_stack(self): |
241 | jenkins_job_template = { |
242 | + 'cardonly': False, |
243 | 'name': 'xenial-hwe-desktop-201802-26107', |
244 | 'arch': 'amd64', |
245 | 'kernel': 'linux-generic-hwe-16_04', |
246 | @@ -500,6 +433,7 @@ class TestTrelloUpdaterKernelDebSRU(unittest.TestCase): |
247 | |
248 | def test_oem_bionic_4_15_kernel_stack(self): |
249 | jenkins_job_template = { |
250 | + 'cardonly': False, |
251 | 'name': 'bionic-desktop-201802-26107', |
252 | 'arch': 'amd64', |
253 | 'kernel': 'linux-oem', |
254 | @@ -520,6 +454,7 @@ class TestTrelloUpdaterKernelDebSRU(unittest.TestCase): |
255 | |
256 | def test_oem_osp1_bionic_4_15_kernel_stack(self): |
257 | jenkins_job_template = { |
258 | + 'cardonly': False, |
259 | 'name': 'bionic-desktop-201906-27089', |
260 | 'arch': 'amd64', |
261 | 'kernel': 'linux-oem-osp1', |
262 | @@ -541,6 +476,7 @@ class TestTrelloUpdaterKernelDebSRU(unittest.TestCase): |
263 | |
264 | def test_argos_dgx_station_xenial_4_4(self): |
265 | jenkins_job_template = { |
266 | + 'cardonly': False, |
267 | 'name': 'xenial-desktop-201711-25989', |
268 | 'arch': 'amd64', |
269 | 'kernel': 'linux-generic', |
270 | @@ -567,6 +503,7 @@ class TestTrelloUpdaterKernelDebSRU(unittest.TestCase): |
271 | |
272 | def test_argos_dgx_1_xenial_4_4(self): |
273 | jenkins_job_template = { |
274 | + 'cardonly': False, |
275 | 'name': 'xenial-server-201802-26098', |
276 | 'arch': 'amd64', |
277 | 'kernel': 'linux-generic', |
278 | @@ -593,10 +530,11 @@ class TestTrelloUpdaterKernelDebSRU(unittest.TestCase): |
279 | |
280 | def test_argos_dgx_1_xenial_hwe(self): |
281 | jenkins_job_template = { |
282 | + 'cardonly': False, |
283 | 'name': 'xenial-hwe-server-201802-26098', |
284 | 'arch': 'amd64', |
285 | 'kernel': 'linux-generic-hwe-16_04', |
286 | - 'series': 'xenial-hwe', |
287 | + 'series': 'xenial', |
288 | 'sru_type': 'oem', |
289 | 'queue': 'argos-201802-26098', |
290 | 'config': self.debs_yaml_stream, |
291 | diff --git a/trello-board-updater-image-testing.py b/trello-board-updater-image-testing.py |
292 | index ed1485b..ca97b92 100755 |
293 | --- a/trello-board-updater-image-testing.py |
294 | +++ b/trello-board-updater-image-testing.py |
295 | @@ -1,85 +1,31 @@ |
296 | #!/usr/bin/env python3 |
297 | -# Copyright 2019 Canonical Ltd. |
298 | +# Copyright 2019-2020 Canonical Ltd. |
299 | # All rights reserved. |
300 | # |
301 | # Written by: |
302 | # Paul Larson <paul.larson@canonical.com> |
303 | +# Sylvain Pineau <sylvain.pineau@canonical.com> |
304 | |
305 | import argparse |
306 | +import importlib |
307 | import os |
308 | import re |
309 | |
310 | from datetime import datetime |
311 | from trello import TrelloClient |
312 | |
313 | - |
314 | -def environ_or_required(key): |
315 | - if os.environ.get(key): |
316 | - return {'default': os.environ.get(key)} |
317 | - else: |
318 | - return {'required': True} |
319 | - |
320 | - |
321 | -def search_card(board, query, card_filter="open"): |
322 | - for card in board.get_cards(card_filter=card_filter): |
323 | - if re.match(query, card.name): |
324 | - return card |
325 | - |
326 | - |
327 | -def archive_card(card): |
328 | - print('Archiving old card:', card) |
329 | - card.set_closed(True) |
330 | - |
331 | - |
332 | -def find_or_create_checklist(card, checklist_name, items=None): |
333 | - existing_checklists = card.fetch_checklists() |
334 | - checklist = None |
335 | - for c in existing_checklists: |
336 | - if c.name == checklist_name: |
337 | - checklist = c |
338 | - break |
339 | - if not checklist: |
340 | - checklist = card.add_checklist(checklist_name, []) |
341 | - if items: |
342 | - for item in items: |
343 | - checklist.add_checklist_item(item + ' (NO RESULTS)') |
344 | - return checklist |
345 | - |
346 | - |
347 | -def change_checklist_item(checklist, name, checked=False): |
348 | - # keep the trailing space so that we don't match the wrong thing later |
349 | - r = re.match('\[*(.* )\(', name) |
350 | - if r: |
351 | - device_name = r.group(1) |
352 | - for item in checklist.items: |
353 | - if device_name in item.get('name'): |
354 | - checklist.rename_checklist_item(item.get('name'), name) |
355 | - checklist.set_checklist_item(name, checked) |
356 | - return True |
357 | - else: |
358 | - return False |
359 | - else: |
360 | - print('WARNING: Invalid name specified', name) |
361 | - |
362 | - |
363 | -def no_new_fails_or_skips(summary_data): |
364 | - """Check summary data for new fails or skips |
365 | - |
366 | - Return True if there are no new fails or skips detected and if all |
367 | - tests passed |
368 | - """ |
369 | - return ("No new failed or skipped tests" in summary_data and |
370 | - "All tests passed" in summary_data) |
371 | +tbu = importlib.import_module("trello-board-updater") |
372 | +tbm = importlib.import_module("trello-board-manager") |
373 | |
374 | |
375 | def main(): |
376 | parser = argparse.ArgumentParser() |
377 | parser.add_argument('--key', help="Trello API key", |
378 | - **environ_or_required('TRELLO_API_KEY')) |
379 | + **tbu.environ_or_required('TRELLO_API_KEY')) |
380 | parser.add_argument('--token', help="Trello OAuth token", |
381 | - **environ_or_required('TRELLO_TOKEN')) |
382 | + **tbu.environ_or_required('TRELLO_TOKEN')) |
383 | parser.add_argument('--board', help="Trello board identifier", |
384 | - **environ_or_required('TRELLO_BOARD')) |
385 | + **tbu.environ_or_required('TRELLO_BOARD')) |
386 | parser.add_argument('-n', '--name', help="SUT device name", required=True) |
387 | parser.add_argument('-i', '--image', help="image name", required=True) |
388 | parser.add_argument('-c', '--channel', help="image name", default="stable") |
389 | @@ -96,14 +42,14 @@ def main(): |
390 | re.escape(args.channel), |
391 | re.escape(args.version)) |
392 | # First, see if this exact card already exists |
393 | - card = search_card(board, pattern) |
394 | + card = tbu.search_card(board, pattern) |
395 | |
396 | # If not, see if there's an older one for this image |
397 | if not card: |
398 | pattern = "{} - {} - .*".format(re.escape(args.image), args.channel) |
399 | - card = search_card(board, pattern) |
400 | + card = tbu.search_card(board, pattern) |
401 | if card: |
402 | - archive_card(card) |
403 | + tbm.archive_card(card) |
404 | # If we get here, then either we just archived the old card, or |
405 | # it didn't exist. We need to create it either way |
406 | lane = None |
407 | @@ -122,19 +68,21 @@ def main(): |
408 | summary_data = args.summary.read() |
409 | summary += summary_data |
410 | summary += '\n```\n' |
411 | - card.comment(summary) |
412 | - checklist = find_or_create_checklist(card, 'Testflinger') |
413 | - item_name = "{} ({})".format(args.name, datetime.utcnow().isoformat()) |
414 | + comment = card.comment(summary) |
415 | + comment_link = "{}#comment-{}".format(card.url, comment['id']) |
416 | + checklist = tbu.find_or_create_checklist(card, 'Testflinger') |
417 | + item_content = "[{}]({}) ({})".format( |
418 | + args.name, comment_link, datetime.utcnow().isoformat()) |
419 | if jenkins_link: |
420 | - item_name += " [[JENKINS]({})]".format(jenkins_link) |
421 | + item_content += " [[JENKINS]({})]".format(jenkins_link) |
422 | if c3_link: |
423 | - item_name += " [[C3]({})]".format(c3_link) |
424 | + item_content += " [[C3]({})]".format(c3_link) |
425 | |
426 | - if not change_checklist_item( |
427 | - checklist, item_name, |
428 | - checked=no_new_fails_or_skips(summary_data)): |
429 | + if not tbu.change_checklist_item( |
430 | + checklist, args.name, item_content, |
431 | + checked=tbu.no_new_fails_or_skips(summary_data)): |
432 | checklist.add_checklist_item( |
433 | - item_name, checked=no_new_fails_or_skips(summary_data)) |
434 | + item_content, checked=tbu.no_new_fails_or_skips(summary_data)) |
435 | |
436 | |
437 | if __name__ == "__main__": |
great!