Merge ~cjwatson/lpci:release-multi-arch into lpci:main
- Git
- lp:~cjwatson/lpci
- release-multi-arch
- Merge into main
Proposed by
Colin Watson
Status: | Merged |
---|---|
Merged at revision: | 1dd9ae1b0bee3e802a3fe7983f3fff1b361d215c |
Proposed branch: | ~cjwatson/lpci:release-multi-arch |
Merge into: | lpci:main |
Diff against target: |
606 lines (+294/-37) 8 files modified
.mypy.ini (+1/-1) NEWS.rst (+7/-0) docs/cli-interface.rst (+3/-0) lpci/commands/release.py (+47/-8) lpci/commands/tests/filter-wadl.py (+1/-1) lpci/commands/tests/launchpad-wadl.xml (+27/-20) lpci/commands/tests/test_release.py (+207/-7) setup.cfg (+1/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Simone Pelosi | Approve | ||
Review via email: mp+452398@code.launchpad.net |
Commit message
Release the latest build of each architecture
Description of the change
`lpci release` incorrectly released the latest build regardless of architecture; this approach was OK when builds were typically only dispatched for a single architecture, but now that we need to handle releasing for (e.g.) both amd64 and arm64, it doesn't work so well. Release the latest build for each architecture for which builds exist instead, and add a new `--architecture` option for the case where people want to override this behaviour and only release the latest build for a single architecture.
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/.mypy.ini b/.mypy.ini |
2 | index 2d0ab77..d749745 100644 |
3 | --- a/.mypy.ini |
4 | +++ b/.mypy.ini |
5 | @@ -6,7 +6,7 @@ disallow_subclassing_any = false |
6 | disallow_untyped_calls = false |
7 | disallow_untyped_defs = false |
8 | |
9 | -[mypy-fixtures.*,launchpadlib.*,systemfixtures.*,testtools.*,pluggy.*,wadllib.*] |
10 | +[mypy-fixtures.*,launchpadlib.*,lazr.restfulclient.*,systemfixtures.*,testtools.*,pluggy.*,wadllib.*] |
11 | ignore_missing_imports = true |
12 | |
13 | [mypy-craft_cli.*] |
14 | diff --git a/NEWS.rst b/NEWS.rst |
15 | index 8c9132a..2748244 100644 |
16 | --- a/NEWS.rst |
17 | +++ b/NEWS.rst |
18 | @@ -2,6 +2,13 @@ |
19 | Version history |
20 | =============== |
21 | |
22 | +0.2.4 (unreleased) |
23 | +================== |
24 | + |
25 | +- Fix ``lpci release`` to release the latest build of each architecture (or |
26 | + a single architecture selected by the new ``--architecture`` option), |
27 | + rather than only releasing the latest build regardless of architecture. |
28 | + |
29 | 0.2.3 (2023-07-20) |
30 | ================== |
31 | |
32 | diff --git a/docs/cli-interface.rst b/docs/cli-interface.rst |
33 | index bab45d1..eb0fafc 100644 |
34 | --- a/docs/cli-interface.rst |
35 | +++ b/docs/cli-interface.rst |
36 | @@ -135,3 +135,6 @@ lpci release optional arguments |
37 | - ``--commit ID`` to specify the source Git branch name, tag name, or commit |
38 | ID (defaults to the tip commit found for the current branch in the |
39 | upstream repository). |
40 | + |
41 | +- ``--architecture NAME`` to only release the builds for this architecture |
42 | + (defaults to the latest build for each built architecture). |
43 | diff --git a/lpci/commands/release.py b/lpci/commands/release.py |
44 | index d51e239..fd523eb 100644 |
45 | --- a/lpci/commands/release.py |
46 | +++ b/lpci/commands/release.py |
47 | @@ -3,11 +3,14 @@ |
48 | |
49 | import re |
50 | from argparse import ArgumentParser, Namespace |
51 | +from collections import defaultdict |
52 | from operator import attrgetter |
53 | +from typing import Dict, List |
54 | from urllib.parse import urlparse |
55 | |
56 | from craft_cli import BaseCommand, emit |
57 | from launchpadlib.launchpad import Launchpad |
58 | +from lazr.restfulclient.resource import Entry |
59 | |
60 | from lpci.errors import CommandError |
61 | from lpci.git import get_current_branch, get_current_remote_url |
62 | @@ -52,13 +55,21 @@ class ReleaseCommand(BaseCommand): |
63 | ), |
64 | ) |
65 | parser.add_argument( |
66 | + "-a", |
67 | + "--architecture", |
68 | + help=( |
69 | + "Only release the latest build for this architecture " |
70 | + "(defaults to the latest build for each built architecture)" |
71 | + ), |
72 | + ) |
73 | + parser.add_argument( |
74 | "archive", help="Target archive, e.g. ppa:OWNER/DISTRIBUTION/NAME" |
75 | ) |
76 | parser.add_argument("suite", help="Target suite, e.g. focal") |
77 | parser.add_argument("channel", help="Target channel, e.g. edge") |
78 | |
79 | - def run(self, args: Namespace) -> int: |
80 | - """Run the command.""" |
81 | + def _check_args(self, args: Namespace) -> None: |
82 | + """Check and process arguments.""" |
83 | if args.repository is None: |
84 | current_remote_url = get_current_remote_url() |
85 | if current_remote_url is None: |
86 | @@ -86,9 +97,9 @@ class ReleaseCommand(BaseCommand): |
87 | "branch." |
88 | ) |
89 | |
90 | - launchpad = Launchpad.login_with( |
91 | - "lpci", args.launchpad_instance, version="devel" |
92 | - ) |
93 | + def _find_builds( |
94 | + self, launchpad: Launchpad, args: Namespace |
95 | + ) -> Dict[str, List[Entry]]: |
96 | repository = launchpad.git_repositories.getByPath(path=args.repository) |
97 | if repository is None: |
98 | raise CommandError( |
99 | @@ -107,6 +118,10 @@ class ReleaseCommand(BaseCommand): |
100 | report.ci_build |
101 | for report in reports |
102 | if report.ci_build is not None |
103 | + and ( |
104 | + args.architecture is None |
105 | + or report.ci_build.arch_tag == args.architecture |
106 | + ) |
107 | and report.ci_build.buildstate == "Successfully built" |
108 | and report.getArtifactURLs(artifact_type="Binary") |
109 | ] |
110 | @@ -115,20 +130,44 @@ class ReleaseCommand(BaseCommand): |
111 | f"{args.repository}:{args.commit} has no completed CI " |
112 | f"builds with attached files." |
113 | ) |
114 | - latest_build = sorted(builds, key=attrgetter("datebuilt"))[-1] |
115 | + builds_by_arch = defaultdict(list) |
116 | + for build in builds: |
117 | + builds_by_arch[build.arch_tag].append(build) |
118 | + return builds_by_arch |
119 | + |
120 | + def _release_build( |
121 | + self, launchpad: Launchpad, build: Entry, args: Namespace |
122 | + ) -> None: |
123 | archive = launchpad.archives.getByReference(reference=args.archive) |
124 | description = ( |
125 | - f"build of {args.repository}:{args.commit} to " |
126 | + f"{build.arch_tag} build of {args.repository}:{args.commit} to " |
127 | f"{args.archive} {args.suite} {args.channel}" |
128 | ) |
129 | if args.dry_run: |
130 | emit.message(f"Would release {description}.") |
131 | else: |
132 | archive.uploadCIBuild( |
133 | - ci_build=latest_build, |
134 | + ci_build=build, |
135 | to_series=args.suite, |
136 | to_pocket="Release", |
137 | to_channel=args.channel, |
138 | ) |
139 | emit.message(f"Released {description}.") |
140 | + |
141 | + def run(self, args: Namespace) -> int: |
142 | + """Run the command.""" |
143 | + self._check_args(args) |
144 | + launchpad = Launchpad.login_with( |
145 | + "lpci", args.launchpad_instance, version="devel" |
146 | + ) |
147 | + builds_by_arch = self._find_builds(launchpad, args) |
148 | + if args.architecture is not None: |
149 | + arch_tags = [args.architecture] |
150 | + else: |
151 | + arch_tags = sorted(builds_by_arch) |
152 | + for arch_tag in arch_tags: |
153 | + latest_build = sorted( |
154 | + builds_by_arch[arch_tag], key=attrgetter("datebuilt") |
155 | + )[-1] |
156 | + self._release_build(launchpad, latest_build, args) |
157 | return 0 |
158 | diff --git a/lpci/commands/tests/filter-wadl.py b/lpci/commands/tests/filter-wadl.py |
159 | index 91ddfbc..89705f4 100755 |
160 | --- a/lpci/commands/tests/filter-wadl.py |
161 | +++ b/lpci/commands/tests/filter-wadl.py |
162 | @@ -23,7 +23,7 @@ keep_collections = { |
163 | |
164 | keep_entries = { |
165 | "archive": ["uploadCIBuild"], |
166 | - "ci_build": ["buildstate", "datebuilt", "get"], |
167 | + "ci_build": ["arch_tag", "buildstate", "datebuilt", "get"], |
168 | "git_ref": ["commit_sha1", "get"], |
169 | "git_repository": ["getRefByPath", "getStatusReports"], |
170 | "revision_status_report": [ |
171 | diff --git a/lpci/commands/tests/launchpad-wadl.xml b/lpci/commands/tests/launchpad-wadl.xml |
172 | index 836fdd6..d61b793 100644 |
173 | --- a/lpci/commands/tests/launchpad-wadl.xml |
174 | +++ b/lpci/commands/tests/launchpad-wadl.xml |
175 | @@ -263,6 +263,12 @@ A build record for a pipeline of CI jobs. |
176 | The value of the HTTP ETag for this resource. |
177 | </wadl:doc> |
178 | </wadl:param> |
179 | + <wadl:param style="plain" required="true" name="arch_tag" path="$['arch_tag']"> |
180 | + <wadl:doc> |
181 | +Architecture tag |
182 | +</wadl:doc> |
183 | + |
184 | + </wadl:param> |
185 | <wadl:param style="plain" required="true" name="buildstate" path="$['buildstate']"> |
186 | <wadl:doc> |
187 | <html:p>Status</html:p> |
188 | @@ -281,6 +287,7 @@ A build record for a pipeline of CI jobs. |
189 | <wadl:option value="Uploading build" /> |
190 | <wadl:option value="Cancelling build" /> |
191 | <wadl:option value="Cancelled build" /> |
192 | + <wadl:option value="Gathering build output" /> |
193 | </wadl:param> |
194 | <wadl:param style="plain" required="true" name="datebuilt" path="$['datebuilt']" type="xsd:dateTime"> |
195 | <wadl:doc> |
196 | @@ -425,29 +432,27 @@ A reference in a Git repository. |
197 | <wadl:doc> |
198 | A Git repository. |
199 | </wadl:doc> |
200 | - <wadl:method id="git_repository-getStatusReports" name="GET"> |
201 | + <wadl:method id="git_repository-getRefByPath" name="GET"> |
202 | <wadl:doc> |
203 | -<html:p>Retrieves the list of reports that exist for a commit.</html:p> |
204 | -<html:blockquote> |
205 | +<html:p>Look up a single reference in this repository by path.</html:p> |
206 | <html:table class="rst-docutils field-list" frame="void" rules="none"> |
207 | <html:col class="field-name" /> |
208 | <html:col class="field-body" /> |
209 | <html:tbody valign="top"> |
210 | -<html:tr class="rst-field"><html:th class="rst-field-name" colspan="2">param commit_sha1:</html:th></html:tr> |
211 | -<html:tr class="rst-field"><html:td>\ </html:td><html:td class="rst-field-body">The commit sha1 for the report.</html:td> |
212 | +<html:tr class="rst-field"><html:th class="rst-field-name">param path:</html:th><html:td class="rst-field-body">A string to look up as a path.</html:td> |
213 | +</html:tr> |
214 | +<html:tr class="rst-field"><html:th class="rst-field-name">return:</html:th><html:td class="rst-field-body">An IGitRef, or None.</html:td> |
215 | </html:tr> |
216 | </html:tbody> |
217 | </html:table> |
218 | -</html:blockquote> |
219 | -<html:p>Scopes: <html:tt class="rst-docutils literal">repository:build_status</html:tt></html:p> |
220 | |
221 | </wadl:doc> |
222 | <wadl:request> |
223 | |
224 | - <wadl:param style="query" name="ws.op" required="true" fixed="getStatusReports" /> |
225 | - <wadl:param style="query" name="commit_sha1" required="true"> |
226 | + <wadl:param style="query" name="ws.op" required="true" fixed="getRefByPath" /> |
227 | + <wadl:param style="query" name="path" required="true"> |
228 | <wadl:doc> |
229 | -The Git commit for which this report is built. |
230 | +A string to look up as a path. |
231 | </wadl:doc> |
232 | |
233 | </wadl:param> |
234 | @@ -455,30 +460,32 @@ The Git commit for which this report is built. |
235 | </wadl:request> |
236 | <wadl:response> |
237 | |
238 | - <wadl:representation href="https://api.launchpad.net/devel/#revision_status_report-page" /> |
239 | + <wadl:representation href="https://api.launchpad.net/devel/#git_ref-full" /> |
240 | </wadl:response> |
241 | </wadl:method> |
242 | - <wadl:method id="git_repository-getRefByPath" name="GET"> |
243 | + <wadl:method id="git_repository-getStatusReports" name="GET"> |
244 | <wadl:doc> |
245 | -<html:p>Look up a single reference in this repository by path.</html:p> |
246 | +<html:p>Retrieves the list of reports that exist for a commit.</html:p> |
247 | +<html:blockquote> |
248 | <html:table class="rst-docutils field-list" frame="void" rules="none"> |
249 | <html:col class="field-name" /> |
250 | <html:col class="field-body" /> |
251 | <html:tbody valign="top"> |
252 | -<html:tr class="rst-field"><html:th class="rst-field-name">param path:</html:th><html:td class="rst-field-body">A string to look up as a path.</html:td> |
253 | -</html:tr> |
254 | -<html:tr class="rst-field"><html:th class="rst-field-name">return:</html:th><html:td class="rst-field-body">An IGitRef, or None.</html:td> |
255 | +<html:tr class="rst-field"><html:th class="rst-field-name" colspan="2">param commit_sha1:</html:th></html:tr> |
256 | +<html:tr class="rst-field"><html:td>\ </html:td><html:td class="rst-field-body">The commit sha1 for the report.</html:td> |
257 | </html:tr> |
258 | </html:tbody> |
259 | </html:table> |
260 | +</html:blockquote> |
261 | +<html:p>Scopes: <html:tt class="rst-docutils literal">repository:build_status</html:tt></html:p> |
262 | |
263 | </wadl:doc> |
264 | <wadl:request> |
265 | |
266 | - <wadl:param style="query" name="ws.op" required="true" fixed="getRefByPath" /> |
267 | - <wadl:param style="query" name="path" required="true"> |
268 | + <wadl:param style="query" name="ws.op" required="true" fixed="getStatusReports" /> |
269 | + <wadl:param style="query" name="commit_sha1" required="true"> |
270 | <wadl:doc> |
271 | -A string to look up as a path. |
272 | +The Git commit for which this report is built. |
273 | </wadl:doc> |
274 | |
275 | </wadl:param> |
276 | @@ -486,7 +493,7 @@ A string to look up as a path. |
277 | </wadl:request> |
278 | <wadl:response> |
279 | |
280 | - <wadl:representation href="https://api.launchpad.net/devel/#git_ref-full" /> |
281 | + <wadl:representation href="https://api.launchpad.net/devel/#revision_status_report-page" /> |
282 | </wadl:response> |
283 | </wadl:method> |
284 | </wadl:resource_type> |
285 | diff --git a/lpci/commands/tests/test_release.py b/lpci/commands/tests/test_release.py |
286 | index 96649f4..46b5883 100644 |
287 | --- a/lpci/commands/tests/test_release.py |
288 | +++ b/lpci/commands/tests/test_release.py |
289 | @@ -269,6 +269,7 @@ class TestRelease(CommandBaseTestCase): |
290 | "entries": [ |
291 | { |
292 | "ci_build": { |
293 | + "arch_tag": "amd64", |
294 | "buildstate": "Successfully built", |
295 | "datebuilt": datetime(2022, 1, 1, 0, 0, 0), |
296 | }, |
297 | @@ -301,7 +302,7 @@ class TestRelease(CommandBaseTestCase): |
298 | MatchesStructure.byEquality( |
299 | exit_code=0, |
300 | messages=[ |
301 | - f"Would release build of example:{commit_sha1} to " |
302 | + f"Would release amd64 build of example:{commit_sha1} to " |
303 | f"ppa:owner/ubuntu/name focal edge." |
304 | ], |
305 | ), |
306 | @@ -318,6 +319,7 @@ class TestRelease(CommandBaseTestCase): |
307 | "entries": [ |
308 | { |
309 | "ci_build": { |
310 | + "arch_tag": "amd64", |
311 | "buildstate": "Successfully built", |
312 | "datebuilt": datetime(2022, 1, 1, 0, 0, 0), |
313 | }, |
314 | @@ -325,6 +327,7 @@ class TestRelease(CommandBaseTestCase): |
315 | }, |
316 | { |
317 | "ci_build": { |
318 | + "arch_tag": "amd64", |
319 | "buildstate": "Successfully built", |
320 | "datebuilt": datetime(2022, 1, 1, 12, 0, 0), |
321 | }, |
322 | @@ -356,7 +359,7 @@ class TestRelease(CommandBaseTestCase): |
323 | MatchesStructure.byEquality( |
324 | exit_code=0, |
325 | messages=[ |
326 | - f"Released build of example:{commit_sha1} to " |
327 | + f"Released amd64 build of example:{commit_sha1} to " |
328 | f"ppa:owner/ubuntu/name focal edge." |
329 | ], |
330 | ), |
331 | @@ -366,8 +369,9 @@ class TestRelease(CommandBaseTestCase): |
332 | upload, |
333 | MatchesListwise( |
334 | [ |
335 | - MatchesStructure( |
336 | - datebuilt=Equals(datetime(2022, 1, 1, 12, 0, 0)) |
337 | + MatchesStructure.byEquality( |
338 | + arch_tag="amd64", |
339 | + datebuilt=datetime(2022, 1, 1, 12, 0, 0), |
340 | ), |
341 | Equals("focal"), |
342 | Equals("Release"), |
343 | @@ -385,6 +389,7 @@ class TestRelease(CommandBaseTestCase): |
344 | "entries": [ |
345 | { |
346 | "ci_build": { |
347 | + "arch_tag": "amd64", |
348 | "buildstate": "Successfully built", |
349 | "datebuilt": datetime(2022, 1, 1, 0, 0, 0), |
350 | }, |
351 | @@ -392,6 +397,7 @@ class TestRelease(CommandBaseTestCase): |
352 | }, |
353 | { |
354 | "ci_build": { |
355 | + "arch_tag": "amd64", |
356 | "buildstate": "Successfully built", |
357 | "datebuilt": datetime(2022, 1, 1, 12, 0, 0), |
358 | }, |
359 | @@ -423,7 +429,7 @@ class TestRelease(CommandBaseTestCase): |
360 | MatchesStructure.byEquality( |
361 | exit_code=0, |
362 | messages=[ |
363 | - f"Released build of example:{commit_sha1} to " |
364 | + f"Released amd64 build of example:{commit_sha1} to " |
365 | f"ppa:owner/ubuntu/name focal edge." |
366 | ], |
367 | ), |
368 | @@ -442,6 +448,7 @@ class TestRelease(CommandBaseTestCase): |
369 | "entries": [ |
370 | { |
371 | "ci_build": { |
372 | + "arch_tag": "amd64", |
373 | "buildstate": "Successfully built", |
374 | "datebuilt": datetime(2022, 1, 1, 0, 0, 0), |
375 | }, |
376 | @@ -469,7 +476,7 @@ class TestRelease(CommandBaseTestCase): |
377 | MatchesStructure.byEquality( |
378 | exit_code=0, |
379 | messages=[ |
380 | - f"Released build of example:{commit_sha1} to " |
381 | + f"Released amd64 build of example:{commit_sha1} to " |
382 | f"ppa:owner/ubuntu/name focal edge." |
383 | ], |
384 | ), |
385 | @@ -488,6 +495,7 @@ class TestRelease(CommandBaseTestCase): |
386 | "entries": [ |
387 | { |
388 | "ci_build": { |
389 | + "arch_tag": "amd64", |
390 | "buildstate": "Successfully built", |
391 | "datebuilt": datetime(2022, 1, 1, 0, 0, 0), |
392 | }, |
393 | @@ -515,8 +523,200 @@ class TestRelease(CommandBaseTestCase): |
394 | MatchesStructure.byEquality( |
395 | exit_code=0, |
396 | messages=[ |
397 | - f"Released build of example:{commit_sha1} to " |
398 | + f"Released amd64 build of example:{commit_sha1} to " |
399 | f"ppa:owner/ubuntu/name focal edge." |
400 | ], |
401 | ), |
402 | ) |
403 | + |
404 | + def test_release_multiple_architectures(self): |
405 | + lp = self.make_fake_launchpad() |
406 | + commit_sha1 = "1" * 40 |
407 | + lp.git_repositories = { |
408 | + "getByPath": lambda path: { |
409 | + "getRefByPath": lambda path: {"commit_sha1": commit_sha1}, |
410 | + "getStatusReports": lambda commit_sha1: { |
411 | + "entries": [ |
412 | + { |
413 | + "ci_build": { |
414 | + "arch_tag": "amd64", |
415 | + "buildstate": "Successfully built", |
416 | + "datebuilt": datetime(2022, 1, 1, 0, 0, 0), |
417 | + }, |
418 | + "getArtifactURLs": lambda artifact_type: ["url"], |
419 | + }, |
420 | + { |
421 | + "ci_build": { |
422 | + "arch_tag": "arm64", |
423 | + "buildstate": "Successfully built", |
424 | + "datebuilt": datetime(2022, 1, 1, 1, 0, 0), |
425 | + }, |
426 | + "getArtifactURLs": lambda artifact_type: ["url"], |
427 | + }, |
428 | + { |
429 | + "ci_build": { |
430 | + "arch_tag": "amd64", |
431 | + "buildstate": "Successfully built", |
432 | + "datebuilt": datetime(2022, 1, 1, 12, 0, 0), |
433 | + }, |
434 | + "getArtifactURLs": lambda artifact_type: ["url"], |
435 | + }, |
436 | + { |
437 | + "ci_build": { |
438 | + "arch_tag": "arm64", |
439 | + "buildstate": "Successfully built", |
440 | + "datebuilt": datetime(2022, 1, 1, 13, 0, 0), |
441 | + }, |
442 | + "getArtifactURLs": lambda artifact_type: ["url"], |
443 | + }, |
444 | + ] |
445 | + }, |
446 | + } |
447 | + } |
448 | + lp.archives = { |
449 | + "getByReference": lambda reference: { |
450 | + "uploadCIBuild": self.fake_upload |
451 | + } |
452 | + } |
453 | + |
454 | + result = self.run_command( |
455 | + "release", |
456 | + "--repository", |
457 | + "example", |
458 | + "--commit", |
459 | + "branch", |
460 | + "ppa:owner/ubuntu/name", |
461 | + "focal", |
462 | + "edge", |
463 | + ) |
464 | + |
465 | + self.assertThat( |
466 | + result, |
467 | + MatchesStructure.byEquality( |
468 | + exit_code=0, |
469 | + messages=[ |
470 | + f"Released amd64 build of example:{commit_sha1} to " |
471 | + f"ppa:owner/ubuntu/name focal edge.", |
472 | + f"Released arm64 build of example:{commit_sha1} to " |
473 | + f"ppa:owner/ubuntu/name focal edge.", |
474 | + ], |
475 | + ), |
476 | + ) |
477 | + self.assertThat( |
478 | + self.uploads, |
479 | + MatchesListwise( |
480 | + [ |
481 | + MatchesListwise( |
482 | + [ |
483 | + MatchesStructure.byEquality( |
484 | + arch_tag="amd64", |
485 | + datebuilt=datetime(2022, 1, 1, 12, 0, 0), |
486 | + ), |
487 | + Equals("focal"), |
488 | + Equals("Release"), |
489 | + Equals("edge"), |
490 | + ] |
491 | + ), |
492 | + MatchesListwise( |
493 | + [ |
494 | + MatchesStructure.byEquality( |
495 | + arch_tag="arm64", |
496 | + datebuilt=datetime(2022, 1, 1, 13, 0, 0), |
497 | + ), |
498 | + Equals("focal"), |
499 | + Equals("Release"), |
500 | + Equals("edge"), |
501 | + ], |
502 | + ), |
503 | + ] |
504 | + ), |
505 | + ) |
506 | + |
507 | + def test_release_select_single_architecture(self): |
508 | + lp = self.make_fake_launchpad() |
509 | + commit_sha1 = "1" * 40 |
510 | + lp.git_repositories = { |
511 | + "getByPath": lambda path: { |
512 | + "getRefByPath": lambda path: {"commit_sha1": commit_sha1}, |
513 | + "getStatusReports": lambda commit_sha1: { |
514 | + "entries": [ |
515 | + { |
516 | + "ci_build": { |
517 | + "arch_tag": "amd64", |
518 | + "buildstate": "Successfully built", |
519 | + "datebuilt": datetime(2022, 1, 1, 0, 0, 0), |
520 | + }, |
521 | + "getArtifactURLs": lambda artifact_type: ["url"], |
522 | + }, |
523 | + { |
524 | + "ci_build": { |
525 | + "arch_tag": "arm64", |
526 | + "buildstate": "Successfully built", |
527 | + "datebuilt": datetime(2022, 1, 1, 1, 0, 0), |
528 | + }, |
529 | + "getArtifactURLs": lambda artifact_type: ["url"], |
530 | + }, |
531 | + { |
532 | + "ci_build": { |
533 | + "arch_tag": "amd64", |
534 | + "buildstate": "Successfully built", |
535 | + "datebuilt": datetime(2022, 1, 1, 12, 0, 0), |
536 | + }, |
537 | + "getArtifactURLs": lambda artifact_type: ["url"], |
538 | + }, |
539 | + { |
540 | + "ci_build": { |
541 | + "arch_tag": "arm64", |
542 | + "buildstate": "Successfully built", |
543 | + "datebuilt": datetime(2022, 1, 1, 13, 0, 0), |
544 | + }, |
545 | + "getArtifactURLs": lambda artifact_type: ["url"], |
546 | + }, |
547 | + ] |
548 | + }, |
549 | + } |
550 | + } |
551 | + lp.archives = { |
552 | + "getByReference": lambda reference: { |
553 | + "uploadCIBuild": self.fake_upload |
554 | + } |
555 | + } |
556 | + |
557 | + result = self.run_command( |
558 | + "release", |
559 | + "--repository", |
560 | + "example", |
561 | + "--commit", |
562 | + "branch", |
563 | + "--architecture", |
564 | + "amd64", |
565 | + "ppa:owner/ubuntu/name", |
566 | + "focal", |
567 | + "edge", |
568 | + ) |
569 | + |
570 | + self.assertThat( |
571 | + result, |
572 | + MatchesStructure.byEquality( |
573 | + exit_code=0, |
574 | + messages=[ |
575 | + f"Released amd64 build of example:{commit_sha1} to " |
576 | + f"ppa:owner/ubuntu/name focal edge.", |
577 | + ], |
578 | + ), |
579 | + ) |
580 | + [upload] = self.uploads |
581 | + self.assertThat( |
582 | + upload, |
583 | + MatchesListwise( |
584 | + [ |
585 | + MatchesStructure.byEquality( |
586 | + arch_tag="amd64", |
587 | + datebuilt=datetime(2022, 1, 1, 12, 0, 0), |
588 | + ), |
589 | + Equals("focal"), |
590 | + Equals("Release"), |
591 | + Equals("edge"), |
592 | + ] |
593 | + ), |
594 | + ) |
595 | diff --git a/setup.cfg b/setup.cfg |
596 | index 803ab01..1fe623d 100644 |
597 | --- a/setup.cfg |
598 | +++ b/setup.cfg |
599 | @@ -28,6 +28,7 @@ install_requires = |
600 | craft-providers |
601 | jinja2 |
602 | launchpadlib[keyring] |
603 | + lazr.restfulclient |
604 | pluggy |
605 | pydantic |
606 | python-dotenv |
LGTM! Thank you Colin for this fix