Repository: spack/spack

Issue:
'spack mirror create -a' doesn't download patches
### Steps to reproduce

Apply this patch to make the process faster

```diff
diff --git a/lib/spack/spack/package_base.py b/lib/spack/spack/package_base.py
index ff29d7c..93407f5 100644
--- a/lib/spack/spack/package_base.py
+++ b/lib/spack/spack/package_base.py
@@ -1032,6 +1032,9 @@ def _make_stage(self):
         if dev_path_var:
             return DIYStage(dev_path_var.value)
         
+        if self.spec.name != 'eigen':
+            return StageComposite()
+
         # To fetch the current version
         source_stage = self._make_root_stage(self.fetcher)
```

Run

```console
spack mirror create -d /tmp/foo -a
tree /tmp/foo > log1.txt 
```

Only the eigen tarballs will be present.

Now run this:

```console
spack mirror create -d /tmp/foo eigen@3.2.10  
tree /tmp/foo > log2.txt  
```

```
diff -Naur log1.txt log2.txt
```

shows:

```diff
--- log1.txt	2023-11-14 19:08:52.998588114 -0800
+++ log2.txt	2023-11-14 19:09:11.022263828 -0800
@@ -1,5 +1,6 @@
 /tmp/foo
 ├── eigen
+│   ├── 001a57519a7aa909d3bf0cd8c6ec8a9cd19d9c70.diff-55daee8 -> ../_source-cache/archive/55/55daee880b7669807efc0dcbeda2ae3b659e6dd4df3932f3573c8778bf5f8a42
 │   ├── eigen-3.2.10.tar.gz -> ../_source-cache/archive/09/0920cb60ec38de5fb509650014eff7cc6d26a097c7b38c7db4b1aa5df5c85042.tar.gz
 │   ├── eigen-3.2.5.tar.gz -> ../_source-cache/archive/80/8068bd528a2ff3885eb55225c27237cf5cda834355599f05c2c85345db8338b4.tar.gz
 │   ├── eigen-3.2.6.tar.gz -> ../_source-cache/archive/e0/e097b8dcc5ad30d40af4ad72d7052e3f78639469baf83cffaadc045459cda21f.tar.gz
@@ -29,6 +30,8 @@
         │   └── 383407ab3d0c268074e97a2cbba84ac197fd24532f014aa2adc522355c1aa2d0.tar.gz
         ├── 50
         │   └── 50dd21a8997fce0857b27a126811ae8ee7619984ab5425ecf33510cec649e642.tar.gz
+        ├── 55
+        │   └── 55daee880b7669807efc0dcbeda2ae3b659e6dd4df3932f3573c8778bf5f8a42
         ├── 64
         │   └── 64c54781cfe9eefef2792003ab04b271d4b2ec32eda6e9cdf120d7aad4ebb282.tar.gz
         ├── 79
@@ -54,4 +57,4 @@
         └── fd
             └── fd72694390bd8e81586205717d2cf823e718f584b779a155db747d1e68481a2e.tar.gz
 
-21 directories, 34 files
+22 directories, 36 files
```

I'd expect no difference if the first spack mirror command downloaded all patches like it did in 0.20

### Error message

No error - it just doesn't produce the expected result.

### Information on your system

```
* **Spack:** 0.21.0
* **Python:** 3.11.5
* **Platform:** linux-manjaro23-skylake
* **Concretizer:** clingo
```

### General information

- [X] I have run `spack debug report` and reported the version of Spack/Python/Platform
- [X] I have searched the issues of this repo and believe this is not a duplicate
- [X] I have run the failing commands in debug mode and reported the output


Discussion:

Resolving patch:
diff --git a/lib/spack/spack/package_base.py b/lib/spack/spack/package_base.py
--- a/lib/spack/spack/package_base.py
+++ b/lib/spack/spack/package_base.py
@@ -1035,15 +1035,26 @@ def _make_stage(self):
         # To fetch the current version
         source_stage = self._make_root_stage(self.fetcher)
 
-        # Extend it with all resources and patches
+        # all_stages is source + resources + patches
         all_stages = StageComposite()
         all_stages.append(source_stage)
         all_stages.extend(
             self._make_resource_stage(source_stage, r) for r in self._get_needed_resources()
         )
-        all_stages.extend(
-            p.stage for p in self.spec.patches if isinstance(p, spack.patch.UrlPatch)
-        )
+        if self.spec.concrete:
+            all_stages.extend(
+                p.stage for p in self.spec.patches if isinstance(p, spack.patch.UrlPatch)
+            )
+        else:
+            # The only code path that gets here is spack mirror create --all which just needs all
+            # matching patches.
+            all_stages.extend(
+                p.stage
+                for when_spec, patch_list in self.patches.items()
+                if self.spec.intersects(when_spec)
+                for p in patch_list
+                if isinstance(p, spack.patch.UrlPatch)
+            )
         return all_stages
 
     @property
@@ -1743,28 +1754,16 @@ def _if_ninja_target_execute(self, target, *args, **kwargs):
             inspect.getmodule(self).ninja(target, *args, **kwargs)
 
     def _get_needed_resources(self):
-        resources = []
-        # Select the resources that are needed for this build
-        if self.spec.concrete:
-            for when_spec, resource_list in self.resources.items():
-                if when_spec in self.spec:
-                    resources.extend(resource_list)
-        else:
-            for when_spec, resource_list in self.resources.items():
-                # Note that variant checking is always strict for specs where
-                # the name is not specified. But with strict variant checking,
-                # only variants mentioned in 'other' are checked. Here we only
-                # want to make sure that no constraints in when_spec
-                # conflict with the spec, so we need to invoke
-                # when_spec.satisfies(self.spec) vs.
-                # self.spec.satisfies(when_spec)
-                if when_spec.intersects(self.spec):
-                    resources.extend(resource_list)
-        # Sorts the resources by the length of the string representing their
-        # destination. Since any nested resource must contain another
-        # resource's name in its path, it seems that should work
-        resources = sorted(resources, key=lambda res: len(res.destination))
-        return resources
+        # We use intersects here cause it would also work if self.spec is abstract
+        resources = [
+            resource
+            for when_spec, resource_list in self.resources.items()
+            if self.spec.intersects(when_spec)
+            for resource in resource_list
+        ]
+        # Sorts the resources by the length of the string representing their destination. Since any
+        # nested resource must contain another resource's path, that should work
+        return sorted(resources, key=lambda res: len(res.destination))
 
     def _resource_stage(self, resource):
         pieces = ["resource", resource.name, self.spec.dag_hash()]


Test patch: