diff --git a/files/files.go b/files/files.go index e837a4c4..064dc1b4 100644 --- a/files/files.go +++ b/files/files.go @@ -8,7 +8,6 @@ import ( "strings" "time" - "github.com/goreleaser/fileglob" "github.com/goreleaser/nfpm/v2/internal/glob" ) @@ -143,11 +142,6 @@ func (c *Content) Sys() interface{} { // ExpandContentGlobs gathers all of the real files to be copied into the package. func ExpandContentGlobs(contents Contents, disableGlobbing bool) (files Contents, err error) { - options := []fileglob.OptFunc{fileglob.MatchDirectoryIncludesContents} - if disableGlobbing { - options = append(options, fileglob.QuoteMeta) - } - for _, f := range contents { var globbed map[string]string @@ -157,7 +151,7 @@ func ExpandContentGlobs(contents Contents, disableGlobbing bool) (files Contents // them because they do not really exist files = append(files, f.WithFileInfoDefaults()) default: - globbed, err = glob.Glob(f.Source, f.Destination, options...) + globbed, err = glob.Glob(f.Source, f.Destination, disableGlobbing) if err != nil { return nil, err } diff --git a/files/files_test.go b/files/files_test.go index 4ba7c24b..288924f6 100644 --- a/files/files_test.go +++ b/files/files_test.go @@ -2,6 +2,7 @@ package files_test import ( "os" + "strconv" "strings" "sync" "testing" @@ -198,3 +199,52 @@ func TestCollision(t *testing.T) { require.ErrorIs(t, err, files.ErrContentCollision) }) } + +func TestDisableGlobbing(t *testing.T) { + testCases := []files.Content{ + { + Source: "testdata/{test}/bar", + Destination: "/etc/{test}/bar", + }, + { + Source: "testdata/{test}/[f]oo", + Destination: "testdata/{test}/[f]oo", + }, + { + Source: "testdata/globtest/a.txt", + Destination: "testdata/globtest/a.txt", + }, + { + Source: "testdata/globtest/a.txt", + Destination: "/etc/a.txt", + }, + } + + disableGlobbing := true + + for i, testCase := range testCases { + content := testCase + + t.Run(strconv.Itoa(i), func(t *testing.T) { + result, err := files.ExpandContentGlobs(files.Contents{&content}, disableGlobbing) + if err != nil { + t.Fatalf("expand content globs: %v", err) + } + + if len(result) != 1 { + t.Fatalf("unexpected result length: %d, expected one", len(result)) + } + + actualContent := result[0] + + // we expect the result content to be identical to the input content + if actualContent.Source != content.Source { + t.Fatalf("unexpected content source: %q, expected %q", actualContent.Source, content.Source) + } + + if actualContent.Destination != content.Destination { + t.Fatalf("unexpected content destination: %q, expected %q", actualContent.Destination, content.Destination) + } + }) + } +} diff --git a/files/testdata/{test}/[f]oo b/files/testdata/{test}/[f]oo new file mode 100644 index 00000000..e69de29b diff --git a/files/testdata/{test}/bar b/files/testdata/{test}/bar new file mode 100644 index 00000000..e69de29b diff --git a/internal/glob/glob.go b/internal/glob/glob.go index 56317572..5bc24979 100644 --- a/internal/glob/glob.go +++ b/internal/glob/glob.go @@ -51,7 +51,12 @@ func (e ErrGlobNoMatch) Error() string { // Glob returns a map with source file path as keys and destination as values. // First the longest common prefix (lcp) of all globbed files is found. The destination // for each globbed file is then dst joined with src with the lcp trimmed off. -func Glob(pattern, dst string, options ...fileglob.OptFunc) (map[string]string, error) { +func Glob(pattern, dst string, ignoreMatchers bool) (map[string]string, error) { + options := []fileglob.OptFunc{fileglob.MatchDirectoryIncludesContents} + if ignoreMatchers { + options = append(options, fileglob.QuoteMeta) + } + if strings.HasPrefix(pattern, "../") { p, err := filepath.Abs(pattern) if err != nil { @@ -59,21 +64,24 @@ func Glob(pattern, dst string, options ...fileglob.OptFunc) (map[string]string, } pattern = filepath.ToSlash(p) } + matches, err := fileglob.Glob(pattern, append(options, fileglob.MaybeRootFS)...) - if errors.Is(err, os.ErrNotExist) { - return nil, err - } if err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil, err + } + return nil, fmt.Errorf("glob failed: %s: %w", pattern, err) } if len(matches) == 0 { return nil, ErrGlobNoMatch{pattern} } + files := make(map[string]string) prefix := pattern // the prefix may not be a complete path or may use glob patterns, in that case use the parent directory - if _, err := os.Stat(prefix); os.IsNotExist(err) || fileglob.ContainsMatchers(pattern) { + if _, err := os.Stat(prefix); os.IsNotExist(err) || (fileglob.ContainsMatchers(pattern) && !ignoreMatchers) { prefix = filepath.Dir(longestCommonPrefix(matches)) } @@ -82,13 +90,16 @@ func Glob(pattern, dst string, options ...fileglob.OptFunc) (map[string]string, if f, err := os.Stat(src); err == nil && f.Mode().IsDir() { continue } + relpath, err := filepath.Rel(prefix, src) if err != nil { // since prefix is a prefix of src a relative path should always be found - panic(err) + return nil, err } + globdst := filepath.ToSlash(filepath.Join(dst, relpath)) files[src] = globdst } + return files, nil } diff --git a/internal/glob/glob_test.go b/internal/glob/glob_test.go index abfca684..721b3c88 100644 --- a/internal/glob/glob_test.go +++ b/internal/glob/glob_test.go @@ -40,7 +40,7 @@ func TestLongestCommonPrefix(t *testing.T) { func TestGlob(t *testing.T) { t.Run("simple", func(t *testing.T) { - files, err := Glob("./testdata/dir_a/dir_*/*", "/foo/bar") + files, err := Glob("./testdata/dir_a/dir_*/*", "/foo/bar", false) require.NoError(t, err) require.Len(t, files, 2) require.Equal(t, "/foo/bar/dir_b/test_b.txt", files["testdata/dir_a/dir_b/test_b.txt"]) @@ -51,47 +51,47 @@ func TestGlob(t *testing.T) { pattern := "../../testdata/fake" abs, err := filepath.Abs(pattern) require.NoError(t, err) - files, err := Glob(pattern, "/foo/fake") + files, err := Glob(pattern, "/foo/fake", false) require.NoError(t, err) require.Len(t, files, 1) require.Equal(t, "/foo/fake", files[filepath.ToSlash(abs)]) }) t.Run("single file", func(t *testing.T) { - files, err := Glob("testdata/dir_a/dir_b/*", "/foo/bar") + files, err := Glob("testdata/dir_a/dir_b/*", "/foo/bar", false) require.NoError(t, err) require.Len(t, files, 1) require.Equal(t, "/foo/bar/test_b.txt", files["testdata/dir_a/dir_b/test_b.txt"]) }) t.Run("double star", func(t *testing.T) { - files, err := Glob("testdata/**/test*.txt", "/foo/bar") + files, err := Glob("testdata/**/test*.txt", "/foo/bar", false) require.NoError(t, err) require.Len(t, files, 3) require.Equal(t, "/foo/bar/dir_a/dir_b/test_b.txt", files["testdata/dir_a/dir_b/test_b.txt"]) }) t.Run("nil value", func(t *testing.T) { - files, err := Glob("does/not/exist", "/foo/bar") + files, err := Glob("does/not/exist", "/foo/bar", false) require.EqualError(t, err, "matching \"./does/not/exist\": file does not exist") require.Nil(t, files) }) t.Run("no matches", func(t *testing.T) { - files, err := Glob("testdata/nothing*", "/foo/bar") + files, err := Glob("testdata/nothing*", "/foo/bar", false) require.Nil(t, files) require.EqualError(t, err, "glob failed: testdata/nothing*: no matching files") }) t.Run("escaped brace", func(t *testing.T) { - files, err := Glob("testdata/\\{dir_d\\}/*", "/foo/bar") + files, err := Glob("testdata/\\{dir_d\\}/*", "/foo/bar", false) require.NoError(t, err) require.Len(t, files, 1) require.Equal(t, "/foo/bar/test_brace.txt", files["testdata/{dir_d}/test_brace.txt"]) }) t.Run("no glob", func(t *testing.T) { - files, err := Glob("testdata/dir_a/dir_b/test_b.txt", "/foo/bar/dest.dat") + files, err := Glob("testdata/dir_a/dir_b/test_b.txt", "/foo/bar/dest.dat", false) require.NoError(t, err) require.Len(t, files, 1) require.Equal(t, "/foo/bar/dest.dat", files["testdata/dir_a/dir_b/test_b.txt"])