From e159ed339db39dd0f405d820ac3b1b2fb4a1edb9 Mon Sep 17 00:00:00 2001 From: Arpit Jain Date: Thu, 9 Jul 2026 04:53:43 +0900 Subject: [PATCH] fix: skip sub-directories when copying a plugin directory CopyDirToDir is documented to copy only the top-level regular files of a directory, but its WalkDir callback names its path parameter "path", which shadows nothing useful and makes the skip check d.Name() != filepath.Base(path) always false (both sides resolve to the current entry's base name). As a result the walk descended into every sub-directory and copied nested files, flattening them into the destination. Compare the walked path against the source root instead, matching the pattern already used in parsePluginFromDir. Add a regression test. Signed-off-by: Arpit Jain --- internal/file/file.go | 6 +++--- internal/file/file_test.go | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/internal/file/file.go b/internal/file/file.go index 464db1cc..9dfcd12e 100644 --- a/internal/file/file.go +++ b/internal/file/file.go @@ -86,12 +86,12 @@ func CopyDirToDir(src, dst string) error { if !fi.Mode().IsDir() { return ErrNotDirectory } - return filepath.WalkDir(src, func(path string, d fs.DirEntry, err error) error { + return filepath.WalkDir(src, func(filePath string, d fs.DirEntry, err error) error { if err != nil { return err } // skip sub-directories - if d.IsDir() && d.Name() != filepath.Base(path) { + if d.IsDir() && filePath != src { return fs.SkipDir } info, err := d.Info() @@ -100,7 +100,7 @@ func CopyDirToDir(src, dst string) error { } // only copy regular files if info.Mode().IsRegular() { - return CopyToDir(path, dst) + return CopyToDir(filePath, dst) } return nil }) diff --git a/internal/file/file_test.go b/internal/file/file_test.go index aeeea399..f85b2468 100644 --- a/internal/file/file_test.go +++ b/internal/file/file_test.go @@ -171,6 +171,49 @@ func TestCopyToDir(t *testing.T) { }) } +func TestCopyDirToDir(t *testing.T) { + t.Run("only top-level regular files are copied", func(t *testing.T) { + srcDir := t.TempDir() + destDir := t.TempDir() + + // top-level regular file + if err := os.WriteFile(filepath.Join(srcDir, "top.txt"), []byte("top"), 0600); err != nil { + t.Fatal(err) + } + // a file nested in a sub-directory that must NOT be copied + subDir := filepath.Join(srcDir, "sub") + if err := os.MkdirAll(subDir, 0700); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(subDir, "nested.txt"), []byte("nested"), 0600); err != nil { + t.Fatal(err) + } + + if err := CopyDirToDir(srcDir, destDir); err != nil { + t.Fatal(err) + } + + if _, err := os.Stat(filepath.Join(destDir, "top.txt")); err != nil { + t.Fatalf("expected top-level file to be copied, but got %v", err) + } + if _, err := os.Stat(filepath.Join(destDir, "nested.txt")); !os.IsNotExist(err) { + t.Fatal("expected sub-directory file to be skipped, but it was copied") + } + }) + + t.Run("source is not a directory", func(t *testing.T) { + srcDir := t.TempDir() + destDir := t.TempDir() + filename := filepath.Join(srcDir, "file.txt") + if err := os.WriteFile(filename, []byte("data"), 0600); err != nil { + t.Fatal(err) + } + if err := CopyDirToDir(filename, destDir); err != ErrNotDirectory { + t.Fatalf("expected ErrNotDirectory, but got %v", err) + } + }) +} + func TestFileNameWithoutExtension(t *testing.T) { input := "testfile.tar.gz" expectedOutput := "testfile.tar"