diff --git a/kyaml/filesys/fsnode.go b/kyaml/filesys/fsnode.go index 58bfe72bd7..983815fa90 100644 --- a/kyaml/filesys/fsnode.go +++ b/kyaml/filesys/fsnode.go @@ -612,6 +612,7 @@ func (n *fsNode) RegExpGlob(pattern string) ([]string, error) { // This is how /bin/ls behaves. func (n *fsNode) Glob(pattern string) ([]string, error) { var result []string + var allFiles []string err := n.WalkMe(func(path string, info os.FileInfo, err error) error { if err != nil { return err @@ -622,7 +623,7 @@ func (n *fsNode) Glob(pattern string) ([]string, error) { return err } if match { - result = append(result, path) + allFiles = append(allFiles, path) } } return nil @@ -630,6 +631,11 @@ func (n *fsNode) Glob(pattern string) ([]string, error) { if err != nil { return nil, err } + if IsHiddenFilePath(pattern) { + result = allFiles + } else { + result = RemoveHiddenFiles(allFiles) + } sort.Strings(result) return result, nil } diff --git a/kyaml/filesys/fsnode_test.go b/kyaml/filesys/fsnode_test.go index 00b0c2ca6e..10186c98cf 100644 --- a/kyaml/filesys/fsnode_test.go +++ b/kyaml/filesys/fsnode_test.go @@ -463,6 +463,13 @@ var bunchOfFiles = []struct { { path: filepath.Join("b", "d", "a", "c", "u"), }, + { + path: filepath.Join("b", "d", ".hidden_file"), + }, + { + path: filepath.Join("b", "d", ".hidden_dir"), + addAsDir: true, + }, } func makeLoadedFileTree(t *testing.T) *fsNode { @@ -579,6 +586,7 @@ func TestExists(t *testing.T) { func TestRegExpGlob(t *testing.T) { n := makeLoadedFileTree(t) expected := []string{ + filepath.Join("b", "d", ".hidden_file"), filepath.Join("b", "d", "a", "c", "i", "beans"), filepath.Join("b", "d", "a", "c", "m"), filepath.Join("b", "d", "a", "c", "u"), @@ -598,16 +606,36 @@ func TestRegExpGlob(t *testing.T) { func TestGlob(t *testing.T) { n := makeLoadedFileTree(t) - expected := []string{ - filepath.Join("b", "d", "x"), - filepath.Join("b", "d", "y"), - filepath.Join("b", "d", "z"), + + tests := map[string]struct { + globPattern string + expectedFiles []string + }{ + "VisibleFiles": { + globPattern: "b/d/*", + expectedFiles: []string{ + filepath.Join("b", "d", "x"), + filepath.Join("b", "d", "y"), + filepath.Join("b", "d", "z"), + }, + }, + "HiddenFiles": { + globPattern: "b/d/.*", + expectedFiles: []string{ + filepath.Join("b", "d", ".hidden_file"), + }, + }, } - paths, err := n.Glob("b/d/*") - if err != nil { - t.Fatalf("glob error: %v", err) + + for test, c := range tests { + t.Run(test, func(t *testing.T) { + paths, err := n.Glob(c.globPattern) + if err != nil { + t.Fatalf("glob error: %v", err) + } + assertEqualStringSlices(t, c.expectedFiles, paths, "glob test") + }) } - assertEqualStringSlices(t, expected, paths, "glob test") } func assertEqualStringSlices(t *testing.T, expected, actual []string, message string) { diff --git a/kyaml/filesys/fsondisk.go b/kyaml/filesys/fsondisk.go index d5c07f381c..7fd02a255a 100644 --- a/kyaml/filesys/fsondisk.go +++ b/kyaml/filesys/fsondisk.go @@ -88,7 +88,17 @@ func (fsOnDisk) Exists(name string) bool { // Glob returns the list of matching files func (fsOnDisk) Glob(pattern string) ([]string, error) { - return filepath.Glob(pattern) + var result []string + allFilePaths, err := filepath.Glob(pattern) + if err != nil { + return nil, err + } + if IsHiddenFilePath(pattern) { + result = allFilePaths + } else { + result = RemoveHiddenFiles(allFilePaths) + } + return result, nil } // IsDir delegates to os.Stat and FileInfo.IsDir diff --git a/kyaml/filesys/fsondisk_test.go b/kyaml/filesys/fsondisk_test.go index 22c930c08b..d00baf1806 100644 --- a/kyaml/filesys/fsondisk_test.go +++ b/kyaml/filesys/fsondisk_test.go @@ -11,6 +11,7 @@ import ( "path" "path/filepath" "reflect" + "sort" "testing" ) @@ -135,31 +136,113 @@ func TestReadFilesRealFS(t *testing.T) { fSys, testDir := makeTestDir(t) defer os.RemoveAll(testDir) - err := fSys.WriteFile(path.Join(testDir, "foo"), []byte(`foo`)) + dir := path.Join(testDir, "dir") + nestedDir := path.Join(dir, "nestedDir") + hiddenDir := path.Join(testDir, ".hiddenDir") + dirs := []string{ + testDir, + dir, + nestedDir, + hiddenDir, + } + // all directories will have all these files + files := []string{ + "bar", + "foo", + "file-1.xtn", + ".file-2.xtn", + ".some-file-3.xtn", + ".some-file-4.xtn", + } + + err := fSys.MkdirAll(nestedDir) if err != nil { - t.Fatalf("unexpected error %s", err) - } - if !fSys.Exists(path.Join(testDir, "foo")) { - t.Fatalf("expected foo") - } - if fSys.IsDir(path.Join(testDir, "foo")) { - t.Fatalf("expected foo not to be a directory") - } - - err = fSys.WriteFile(path.Join(testDir, "bar"), []byte(`bar`)) - if err != nil { - t.Fatalf("unexpected error %s", err) - } - - files, err := fSys.Glob(path.Join("testDir", "*")) - expected := []string{ - path.Join(testDir, "bar"), - path.Join(testDir, "foo"), + t.Fatalf("Unexpected Error %v\n", err) } + err = fSys.MkdirAll(hiddenDir) if err != nil { - t.Fatalf("expected no error") - } - if reflect.DeepEqual(files, expected) { - t.Fatalf("incorrect files found by glob: %v", files) + t.Fatalf("Unexpected Error %v\n", err) + } + + // adding all files in every directory that we had defined + for _, d := range dirs { + if !fSys.IsDir(d) { + t.Fatalf("Expected %s to be a dir\n", d) + } + for _, f := range files { + err = fSys.WriteFile(path.Join(d, f), []byte(f)) + if err != nil { + t.Fatalf("unexpected error %s", err) + } + if !fSys.Exists(path.Join(d, f)) { + t.Fatalf("expected %s", f) + } + } + } + + tests := map[string]struct { + globPattern string + expectedFiles []string + expectedDirs map[string][]string // glob returns directories as well, so we need to add those to expected files + }{ + "AllVisibleFiles": { + globPattern: "*", + expectedFiles: []string{ + "bar", + "foo", + "file-1.xtn", + }, + expectedDirs: map[string][]string{ + testDir: []string{dir}, + dir: []string{nestedDir}, + }, + }, + "AllHiddenFiles": { + globPattern: ".*", + expectedFiles: []string{ + ".file-2.xtn", + ".some-file-3.xtn", + ".some-file-4.xtn", + }, + expectedDirs: map[string][]string{ + testDir: []string{hiddenDir}, + }, + }, + "foo_File": { + globPattern: "foo", + expectedFiles: []string{ + "foo", + }, + }, + "dotsome-file_PrefixedFiles": { + globPattern: ".some-file*", + expectedFiles: []string{ + ".some-file-3.xtn", + ".some-file-4.xtn", + }, + }, + } + + for n, c := range tests { + t.Run(n, func(t *testing.T) { + for _, d := range dirs { + var expectedPaths []string + for _, f := range c.expectedFiles { + expectedPaths = append(expectedPaths, path.Join(d, f)) + } + if c.expectedDirs != nil { + expectedPaths = append(expectedPaths, c.expectedDirs[d]...) + } + actualPaths, globErr := fSys.Glob(path.Join(d, c.globPattern)) + if globErr != nil { + t.Fatalf("Unexpected Error : %v\n", globErr) + } + sort.Strings(actualPaths) + sort.Strings(expectedPaths) + if !reflect.DeepEqual(actualPaths, expectedPaths) { + t.Fatalf("incorrect files found by glob: expected=%v, actual=%v", expectedPaths, actualPaths) + } + } + }) } } diff --git a/kyaml/filesys/util.go b/kyaml/filesys/util.go index 0ccb291324..4d8958a2bc 100644 --- a/kyaml/filesys/util.go +++ b/kyaml/filesys/util.go @@ -123,3 +123,21 @@ func InsertPathPart(path string, pos int, part string) string { result[pos] = part return PathJoin(append(result, parts[pos:]...)) } + +func IsHiddenFilePath(pattern string) bool { + return strings.HasPrefix(filepath.Base(pattern), ".") +} + +// Removes paths containing hidden files/folders from a list of paths +func RemoveHiddenFiles(paths []string) []string { + if len(paths) == 0 { + return paths + } + var result []string + for _, path := range paths { + if !IsHiddenFilePath(path) { + result = append(result, path) + } + } + return result +} diff --git a/kyaml/filesys/util_test.go b/kyaml/filesys/util_test.go index 3c510bf1f2..266444c113 100644 --- a/kyaml/filesys/util_test.go +++ b/kyaml/filesys/util_test.go @@ -8,6 +8,7 @@ package filesys import ( "os" "path/filepath" + "reflect" "testing" ) @@ -375,3 +376,90 @@ func TestStripLeadingSeps(t *testing.T) { } } } + +func TestIsHiddenFilePath(t *testing.T) { + tests := map[string]struct { + paths []string + expectHidden bool + }{ + "hiddenGlobs": { + expectHidden: true, + paths: []string{ + ".*", + "/.*", + "dir/.*", + "dir1/dir2/dir3/.*", + "../../.*", + "../../dir/.*", + }, + }, + "visibleGlobes": { + expectHidden: false, + paths: []string{ + "*", + "/*", + "dir/*", + "dir1/dir2/dir3/*", + "../../*", + "../../dir/*", + }, + }, + "hiddenFiles": { + expectHidden: true, + paths: []string{ + ".root_file.xtn", + "/.file_1.xtn", + "dir/.file_2.xtn", + "dir1/dir2/dir3/.file_3.xtn", + "../../.file_4.xtn", + "../../dir/.file_5.xtn", + }, + }, + "visibleFiles": { + expectHidden: false, + paths: []string{ + "root_file.xtn", + "/file_1.xtn", + "dir/file_2.xtn", + "dir1/dir2/dir3/file_3.xtn", + "../../file_4.xtn", + "../../dir/file_5.xtn", + }, + }, + } + for n, c := range tests { + t.Run(n, func(t *testing.T) { + for _, path := range c.paths { + actual := IsHiddenFilePath(path) + if actual != c.expectHidden { + t.Fatalf("For file path %q, expected hidden: %v, got hidden: %v", path, c.expectHidden, actual) + } + } + }) + } +} + +func TestRemoveHiddenFiles(t *testing.T) { + paths := []string{ + "file1.xtn", + ".file2.xtn", + "dir/fa1", + "dir/fa2", + "dir/.fa3", + "../../.fa4", + "../../fa5", + "../../dir/fa6", + "../../dir/.fa7", + } + result := RemoveHiddenFiles(paths) + expected := []string{ + "file1.xtn", + "dir/fa1", + "dir/fa2", + "../../fa5", + "../../dir/fa6", + } + if !reflect.DeepEqual(result, expected) { + t.Fatalf("Hidden dirs not correctly removed, expected %v but got %v\n", expected, result) + } +}