Repository: spack/spack

Issue:
When creating filesystem views, skip hidden spack folder literally, not as a regex
Not sure how to add a test for this, but basically we currently skip files matching `.spack` as a regex, which includes `bin/spack` when there is a spack package in spack. This PR makes sure we match '.spack' literally.



Discussion:

Resolving patch:
diff --git a/lib/spack/spack/directory_layout.py b/lib/spack/spack/directory_layout.py
--- a/lib/spack/spack/directory_layout.py
+++ b/lib/spack/spack/directory_layout.py
@@ -102,7 +102,7 @@ def __init__(self, root, **kwargs):
 
     @property
     def hidden_file_regexes(self):
-        return (re.escape(self.metadata_dir),)
+        return ("^{0}$".format(re.escape(self.metadata_dir)),)
 
     def relative_path_for_spec(self, spec):
         _check_concrete(spec)
diff --git a/var/spack/repos/builtin.mock/packages/view-not-ignored/package.py b/var/spack/repos/builtin.mock/packages/view-not-ignored/package.py
new file mode 100644
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/view-not-ignored/package.py
@@ -0,0 +1,46 @@
+# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+
+import os.path
+
+from spack.package import *
+
+
+class ViewNotIgnored(Package):
+    """Install files that should not be ignored by spack."""
+
+    homepage = "http://www.spack.org"
+    url = "http://www.spack.org/downloads/aml-1.0.tar.gz"
+    has_code = False
+
+    version("0.1.0", sha256="cc89a8768693f1f11539378b21cdca9f0ce3fc5cb564f9b3e4154a051dcea69b")
+
+    install_test_files = [
+        "foo.spack",
+        ".spack.bar",
+        "aspack",
+        "bin/foo.spack",
+        "bin/.spack.bar",
+        "bin/aspack",
+    ]
+
+    def install(self, spec, prefix):
+        for test_file in self.install_test_files:
+            path = os.path.join(prefix, test_file)
+            mkdirp(os.path.dirname(path))
+            with open(path, "w") as f:
+                f.write(test_file)
+
+    @classmethod
+    def assert_installed(cls, prefix):
+        for test_file in cls.install_test_files:
+            path = os.path.join(prefix, test_file)
+            assert os.path.exists(path), "Missing installed file: {}".format(path)
+
+    @classmethod
+    def assert_not_installed(cls, prefix):
+        for test_file in cls.install_test_files:
+            path = os.path.join(prefix, test_file)
+            assert not os.path.exists(path), "File was not uninstalled: {}".format(path)


Test patch:
diff --git a/lib/spack/spack/test/cmd/view.py b/lib/spack/spack/test/cmd/view.py
--- a/lib/spack/spack/test/cmd/view.py
+++ b/lib/spack/spack/test/cmd/view.py
@@ -10,6 +10,7 @@
 
 import spack.util.spack_yaml as s_yaml
 from spack.main import SpackCommand
+from spack.spec import Spec
 
 activate = SpackCommand("activate")
 extensions = SpackCommand("extensions")
@@ -261,3 +262,34 @@ def test_view_fails_with_missing_projections_file(tmpdir):
     projection_file = os.path.join(str(tmpdir), "nonexistent")
     with pytest.raises(SystemExit):
         view("symlink", "--projection-file", projection_file, viewpath, "foo")
+
+
+@pytest.mark.parametrize("with_projection", [False, True])
+@pytest.mark.parametrize("cmd", ["symlink", "copy"])
+def test_view_files_not_ignored(
+    tmpdir, mock_packages, mock_archive, mock_fetch, config, install_mockery, cmd, with_projection
+):
+    spec = Spec("view-not-ignored").concretized()
+    pkg = spec.package
+    pkg.do_install()
+    pkg.assert_installed(spec.prefix)
+
+    install("view-dir-file")  # Arbitrary package to add noise
+
+    viewpath = str(tmpdir.mkdir("view_{0}".format(cmd)))
+
+    if with_projection:
+        proj = str(tmpdir.join("proj.yaml"))
+        with open(proj, "w") as f:
+            f.write('{"projections":{"all":"{name}"}}')
+        prefix_in_view = os.path.join(viewpath, "view-not-ignored")
+        args = ["--projection-file", proj]
+    else:
+        prefix_in_view = viewpath
+        args = []
+
+    view(cmd, *(args + [viewpath, "view-not-ignored", "view-dir-file"]))
+    pkg.assert_installed(prefix_in_view)
+
+    view("remove", viewpath, "view-not-ignored")
+    pkg.assert_not_installed(prefix_in_view)