← Back to issue list

The prime step uses the install dir when creating a list of migrateable filesets

View original Github issue

Metadata

Project
craft-parts
Number
#766
Type
issue
State
open
Author
mr-cal
Labels
Type: Bug
Created
Updated
Closed
—

Current evaluation

Prime can fail after stage exclusions because its fileset is computed from install rather than staged content; the proposed fix is blocked by undefined overlay-cleaning semantics.

Suggested action: keep open

Reason: The current prime implementation still computes its fileset from part_install_dir while copying from stage_dir, matching the reported CopyFileNotFound failure. A maintainer confirmed that switching to stage_dir changes overlay-cleaning behavior and breaks overlay integration tests, so the valid bug remains open pending definition and testing of overlay semantics.

Evaluated by: mr-cal-bot

Impact: 58 Quick Win: 10 Complexity: 76 Confidence: 96 Actionability: 70

Issue body

### Bug Description craft-parts will fail if a `stage` and `prime` are defined for a part and `stage` removes a file. This can be fixed by changing [this line](https://github.com/canonical/craft-parts/blob/cdcc9dbc76e5fcf352a9744f1d1c74cfab6c15e5/craft_parts/executor/step_handler.py#L243): ```diff diff --git a/craft_parts/executor/step_handler.py b/craft_parts/executor/step_handler.py index c88b409..29e6ef8 100644 --- a/craft_parts/executor/step_handler.py +++ b/craft_parts/executor/step_handler.py @@ -240,7 +240,7 @@ class StepHandler: else: files, dirs = filesets.migratable_filesets( - prime_fileset, str(self._part.part_install_dir) + prime_fileset, str(self._part.stage_dir) ) files, dirs = migrate_files( files=files, ``` I don't know if this is on purpose, but it has been like this since it was first added to craft-parts [here](https://github.com/canonical/craft-parts/pull/59/files#diff-8b05be16f7434251a31499a62b63d4f9f2962efd02f3896a9308cbc3ab034dcfR157). source: https://github.com/canonical/snapcraft/issues/4835 from @dilyn-corner ### To Reproduce 1. Create the filetree: ``` └── src    └── foo    └── bar    ├── baz    ├── buzz    └── fizz ``` 2. Create and run the python script: ```python import logging import yaml import craft_parts from craft_parts import LifecycleManager, Step logging.basicConfig(level=logging.DEBUG) parts_yaml = """ parts: hello: plugin: dump source: src/ stage: - -foo/bar/baz prime: - foo/bar """ parts = yaml.safe_load(parts_yaml) lcm = LifecycleManager( parts, application_name="example", cache_dir=".", ) with lcm.action_executor() as aex: aex.execute(lcm.plan(Step.PRIME)) ``` ### part yaml _No response_ ### Relevant log output ```shell DEBUG:craft_parts.utils.os_utils:is_snap: False, SNAP_NAME set to None DEBUG:craft_parts.infos:Setting target machine to x86_64 DEBUG:craft_parts.sources.local_source:ignore patterns: [] INFO:craft_parts.executor.executor:Installing build-packages DEBUG:craft_parts.executor.executor:verify plugin environment for part 'hello' DEBUG:craft_parts.sequencer:process hello:1 DEBUG:craft_parts.sequencer:add action hello:1(0) DEBUG:craft_parts.sequencer:process hello:3 DEBUG:craft_parts.sequencer:add action hello:3(0) DEBUG:craft_parts.sequencer:process hello:4 DEBUG:craft_parts.sequencer:add action hello:4(0) DEBUG:craft_parts.sequencer:process hello:5 DEBUG:craft_parts.sequencer:add action hello:5(0) DEBUG:craft_parts.executor.executor:execute action hello:Action(part_name='hello', step=Step.PULL, action_type=ActionType.RUN, reason=None, project_vars=None, properties=ActionProperties(changed_files=None, changed_dirs=None)) DEBUG:craft_parts.executor.executor:execute action hello:Action(part_name='hello', step=Step.BUILD, action_type=ActionType.RUN, reason=None, project_vars=None, properties=ActionProperties(changed_files=None, changed_dirs=None)) DEBUG:craft_parts.state_manager.states:load state file: /home/developer/dev/craft-parts-examples/prime-fileset-from-stage-dir/parts/hello/state/pull DEBUG:craft_parts.executor.part_handler:remove directory /home/developer/dev/craft-parts-examples/prime-fileset-from-stage-dir/parts/hello/build DEBUG:craft_parts.executor.step_handler:Executing PosixPath('/home/developer/dev/craft-parts-examples/prime-fileset-from-stage-dir/parts/hello/run/build.sh') + cp --archive --link --no-dereference . /home/developer/dev/craft-parts-examples/prime-fileset-from-stage-dir/parts/hello/install DEBUG:urllib3.connectionpool:http://localhost:None "GET /v2/snaps HTTP/1.1" 200 None DEBUG:craft_parts.executor.executor:execute action hello:Action(part_name='hello', step=Step.STAGE, action_type=ActionType.RUN, reason=None, project_vars=None, properties=ActionProperties(changed_files=None, changed_dirs=None)) DEBUG:craft_parts.executor.executor:execute action hello:Action(part_name='hello', step=Step.PRIME, action_type=ActionType.RUN, reason=None, project_vars=None, properties=ActionProperties(changed_files=None, changed_dirs=None)) Traceback (most recent call last): File "/home/developer/dev/craft-parts/craft_parts/utils/file_utils.py", line 141, in link os.link(source_path, destination, follow_symlinks=False) FileNotFoundError: [Errno 2] No such file or directory: '/home/developer/dev/craft-parts-examples/prime-fileset-from-stage-dir/stage/foo/bar/baz' -> '/home/developer/dev/craft-parts-examples/prime-fileset-from-stage-dir/prime/foo/bar/baz' The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/home/developer/dev/craft-parts-examples/prime-fileset-from-stage-dir/main.py", line 29, in <module> aex.execute(lcm.plan(Step.PRIME)) File "/home/developer/dev/craft-parts/craft_parts/executor/executor.py", line 327, in execute self._executor.execute(actions, stdout=stdout, stderr=stderr) File "/home/developer/dev/craft-parts/craft_parts/executor/executor.py", line 137, in execute self._run_action(act, stdout=stdout, stderr=stderr) File "/home/developer/dev/craft-parts/craft_parts/executor/executor.py", line 214, in _run_action handler.run_action(action, stdout=stdout, stderr=stderr) File "/home/developer/dev/craft-parts/craft_parts/executor/part_handler.py", line 170, in run_action state = handler(step_info, stdout=stdout, stderr=stderr) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/developer/dev/craft-parts/craft_parts/executor/part_handler.py", line 407, in _run_prime contents = self._run_step( ^^^^^^^^^^^^^^^ File "/home/developer/dev/craft-parts/craft_parts/executor/part_handler.py", line 491, in _run_step return step_handler.run_builtin() ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/developer/dev/craft-parts/craft_parts/executor/step_handler.py", line 108, in run_builtin return handler() ^^^^^^^^^ File "/home/developer/dev/craft-parts/craft_parts/executor/step_handler.py", line 245, in _builtin_prime files, dirs = migrate_files( ^^^^^^^^^^^^^^ File "/home/developer/dev/craft-parts/craft_parts/executor/migration.py", line 123, in migrate_files file_utils.link_or_copy( File "/home/developer/dev/craft-parts/craft_parts/utils/file_utils.py", line 98, in link_or_copy link(source, destination, follow_symlinks=follow_symlinks) File "/home/developer/dev/craft-parts/craft_parts/utils/file_utils.py", line 143, in link raise errors.CopyFileNotFound(source) from err craft_parts.errors.CopyFileNotFound: Failed to copy '/home/developer/dev/craft-parts-examples/prime-fileset-from-stage-dir/stage/foo/bar/baz': no such file or directory. ```

