Repository: canonical/snapcraft

Issue:
Inconsistent results when building, cleaning stage and rebuilding
[Inconsistent results when building, cleaning stage and rebuilding](https://bugs.launchpad.net/snapcraft/+bug/1757093)
Inconsistent results when building, cleaning stage and rebuilding
[Inconsistent results when building, cleaning stage and rebuilding](https://bugs.launchpad.net/snapcraft/+bug/1757093)


Discussion:

Resolving patch:
diff --git a/snapcraft/internal/pluginhandler/__init__.py b/snapcraft/internal/pluginhandler/__init__.py
--- a/snapcraft/internal/pluginhandler/__init__.py
+++ b/snapcraft/internal/pluginhandler/__init__.py
@@ -382,6 +382,16 @@ def ignore(directory, files):
         self._runner.build()
         self._runner.install()
 
+        # Organize the installed files as requested. We do this in the build
+        # step for two reasons:
+        #
+        #   1. So cleaning and re-running the stage step works even if
+        #      `organize` is used
+        #   2. So collision detection takes organization into account, i.e. we
+        #      can use organization to get around file collisions between
+        #      parts when staging.
+        self._organize()
+
         self.mark_build_done()
 
     def mark_build_done(self):
@@ -479,7 +489,6 @@ def _organize(self):
     def stage(self, force=False):
         self.makedirs()
         self.notify_part_progress('Staging')
-        self._organize()
         snap_files, snap_dirs = self.migratable_fileset_for('stage')
 
         def fixup_func(file_path):


Test patch:
diff --git a/tests/integration/general/test_stage.py b/tests/integration/general/test_stage.py
--- a/tests/integration/general/test_stage.py
+++ b/tests/integration/general/test_stage.py
@@ -87,6 +87,23 @@ def test_conflicts(self):
         )
         self.assertThat(exception.output, Contains(expected_help))
 
+    def test_no_conflicts(self):
+        self.run_snapcraft(['stage'], 'organize-no-conflicts')
+        self.assertThat(os.path.join(self.stage_dir, 'file'), FileExists())
+        self.assertThat(os.path.join(self.stage_dir, 'file2'), FileExists())
+
+    def test_stage_twice(self):
+        """Test that snap that uses organize can be staged twice"""
+        self.run_snapcraft(['stage'], 'stage-twice')
+        self.assertThat(
+            os.path.join(self.stage_dir, 'dir', 'dir', 'file'), FileExists())
+
+        # Now clean, and stage again
+        self.run_snapcraft(['clean', '--step=stage'], 'stage-twice')
+        self.run_snapcraft(['stage'], 'stage-twice')
+        self.assertThat(
+            os.path.join(self.stage_dir, 'dir', 'dir', 'file'), FileExists())
+
     def test_classic_confinement(self):
         if os.environ.get('ADT_TEST') and self.deb_arch == 'armhf':
             self.skipTest("The autopkgtest armhf runners can't install snaps")
diff --git a/tests/integration/snaps/organize-no-conflicts/snap/snapcraft.yaml b/tests/integration/snaps/organize-no-conflicts/snap/snapcraft.yaml
new file mode 100644
--- /dev/null
+++ b/tests/integration/snaps/organize-no-conflicts/snap/snapcraft.yaml
@@ -0,0 +1,19 @@
+name: organize-no-conflicts
+version: '0.1' # just for humans, typically '1.2+git' or '1.3.2'
+summary: Test snapcraft's organize keyword
+description: |
+  No parts should conflict with each other unless `organize` is broken.
+
+grade: devel # must be 'stable' to release into candidate/stable channels
+confinement: devmode # use 'strict' once you have the right plugs and slots
+
+parts:
+  part1:
+    plugin: dump
+    source: src1/
+    organize:
+      file: file2
+
+  part2:
+    plugin: dump
+    source: src2/
diff --git a/tests/integration/snaps/organize-no-conflicts/src1/file b/tests/integration/snaps/organize-no-conflicts/src1/file
new file mode 100644
--- /dev/null
+++ b/tests/integration/snaps/organize-no-conflicts/src1/file
@@ -0,0 +1 @@
+1
diff --git a/tests/integration/snaps/organize-no-conflicts/src2/file b/tests/integration/snaps/organize-no-conflicts/src2/file
new file mode 100644
--- /dev/null
+++ b/tests/integration/snaps/organize-no-conflicts/src2/file
@@ -0,0 +1 @@
+2
diff --git a/tests/integration/snaps/stage-twice/dir/file b/tests/integration/snaps/stage-twice/dir/file
new file mode 100644
--- /dev/null
+++ b/tests/integration/snaps/stage-twice/dir/file
@@ -0,0 +1 @@
+The best little file you ever did see
diff --git a/tests/integration/snaps/stage-twice/snap/snapcraft.yaml b/tests/integration/snaps/stage-twice/snap/snapcraft.yaml
new file mode 100644
--- /dev/null
+++ b/tests/integration/snaps/stage-twice/snap/snapcraft.yaml
@@ -0,0 +1,16 @@
+name: stage-twice
+version: '0.1'
+summary: A snap which should be able to be staged twice
+description: |
+  Snaps that organize should be able to be staged, clean the stage, and staged
+  again.
+
+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-subdir: dir/
+    organize:
+      '*': dir/dir/