Merge ~pelpsi/lpci:snaps-key-improved-to-specify-channel-and-classic-parameters into lpci:main
Status: | Merged |
---|---|
Approved by: | Simone Pelosi |
Approved revision: | 8e61e16fe92155b72bf6d98876f352e1b7e0a23b |
Merge reported by: | Simone Pelosi |
Merged at revision: | 8e61e16fe92155b72bf6d98876f352e1b7e0a23b |
Proposed branch: | ~pelpsi/lpci:snaps-key-improved-to-specify-channel-and-classic-parameters |
Merge into: | lpci:main |
Diff against target: |
627 lines (+484/-11) 7 files modified
NEWS.rst (+6/-0) docs/configuration.rst (+21/-0) lpcraft/commands/run.py (+9/-4) lpcraft/commands/tests/test_run.py (+379/-2) lpcraft/config.py (+58/-1) lpcraft/plugin/lib.py (+2/-2) lpcraft/tests/test_config.py (+9/-2) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jürgen Gmach | Approve | ||
Review via email: mp+440175@code.launchpad.net |
Commit message
Added support for channel and classic parameters
Snap keys now support channel and classic parameters.
Snap key new syntax <name>:
LP: #1995101
Description of the change
Jürgen Gmach (jugmac00) wrote : | # |
It looks like that I missed to make you aware that we use the Pydantic library to parse the yaml input. In order to leverage that library, you need to create a new configuration class, see inline comments. Sorry for the extra work! Please reach out in case you need any help or a quick introduction to this topic.
Jürgen Gmach (jugmac00) wrote : | # |
This looks good! Ideally, we could improve the error messages and make them more helpful for our users, see inline comments.
There is one thing we need to discuss. This change breaks backwards compatibility, ie configurations a la
```
snaps = [firefox, chromium]
```
will no longer work. In my last comment (see inline comments when you click on "show diff comments") I showed you a possible way to stay backwards compatible.
Did you try to do it? Have you encountered any problems? Or did you intentionally decide to break backwards compatibility?
Jürgen Gmach (jugmac00) wrote : | # |
This looks really good now! Please have a look at my comments before merging.
And please remember... for lpcraft we do not have a merge bot, so after applying the suggested changes, you need to merge the branch yourself.
Imho it would make sense to squash your commits prior to merging.
Thanks for your patience!
Preview Diff
1 | diff --git a/NEWS.rst b/NEWS.rst |
2 | index d38f50c..67eb3af 100644 |
3 | --- a/NEWS.rst |
4 | +++ b/NEWS.rst |
5 | @@ -2,6 +2,12 @@ |
6 | Version history |
7 | =============== |
8 | |
9 | +0.0.51 (2023-04-05) |
10 | +=================== |
11 | +- Add support for snap's channel and confinement level. |
12 | + See https://snapcraft.io/docs/channels and |
13 | + https://snapcraft.io/docs/snap-confinement. |
14 | + |
15 | 0.0.50 (2023-03-20) |
16 | =================== |
17 | - Rebuild the Snap package to include updated system packages. |
18 | diff --git a/docs/configuration.rst b/docs/configuration.rst |
19 | index d25c96d..8655653 100644 |
20 | --- a/docs/configuration.rst |
21 | +++ b/docs/configuration.rst |
22 | @@ -63,6 +63,7 @@ Job definitions |
23 | |
24 | ``snaps`` (optional) |
25 | Snaps to install as dependencies of this job. |
26 | + Also see the :ref:`snap-properties` section below. |
27 | |
28 | ``environment`` (optional) |
29 | A mapping of environment variable names to values, to be set while |
30 | @@ -171,6 +172,26 @@ artifacts. (This mirrors the output file structure created by ``lpcraft run |
31 | to which the artifacts of the chosen job will be copied; the directory |
32 | will be created if necessary. Paths may not escape the build tree. |
33 | |
34 | +.. _snap-properties: |
35 | + |
36 | +Snap properties |
37 | +----------------- |
38 | + |
39 | +``name`` |
40 | + The name of a snap will be installed.. |
41 | + |
42 | +``channel`` (optional) |
43 | + `Channel <https://snapcraft.io/docs/channels>`_ |
44 | + defining which release of a snap want to install. |
45 | + Default value: ``latest/stable``. |
46 | + |
47 | + |
48 | +``classic`` (optional) |
49 | + Classic boolean defining whether to use or not |
50 | + `confiment=classic |
51 | + <https://snapcraft.io/docs/snap-confinement>`_. |
52 | + Default value: False. |
53 | + |
54 | .. _package-repositories: |
55 | |
56 | Package-repositories properties |
57 | diff --git a/lpcraft/commands/run.py b/lpcraft/commands/run.py |
58 | index c0e00d9..3a914df 100644 |
59 | --- a/lpcraft/commands/run.py |
60 | +++ b/lpcraft/commands/run.py |
61 | @@ -543,12 +543,17 @@ def _run_job( |
62 | ) as instance: |
63 | snaps = list(itertools.chain(*pm.hook.lpcraft_install_snaps())) |
64 | for snap in snaps: |
65 | - emit.progress(f"Running `snap install {snap}`") |
66 | + emit.progress( |
67 | + "Running `snap install " |
68 | + + f"{snap.name} " |
69 | + + f"(channel={snap.channel}, " |
70 | + + f"classic={snap.classic})`" |
71 | + ) |
72 | install_from_store( |
73 | executor=instance, |
74 | - snap_name=snap, |
75 | - channel="latest/stable", |
76 | - classic=True, |
77 | + snap_name=snap.name, |
78 | + channel=snap.channel, |
79 | + classic=snap.classic, |
80 | ) |
81 | packages = list(itertools.chain(*pm.hook.lpcraft_install_packages())) |
82 | if packages: |
83 | diff --git a/lpcraft/commands/tests/test_run.py b/lpcraft/commands/tests/test_run.py |
84 | index 4466295..361661c 100644 |
85 | --- a/lpcraft/commands/tests/test_run.py |
86 | +++ b/lpcraft/commands/tests/test_run.py |
87 | @@ -1956,7 +1956,7 @@ class TestRun(RunBaseTestCase): |
88 | @responses.activate |
89 | @patch("lpcraft.commands.run.get_provider") |
90 | @patch("lpcraft.commands.run.get_host_architecture", return_value="amd64") |
91 | - def test_install_snaps( |
92 | + def test_install_snaps_classic_parameter( |
93 | self, mock_get_host_architecture, mock_get_provider |
94 | ): |
95 | def run_side_effect( |
96 | @@ -1985,7 +1985,11 @@ class TestRun(RunBaseTestCase): |
97 | series: focal |
98 | architectures: amd64 |
99 | run: tox |
100 | - snaps: [chromium, firefox] |
101 | + snaps: |
102 | + - name: chromium |
103 | + classic: True |
104 | + - name: firefox |
105 | + classic: True |
106 | """ |
107 | ) |
108 | Path(".launchpad.yaml").write_text(config) |
109 | @@ -2052,6 +2056,379 @@ class TestRun(RunBaseTestCase): |
110 | execute_run.call_args_list, |
111 | ) |
112 | |
113 | + @responses.activate |
114 | + @patch("lpcraft.commands.run.get_provider") |
115 | + @patch("lpcraft.commands.run.get_host_architecture", return_value="amd64") |
116 | + def test_install_snaps_provided_as_list_of_strings( |
117 | + self, mock_get_host_architecture, mock_get_provider |
118 | + ): |
119 | + def run_side_effect( |
120 | + command: List[str], **kwargs: Any |
121 | + ) -> "subprocess.CompletedProcess[bytes]": |
122 | + if command[0] == "curl": |
123 | + response = {"result": {"revision": "1"}, "status-code": 200} |
124 | + return subprocess.CompletedProcess( |
125 | + [], 0, stdout=json.dumps(response).encode() |
126 | + ) |
127 | + else: |
128 | + return subprocess.CompletedProcess([], 0) |
129 | + |
130 | + launcher = Mock(spec=launch) |
131 | + provider = makeLXDProvider(lxd_launcher=launcher) |
132 | + mock_get_provider.return_value = provider |
133 | + execute_run = launcher.return_value.execute_run |
134 | + execute_run.side_effect = run_side_effect |
135 | + config = dedent( |
136 | + """ |
137 | + pipeline: |
138 | + - test |
139 | + |
140 | + jobs: |
141 | + test: |
142 | + series: focal |
143 | + architectures: amd64 |
144 | + run: tox |
145 | + snaps: [chromium, firefox] |
146 | + """ |
147 | + ) |
148 | + Path(".launchpad.yaml").write_text(config) |
149 | + result = self.run_command("run") |
150 | + self.assertEqual(0, result.exit_code) |
151 | + self.assertEqual( |
152 | + [ |
153 | + call( |
154 | + [ |
155 | + "snap", |
156 | + "install", |
157 | + "chromium", |
158 | + "--channel", |
159 | + "latest/stable", |
160 | + ], |
161 | + check=True, |
162 | + capture_output=True, |
163 | + ), |
164 | + call( |
165 | + [ |
166 | + "curl", |
167 | + "--silent", |
168 | + "--unix-socket", |
169 | + "/run/snapd.socket", |
170 | + "http://localhost/v2/snaps/chromium", |
171 | + ], |
172 | + check=True, |
173 | + capture_output=True, |
174 | + ), |
175 | + call( |
176 | + [ |
177 | + "snap", |
178 | + "install", |
179 | + "firefox", |
180 | + "--channel", |
181 | + "latest/stable", |
182 | + ], |
183 | + check=True, |
184 | + capture_output=True, |
185 | + ), |
186 | + call( |
187 | + [ |
188 | + "curl", |
189 | + "--silent", |
190 | + "--unix-socket", |
191 | + "/run/snapd.socket", |
192 | + "http://localhost/v2/snaps/firefox", |
193 | + ], |
194 | + check=True, |
195 | + capture_output=True, |
196 | + ), |
197 | + call( |
198 | + ["bash", "--noprofile", "--norc", "-ec", "tox"], |
199 | + cwd=Path("/build/lpcraft/project"), |
200 | + env={}, |
201 | + stdout=ANY, |
202 | + stderr=ANY, |
203 | + ), |
204 | + ], |
205 | + execute_run.call_args_list, |
206 | + ) |
207 | + |
208 | + @responses.activate |
209 | + @patch("lpcraft.commands.run.get_provider") |
210 | + @patch("lpcraft.commands.run.get_host_architecture", return_value="amd64") |
211 | + def test_install_snaps_wrong_array_format( |
212 | + self, mock_get_host_architecture, mock_get_provider |
213 | + ): |
214 | + |
215 | + launcher = Mock(spec=launch) |
216 | + provider = makeLXDProvider(lxd_launcher=launcher) |
217 | + mock_get_provider.return_value = provider |
218 | + config = dedent( |
219 | + """ |
220 | + pipeline: |
221 | + - test |
222 | + |
223 | + jobs: |
224 | + test: |
225 | + series: focal |
226 | + architectures: amd64 |
227 | + run: tox |
228 | + snaps: [1, True, 3] |
229 | + """ |
230 | + ) |
231 | + Path(".launchpad.yaml").write_text(config) |
232 | + result = self.run_command("run") |
233 | + self.assertEqual(1, result.exit_code) |
234 | + self.assertRegex( |
235 | + str(result.errors[0]), |
236 | + "You configured a Snap, " |
237 | + + "but you used an unknown format. " |
238 | + + "Please refer to the documentation for an " |
239 | + + "overview of supported formats.", |
240 | + ) |
241 | + |
242 | + @responses.activate |
243 | + @patch("lpcraft.commands.run.get_provider") |
244 | + @patch("lpcraft.commands.run.get_host_architecture", return_value="amd64") |
245 | + def test_install_snaps_channel_parameter( |
246 | + self, mock_get_host_architecture, mock_get_provider |
247 | + ): |
248 | + def run_side_effect( |
249 | + command: List[str], **kwargs: Any |
250 | + ) -> "subprocess.CompletedProcess[bytes]": |
251 | + if command[0] == "curl": |
252 | + response = {"result": {"revision": "1"}, "status-code": 200} |
253 | + return subprocess.CompletedProcess( |
254 | + [], 0, stdout=json.dumps(response).encode() |
255 | + ) |
256 | + else: |
257 | + return subprocess.CompletedProcess([], 0) |
258 | + |
259 | + launcher = Mock(spec=launch) |
260 | + provider = makeLXDProvider(lxd_launcher=launcher) |
261 | + mock_get_provider.return_value = provider |
262 | + execute_run = launcher.return_value.execute_run |
263 | + execute_run.side_effect = run_side_effect |
264 | + config = dedent( |
265 | + """ |
266 | + pipeline: |
267 | + - test |
268 | + |
269 | + jobs: |
270 | + test: |
271 | + series: focal |
272 | + architectures: amd64 |
273 | + run: tox |
274 | + snaps: |
275 | + - name: black |
276 | + channel: 22/stable |
277 | + - firefox |
278 | + """ |
279 | + ) |
280 | + Path(".launchpad.yaml").write_text(config) |
281 | + |
282 | + result = self.run_command("run") |
283 | + |
284 | + self.assertEqual(0, result.exit_code) |
285 | + self.assertEqual( |
286 | + [ |
287 | + call( |
288 | + [ |
289 | + "snap", |
290 | + "install", |
291 | + "black", |
292 | + "--channel", |
293 | + "22/stable", |
294 | + ], |
295 | + check=True, |
296 | + capture_output=True, |
297 | + ), |
298 | + call( |
299 | + [ |
300 | + "curl", |
301 | + "--silent", |
302 | + "--unix-socket", |
303 | + "/run/snapd.socket", |
304 | + "http://localhost/v2/snaps/black", |
305 | + ], |
306 | + check=True, |
307 | + capture_output=True, |
308 | + ), |
309 | + call( |
310 | + [ |
311 | + "snap", |
312 | + "install", |
313 | + "firefox", |
314 | + "--channel", |
315 | + "latest/stable", |
316 | + ], |
317 | + check=True, |
318 | + capture_output=True, |
319 | + ), |
320 | + call( |
321 | + [ |
322 | + "curl", |
323 | + "--silent", |
324 | + "--unix-socket", |
325 | + "/run/snapd.socket", |
326 | + "http://localhost/v2/snaps/firefox", |
327 | + ], |
328 | + check=True, |
329 | + capture_output=True, |
330 | + ), |
331 | + call( |
332 | + ["bash", "--noprofile", "--norc", "-ec", "tox"], |
333 | + cwd=Path("/build/lpcraft/project"), |
334 | + env={}, |
335 | + stdout=ANY, |
336 | + stderr=ANY, |
337 | + ), |
338 | + ], |
339 | + execute_run.call_args_list, |
340 | + ) |
341 | + |
342 | + @responses.activate |
343 | + @patch("lpcraft.commands.run.get_provider") |
344 | + @patch("lpcraft.commands.run.get_host_architecture", return_value="amd64") |
345 | + def test_install_snaps_snap_name_missing( |
346 | + self, mock_get_host_architecture, mock_get_provider |
347 | + ): |
348 | + |
349 | + launcher = Mock(spec=launch) |
350 | + provider = makeLXDProvider(lxd_launcher=launcher) |
351 | + mock_get_provider.return_value = provider |
352 | + config = dedent( |
353 | + """ |
354 | + pipeline: |
355 | + - test |
356 | + |
357 | + jobs: |
358 | + test: |
359 | + series: focal |
360 | + architectures: amd64 |
361 | + run: tox |
362 | + snaps: |
363 | + - classic: True |
364 | + """ |
365 | + ) |
366 | + Path(".launchpad.yaml").write_text(config) |
367 | + |
368 | + result = self.run_command("run") |
369 | + |
370 | + self.assertEqual(1, result.exit_code) |
371 | + self.assertRegex( |
372 | + str(result.errors[0]), |
373 | + "You configured a Snap " + "but you did not specify a name.", |
374 | + ) |
375 | + |
376 | + @responses.activate |
377 | + @patch("lpcraft.commands.run.get_provider") |
378 | + @patch("lpcraft.commands.run.get_host_architecture", return_value="amd64") |
379 | + def test_install_snaps_snap_channel_none( |
380 | + self, mock_get_host_architecture, mock_get_provider |
381 | + ): |
382 | + |
383 | + launcher = Mock(spec=launch) |
384 | + provider = makeLXDProvider(lxd_launcher=launcher) |
385 | + mock_get_provider.return_value = provider |
386 | + config = dedent( |
387 | + """ |
388 | + pipeline: |
389 | + - test |
390 | + |
391 | + jobs: |
392 | + test: |
393 | + series: focal |
394 | + architectures: amd64 |
395 | + run: tox |
396 | + snaps: |
397 | + - name: chromium |
398 | + channel: |
399 | + """ |
400 | + ) |
401 | + Path(".launchpad.yaml").write_text(config) |
402 | + |
403 | + result = self.run_command("run") |
404 | + |
405 | + self.assertEqual(1, result.exit_code) |
406 | + self.assertRegex( |
407 | + str(result.errors[0]), |
408 | + "You configured a Snap `channel`, " |
409 | + + "but you did not specify a value.", |
410 | + ) |
411 | + |
412 | + @responses.activate |
413 | + @patch("lpcraft.commands.run.get_provider") |
414 | + @patch("lpcraft.commands.run.get_host_architecture", return_value="amd64") |
415 | + def test_install_snaps_snap_classic_none( |
416 | + self, mock_get_host_architecture, mock_get_provider |
417 | + ): |
418 | + |
419 | + launcher = Mock(spec=launch) |
420 | + provider = makeLXDProvider(lxd_launcher=launcher) |
421 | + mock_get_provider.return_value = provider |
422 | + config = dedent( |
423 | + """ |
424 | + pipeline: |
425 | + - test |
426 | + |
427 | + jobs: |
428 | + test: |
429 | + series: focal |
430 | + architectures: amd64 |
431 | + run: tox |
432 | + snaps: |
433 | + - name: chromium |
434 | + classic: |
435 | + """ |
436 | + ) |
437 | + Path(".launchpad.yaml").write_text(config) |
438 | + |
439 | + result = self.run_command("run") |
440 | + |
441 | + self.assertEqual(1, result.exit_code) |
442 | + self.assertRegex( |
443 | + str(result.errors[0]), |
444 | + "You configured a Snap `classic`, " |
445 | + + "but you did not specify a value. " |
446 | + + "Valid values would either be `True` or `False`.", |
447 | + ) |
448 | + |
449 | + @responses.activate |
450 | + @patch("lpcraft.commands.run.get_provider") |
451 | + @patch("lpcraft.commands.run.get_host_architecture", return_value="amd64") |
452 | + def test_install_snaps_classic_wrong_value( |
453 | + self, mock_get_host_architecture, mock_get_provider |
454 | + ): |
455 | + |
456 | + launcher = Mock(spec=launch) |
457 | + provider = makeLXDProvider(lxd_launcher=launcher) |
458 | + mock_get_provider.return_value = provider |
459 | + config = dedent( |
460 | + """ |
461 | + pipeline: |
462 | + - test |
463 | + |
464 | + jobs: |
465 | + test: |
466 | + series: focal |
467 | + architectures: amd64 |
468 | + run: tox |
469 | + snaps: |
470 | + - name: chromium |
471 | + classic: wrong_value |
472 | + """ |
473 | + ) |
474 | + Path(".launchpad.yaml").write_text(config) |
475 | + |
476 | + result = self.run_command("run") |
477 | + |
478 | + self.assertEqual(1, result.exit_code) |
479 | + self.assertRegex( |
480 | + str(result.errors[0]), |
481 | + "You configured a Snap `classic`, " |
482 | + + "but you did not specify a valid value. " |
483 | + + "Valid values would either be `True` or `False`.", |
484 | + ) |
485 | + |
486 | @patch("lpcraft.commands.run.get_provider") |
487 | @patch("lpcraft.commands.run.get_host_architecture", return_value="amd64") |
488 | def test_install_system_packages( |
489 | diff --git a/lpcraft/config.py b/lpcraft/config.py |
490 | index 5b8d126..0fdb9b4 100644 |
491 | --- a/lpcraft/config.py |
492 | +++ b/lpcraft/config.py |
493 | @@ -242,6 +242,33 @@ class PackageRepository(ModelConfigDefaults): |
494 | yield f"{format.value} {self.url!s} {suite.value} {' '.join(self.components)}" # noqa: E501 |
495 | |
496 | |
497 | +class Snap(ModelConfigDefaults): |
498 | + """A snap definition.""" |
499 | + |
500 | + name: StrictStr |
501 | + channel: Optional[StrictStr] = "latest/stable" |
502 | + classic: Optional[bool] = False |
503 | + |
504 | + @validator("channel") |
505 | + def prevent_channel_none(cls, v: StrictStr) -> Any: |
506 | + if v is None: |
507 | + raise ValueError( |
508 | + "You configured a Snap `channel`, " |
509 | + + "but you did not specify a value." |
510 | + ) |
511 | + return v |
512 | + |
513 | + @validator("classic") |
514 | + def prevent_classic_none(cls, v: bool) -> Any: |
515 | + if v is None: |
516 | + raise ValueError( |
517 | + "You configured a Snap `classic`, " |
518 | + + "but you did not specify a value. " |
519 | + + "Valid values would either be `True` or `False`." |
520 | + ) |
521 | + return v |
522 | + |
523 | + |
524 | class Job(ModelConfigDefaults): |
525 | """A job definition.""" |
526 | |
527 | @@ -257,7 +284,7 @@ class Job(ModelConfigDefaults): |
528 | environment: Optional[Dict[str, Optional[str]]] |
529 | output: Optional[Output] |
530 | input: Optional[Input] |
531 | - snaps: Optional[List[StrictStr]] |
532 | + snaps: Optional[List[Snap]] |
533 | packages: Optional[List[StrictStr]] |
534 | package_repositories: List[PackageRepository] = [] |
535 | plugin: Optional[StrictStr] |
536 | @@ -271,6 +298,36 @@ class Job(ModelConfigDefaults): |
537 | v = [v] |
538 | return v |
539 | |
540 | + @pydantic.validator("snaps", pre=True) |
541 | + def validate_snaps(cls, v: List[Any]) -> Any: |
542 | + clean_values = [] |
543 | + for value in v: |
544 | + # Backward compatibility, i.e. [chromium, firefox] |
545 | + if type(value) is str: |
546 | + clean_values.append({"name": value}) |
547 | + elif type(value) is dict: |
548 | + if "name" not in value or value["name"] is None: |
549 | + raise ValueError( |
550 | + "You configured a Snap " |
551 | + + "but you did not specify a name." |
552 | + ) |
553 | + if "classic" in value and value["classic"] is not None: |
554 | + if type(value["classic"]) is not bool: |
555 | + raise ValueError( |
556 | + "You configured a Snap `classic`, " |
557 | + + "but you did not specify a valid value. " |
558 | + + "Valid values would either be `True` or `False`." |
559 | + ) |
560 | + clean_values.append(value) |
561 | + else: |
562 | + raise ValueError( |
563 | + "You configured a Snap, " |
564 | + + "but you used an unknown format. " |
565 | + + "Please refer to the documentation for an " |
566 | + + "overview of supported formats." |
567 | + ) |
568 | + return clean_values |
569 | + |
570 | @pydantic.validator("package_repositories") |
571 | def validate_package_repositories( |
572 | cls, v: List[PackageRepository], values: Dict[StrictStr, Any] |
573 | diff --git a/lpcraft/plugin/lib.py b/lpcraft/plugin/lib.py |
574 | index 2b22680..92510e6 100644 |
575 | --- a/lpcraft/plugin/lib.py |
576 | +++ b/lpcraft/plugin/lib.py |
577 | @@ -10,7 +10,7 @@ The provided functionality can be extended by additional plugins. |
578 | |
579 | from __future__ import annotations |
580 | |
581 | -from lpcraft.config import Job |
582 | +from lpcraft.config import Job, Snap |
583 | from lpcraft.plugin import hookimpl |
584 | |
585 | |
586 | @@ -28,7 +28,7 @@ class InternalPlugins: |
587 | return [] |
588 | |
589 | @hookimpl # type: ignore |
590 | - def lpcraft_install_snaps(self) -> list[str]: |
591 | + def lpcraft_install_snaps(self) -> list[Snap]: |
592 | if self.config.snaps: |
593 | return self.config.snaps |
594 | return [] |
595 | diff --git a/lpcraft/tests/test_config.py b/lpcraft/tests/test_config.py |
596 | index d64533a..a5b875a 100644 |
597 | --- a/lpcraft/tests/test_config.py |
598 | +++ b/lpcraft/tests/test_config.py |
599 | @@ -26,6 +26,7 @@ from lpcraft.config import ( |
600 | PackageSuite, |
601 | PackageType, |
602 | PPAShortFormURL, |
603 | + Snap, |
604 | get_ppa_url_parts, |
605 | ) |
606 | from lpcraft.errors import CommandError |
607 | @@ -370,12 +371,18 @@ class TestConfig(TestCase): |
608 | test: |
609 | series: focal |
610 | architectures: amd64 |
611 | - snaps: [chromium, firefox] |
612 | + snaps: [name: chromium, name: firefox] |
613 | """ |
614 | ) |
615 | ) |
616 | config = Config.load(path) |
617 | - self.assertEqual(["chromium", "firefox"], config.jobs["test"][0].snaps) |
618 | + self.assertEqual( |
619 | + [ |
620 | + Snap(name="chromium", channel="latest/stable", classic=False), |
621 | + Snap(name="firefox", channel="latest/stable", classic=False), |
622 | + ], |
623 | + config.jobs["test"][0].snaps, |
624 | + ) |
625 | |
626 | def test_load_config_without_snaps(self): |
627 | path = self.create_config( |
Using a colon as a delimiter looks like an elegant way to encode information.
As mentioned below, is this an official way?
If not, we should have a quick discussion about alternative ways at the standup, e.g. having separate keys for name, channel and classic.
Anyway, please rewrite the way you parse the snap string.