Evaluation history

Date Model Scores Action Summary
stealth/space-bunny-alpha
Impact: 58
Quick Win: 10
Complexity: 76
Confidence: 96
Actionability: 70
keep open Prime can fail after stage exclusions because its fileset is computed from install rather than staged content; the proposed fix is blocked by undefined overlay-cleaning semantics.
qwen/qwen3.6-35b-a3b
Impact: 60
Quick Win: 10
Complexity: 60
Confidence: 70
Actionability: 40
keep open Prime step fails when stage excludes files, using install dir instead of stage dir for migratable filesets. Blocked on overlay cleaning documentation.
qwen/qwen3.8-27b
Impact: 50
Quick Win: 14
Staleness: 58
Complexity: 72
Confidence: 82
Support Request: 0
keep open craft-parts prime step computes migratable filesets from the install dir instead of the stage dir, failing when stage removes a file prime includes. A one-line fix was proposed but breaks overlay cleaning tests; blocked on defining overlay cleaning semantics.
qwen/qwen3.6-35b-a3b
Staleness: 75
Complexity: 85
Confidence: 85
Support Request: 0
keep open Bug where prime step incorrectly uses install dir instead of stage dir for fileset migration, causing failures when stage removes files. Currently blocked on defining overlay cleaning behavior after a proposed fix broke integration tests.
qwen3.6-35b-a3b-mtp-q6
Staleness: 75
Complexity: 80
Confidence: 85
Support Request: 10
keep open Bug where prime step incorrectly uses install dir for migratable filesets, causing failures when stage removes files. Currently blocked by maintainer pending documentation of overlay cleaning behavior.

Update history

No update history recorded yet.

Related work

  • Related To: canonical/snapcraft#4835 (confidence 100%)

    Explicitly cited as the source of the report.

  • Related To: canonical/craft-parts#59 (confidence 98%)

    The pull request that originally introduced the install-dir-based fileset calculation.

Related issues

No related issues found above the similarity threshold.