Organizing files fails if equivalent file already exists
Metadata
Current evaluation
Organize collisions were fixed by PR #689 (commit c349e740), making organize_files() use part install directories instead of cwd-relative paths.
Resolution: closed resolved
Reason: Closed as likely fixed by PR #689, commit c349e740. The change corrects path resolution for organized files in non-default partitions, addressing the reported equivalent-file collision.
Evaluated by: mr-cal-bot
No scores available.
Issue body
### Bug Description
When a `part` defines an `organize:` which includes moving a file into a (not `default`) component partition, the organize will fail just in case the file already exists in that partition.
This behavior breaks from how that functionality works when not organizing into a component, where the collision of identical files is ignored.
This problem only seems to impact organizing into a partition which isn't `default`. To see this, remove the `organize:` from `my-part` in the below `snapcraft.yaml` and run `snapcraft build my-part --verbose` twice. It should not fail.
I would expect the behavior to match regardless of the name of the partition the file is being organized into.
### To Reproduce
Install snapcraft from a feature branch and create a boilerplate snap:
```
snap switch --channel=latest/edge/components snapcraft; snap refresh snapcraft
# Tested with revision 10677
mkdir test; cd test; snapcraft init
```
Create some simple files:
```
mkdir src/
touch src/default-file src/foo-file
```
Use the below `snapcraft.yaml` and run `snapcraft build my-part --verbose` twice.
To compare the behavior when not organizing into a component not named `default`, remove the `organize` section.
To compare the behavior when not using components, remove the `components:` section and the usage of `organize:`.
Both of these comparisons should succeed, while the provided example fails.
### part yaml
```shell
name: my-snap-name # you probably want to 'snapcraft register <name>'
base: core24 # the base snap is the execution environment for this snap
build-base: devel
version: '0.1' # just for humans, typically '1.2+git' or '1.3.2'
summary: Single-line elevator pitch for your amazing snap # 79 char long summary
description: |
This is my-snap's description. You have a paragraph or two to tell the
most important story about your snap. Keep it under 100 words though,
we live in tweetspace and your description wants to look good in the snap
store.
grade: devel # must be 'stable' to release into candidate/stable channels
confinement: devmode # use 'strict' once you have the right plugs and slots
parts:
my-part:
plugin: dump
source: src/
organize:
foo-file: (component/foo)/foo-file
components:
foo:
type: test
summary: test
description: test
```
### Relevant log output
```shell
dilyn@Ares:~/test -> snapcraft build my-part --verbose
Starting snapcraft
Logging execution to '/home/dilyn/.local/state/snapcraft/log/snapcraft-20240227-175558.077912.log'
Starting snapcraft
Logging execution to '/home/dilyn/.local/state/snapcraft/log/snapcraft-20240227-175558.077912.log'
deleting current features configuration
Running on amd64 for amd64
Running snapcraft with a devel instance is for testing purposes only.
Launching instance...
Creating instance from base instance
Starting instance \ (11.7s) Starting instance \ (14.1s) Starting instance
Starting snapcraft
Logging execution to '/root/.local/state/snapcraft/log/snapcraft-20240227-175622.766916.log'
Starting snapcraft
Logging execution to '/root/.local/state/snapcraft/log/snapcraft-20240227-175622.766916.log'
deleting current features configuration
Running on amd64 for amd64
Initializing parts lifecycle
Installing build-packages
Installing build-snaps
Pulling my-part
Building my-part
:: + cp --archive --link --no-dereference . /root/parts/my-part/install/default
dilyn@Ares:~/test -> snapcraft build my-part --verbose
Starting snapcraft
Logging execution to '/home/dilyn/.local/state/snapcraft/log/snapcraft-20240227-175642.418888.log'
Starting snapcraft
Logging execution to '/home/dilyn/.local/state/snapcraft/log/snapcraft-20240227-175642.418888.log'
deleting current features configuration
Running on amd64 for amd64
Running snapcraft with a devel instance is for testing purposes only.
Launching instance...
Starting instance
Starting snapcraft
Logging execution to '/root/.local/state/snapcraft/log/snapcraft-20240227-175653.254644.log'
Starting snapcraft
Logging execution to '/root/.local/state/snapcraft/log/snapcraft-20240227-175653.254644.log'
deleting current features configuration
Running on amd64 for amd64
Initializing parts lifecycle
Installing build-packages
Installing build-snaps
Skipping pull for my-part (already ran)
Rebuilding my-part (requested step)
:: + cp --archive --link --no-dereference . /root/parts/my-part/install/default
Failed to organize part 'my-part': trying to organize file 'foo-file' to '(component/foo)/foo-file', but '(component/foo)/foo-file' already exists.
Failed to execute build in instance.
Recommended resolution: Run the same command again with --debug to shell into the environment if you wish to introspect this failure.
Full execution log: '/home/dilyn/.local/state/snapcraft/log/snapcraft-20240227-175642.418888.log'
```
Evaluation history
| Date | Model | Scores | Action | Summary |
|---|---|---|---|---|
| stealth/space-bunny-alpha | — | closed resolved | Organize collisions were fixed by PR #689 (commit c349e740), making organize_files() use part install directories instead of cwd-relative paths. | |
| stealth/space-bunny-alpha | — | closed resolved | Closed as likely fixed by craft-parts#689 (c349e740): organize_files() now uses the part's install directories rather than cwd-relative paths. | |
| qwen/qwen3.6-35b-a3b | — | — | Closed as likely fixed. Commit c349e740 (PR #68) corrected path handling in organize_files(), resolving the duplicate file collision error that broke idempotent builds. Maintainers requested reopening if the issue persists. | |
| qwen/qwen3.6-35b-a3b |
Staleness:
25
Complexity:
45
Confidence:
90
Support Request:
5
|
keep open | Bug where snapcraft fails when organizing files to an existing location, breaking idempotent builds. Acknowledged by maintainers and on backlog, but unfixed. | |
| qwen3.6-35b-a3b-mtp-q6 |
Staleness:
40
Complexity:
45
Confidence:
85
Support Request:
10
|
keep open | Bug where snapcraft's organize step fails on duplicate files instead of being idempotent. Acknowledged by maintainers, on backlog, with recent user reports and discussion. |
Update history
| Date | Change |
|---|---|
| closed |
Related work
-
Likely Fixed By:
craft-parts#689
(confidence 99%)
Merged as commit c349e740 and cited by the closing maintainer comment.
Related issues
| Issue | Project | State | Summary | Similarity |
|---|---|---|---|---|
| #690 fix(organize): use the part's install directories (#689) | craft-parts | merged | Merged PR #690 in commit 0eff7cc9 (change c349e740), fixing organize_files() to use part install directories and organize under base_dir. | |
| #1366 Unhandled error when organizing a file to a directory where the file already exists | craft-parts | closed | Fixed by merged PR canonical/craft-parts#1370 (commit 7fe7c84e), which checks whether an organize destination exists before moving files. | |
| #1544 fix: prevent organizing a directory into itself | craft-parts | closed | PR #1544 was closed unmerged as incomplete; the solution was replaced by the organize-from-build rework in #1598 (commit cc02958b). | |
| #1007 Organize fails with an unhelpful error when attempting to overwrite a file | craft-parts | closed | Closed as stale without a direct fix; the organize-overwrite failure was later fixed by merged PR #1370 (commit 7fe7c84e), ensuring the destination does not exist before moving files. | |
| #1525 Directory traversal issue when organizing a part | craft-parts | closed | Fixed by merged PR #1562 (commit 32b95b4f), rejecting organize sources that escape the install directory via absolute paths or traversal. |