From 526fad1dd7a72f64ec112c1527f27497127a0058 Mon Sep 17 00:00:00 2001 From: Sergio Schvezov Date: Fri, 1 Sep 2023 16:59:04 -0300 Subject: [PATCH] fix: reprime for try This solves the case where prime was run before try, where a subsequent run of try would mask the contents of the existing prime directory with a newly mounted one from the host. In legacy we had the concept of `clean --unprime` to solve this. Signed-off-by: Sergio Schvezov --- snapcraft/parts/lifecycle.py | 3 ++ snapcraft/parts/parts.py | 14 +++++++++ tests/spread/core22/try/task.yaml | 5 ++- tests/unit/parts/test_lifecycle.py | 50 +++++++++++++++++++++++++++--- 4 files changed, 66 insertions(+), 6 deletions(-) diff --git a/snapcraft/parts/lifecycle.py b/snapcraft/parts/lifecycle.py index c096963238..4c52e4ab1c 100644 --- a/snapcraft/parts/lifecycle.py +++ b/snapcraft/parts/lifecycle.py @@ -346,6 +346,9 @@ def _run_lifecycle_and_pack( step_name, shell=getattr(parsed_args, "shell", False), shell_after=getattr(parsed_args, "shell_after", False), + # Repriming needs to happen to take into account any changes to + # the actual target directory. + rerun_step=command_name == "try", ) # Extract metadata and generate snap.yaml diff --git a/snapcraft/parts/parts.py b/snapcraft/parts/parts.py index be2d95072b..e0c81bbdc5 100644 --- a/snapcraft/parts/parts.py +++ b/snapcraft/parts/parts.py @@ -141,10 +141,14 @@ def run( *, shell: bool = False, shell_after: bool = False, + rerun_step: bool = False, ) -> None: """Run the parts lifecycle. :param target_step: The final step to execute. + :param shell: Enter a shell instead of running step_name. + :param shell: Enter a shell after running step_name. + :param rerun_step: Force running step_name. :raises PartsLifecycleError: On error during lifecycle. :raises RuntimeError: On unexpected error. @@ -171,6 +175,16 @@ def run( with self._lcm.action_executor() as aex: for action in actions: + # Workaround until canonical/craft-parts#540 is fixed + if action.step == target_step and rerun_step: + action = craft_parts.Action( + part_name=action.part_name, + step=action.step, + action_type=ActionType.RERUN, + reason="forced rerun", + project_vars=action.project_vars, + properties=action.properties, + ) message = _action_message(action) emit.progress(f"Executing parts lifecycle: {message}") with emit.open_stream("Executing action") as stream: diff --git a/tests/spread/core22/try/task.yaml b/tests/spread/core22/try/task.yaml index c2e9bb2418..5dff360e1c 100644 --- a/tests/spread/core22/try/task.yaml +++ b/tests/spread/core22/try/task.yaml @@ -7,6 +7,9 @@ execute: | chmod a+w prime unset SNAPCRAFT_BUILD_ENVIRONMENT + # Prime first to regression test snapcore/snapcraft#4219 + snapcraft prime --use-lxd + # Followed by the actual try snapcraft try --use-lxd find prime/meta/snap.yaml @@ -14,4 +17,4 @@ execute: | snap try prime hello-try | MATCH "Hello, world" - snap remove hello-try \ No newline at end of file + snap remove hello-try diff --git a/tests/unit/parts/test_lifecycle.py b/tests/unit/parts/test_lifecycle.py index 369dbf88b3..f41a629d98 100644 --- a/tests/unit/parts/test_lifecycle.py +++ b/tests/unit/parts/test_lifecycle.py @@ -299,13 +299,45 @@ def test_lifecycle_run_command_step( parsed_args=parsed_args, ) - call_args = {"shell": False, "shell_after": False} + call_args = {"shell": False, "shell_after": False, "rerun_step": False} if debug_shell: call_args[debug_shell] = True assert run_mock.mock_calls == [call(step, **call_args)] +def test_lifecycle_run_try_command(snapcraft_yaml, project_vars, new_dir, mocker): + project = Project.unmarshal(snapcraft_yaml(base="core22")) + run_mock = mocker.patch("snapcraft.parts.PartsLifecycle.run") + mocker.patch("snapcraft.meta.snap_yaml.write") + mocker.patch("snapcraft.pack.pack_snap") + + parsed_args = argparse.Namespace( + debug=False, + destructive_mode=True, + enable_manifest=False, + shell=False, + shell_after=False, + use_lxd=False, + ua_token=None, + parts=[], + ) + + parts_lifecycle._run_command( + "try", + project=project, + parse_info={}, + assets_dir=Path(), + start_time=datetime.now(), + parallel_build_count=8, + parsed_args=parsed_args, + ) + + assert run_mock.mock_calls == [ + call("prime", shell=False, shell_after=False, rerun_step=True) + ] + + @pytest.mark.parametrize("cmd", ["pack", "snap"]) def test_lifecycle_run_command_pack(cmd, snapcraft_yaml, project_vars, new_dir, mocker): project = Project.unmarshal(snapcraft_yaml(base="core22")) @@ -333,7 +365,9 @@ def test_lifecycle_run_command_pack(cmd, snapcraft_yaml, project_vars, new_dir, ), ) - assert run_mock.mock_calls == [call("prime", shell=False, shell_after=False)] + assert run_mock.mock_calls == [ + call("prime", shell=False, shell_after=False, rerun_step=False) + ] assert pack_mock.mock_calls[:1] == [ call( new_dir / "prime", @@ -382,7 +416,9 @@ def test_lifecycle_pack_destructive_mode( ) assert run_in_provider_mock.mock_calls == [] - assert run_mock.mock_calls == [call("prime", shell=False, shell_after=False)] + assert run_mock.mock_calls == [ + call("prime", shell=False, shell_after=False, rerun_step=False) + ] assert pack_mock.mock_calls[:1] == [ call( new_dir / "home/prime", @@ -432,7 +468,9 @@ def test_lifecycle_pack_managed(cmd, snapcraft_yaml, project_vars, new_dir, mock ) assert run_in_provider_mock.mock_calls == [] - assert run_mock.mock_calls == [call("prime", shell=False, shell_after=False)] + assert run_mock.mock_calls == [ + call("prime", shell=False, shell_after=False, rerun_step=False) + ] assert pack_mock.mock_calls[:1] == [ call( new_dir / "home/prime", @@ -525,7 +563,9 @@ def test_lifecycle_pack_metadata_error(cmd, snapcraft_yaml, new_dir, mocker): assert str(raised.value) == ( "error setting grade: unexpected value; permitted: 'stable', 'devel'" ) - assert run_mock.mock_calls == [call("prime", shell=False, shell_after=False)] + assert run_mock.mock_calls == [ + call("prime", shell=False, shell_after=False, rerun_step=False) + ] assert pack_mock.mock_calls == []