diff --git a/internal/archivefiles/archivefiles.go b/internal/archivefiles/archivefiles.go new file mode 100644 index 00000000000..84e61968bcf --- /dev/null +++ b/internal/archivefiles/archivefiles.go @@ -0,0 +1,74 @@ +// Package archivefiles can evaluate a list of config.Files into their final form. +package archivefiles + +import ( + "fmt" + "path/filepath" + "sort" + + "github.com/apex/log" + "github.com/goreleaser/fileglob" + "github.com/goreleaser/goreleaser/internal/tmpl" + "github.com/goreleaser/goreleaser/pkg/config" +) + +// Eval evaluates the given list of files to their final form. +func Eval(template *tmpl.Template, files []config.File) ([]config.File, error) { + var result []config.File + for _, f := range files { + replaced, err := template.Apply(f.Source) + if err != nil { + return result, fmt.Errorf("failed to apply template %s: %w", f.Source, err) + } + + files, err := fileglob.Glob(replaced) + if err != nil { + return result, fmt.Errorf("globbing failed for pattern %s: %w", f.Source, err) + } + + for _, file := range files { + result = append(result, config.File{ + Source: file, + Destination: destinationFor(f, file), + Info: f.Info, + }) + } + } + + sort.Slice(result, func(i, j int) bool { + return result[i].Destination < result[j].Destination + }) + + return unique(result), nil +} + +// remove duplicates +func unique(in []config.File) []config.File { + var result []config.File + exist := map[string]string{} + for _, f := range in { + if current := exist[f.Destination]; current != "" { + log.Warnf( + "file '%s' already exists in archive as '%s' - '%s' will be ignored", + f.Destination, + current, + f.Source, + ) + continue + } + exist[f.Destination] = f.Source + result = append(result, f) + } + + return result +} + +func destinationFor(f config.File, path string) string { + if f.Destination == "" { + return path + } + if f.StripParent { + return filepath.Join(f.Destination, filepath.Base(path)) + } + return filepath.Join(f.Destination, path) +} diff --git a/internal/archivefiles/archivefiles_test.go b/internal/archivefiles/archivefiles_test.go new file mode 100644 index 00000000000..a67cf4e68f6 --- /dev/null +++ b/internal/archivefiles/archivefiles_test.go @@ -0,0 +1,133 @@ +package archivefiles + +import ( + "testing" + "time" + + "github.com/goreleaser/goreleaser/internal/tmpl" + "github.com/goreleaser/goreleaser/pkg/config" + "github.com/goreleaser/goreleaser/pkg/context" + "github.com/stretchr/testify/require" +) + +func TestEval(t *testing.T) { + now := time.Now().Truncate(time.Second) + tmpl := tmpl.New(context.New(config.Project{})) + + t.Run("single file", func(t *testing.T) { + result, err := Eval(tmpl, []config.File{ + { + Source: "./testdata/**/d.txt", + Destination: "var/foobar/d.txt", + }, + }) + + require.NoError(t, err) + require.Equal(t, []config.File{ + { + Source: "testdata/a/b/c/d.txt", + Destination: "var/foobar/d.txt/testdata/a/b/c/d.txt", + }, + }, result) + }) + + t.Run("match multiple files within tree without destination", func(t *testing.T) { + result, err := Eval(tmpl, []config.File{{Source: "./testdata/a"}}) + + require.NoError(t, err) + require.Equal(t, []config.File{ + {Source: "testdata/a/a.txt", Destination: "testdata/a/a.txt"}, + {Source: "testdata/a/b/a.txt", Destination: "testdata/a/b/a.txt"}, + {Source: "testdata/a/b/c/d.txt", Destination: "testdata/a/b/c/d.txt"}, + }, result) + }) + + t.Run("match multiple files within tree specific destination", func(t *testing.T) { + result, err := Eval(tmpl, []config.File{ + { + Source: "./testdata/a", + Destination: "usr/local/test", + Info: config.FileInfo{ + Owner: "carlos", + Group: "users", + Mode: 0o755, + MTime: now, + }, + }, + }) + + require.NoError(t, err) + require.Equal(t, []config.File{ + { + Source: "testdata/a/a.txt", + Destination: "usr/local/test/testdata/a/a.txt", + Info: config.FileInfo{ + Owner: "carlos", + Group: "users", + Mode: 0o755, + MTime: now, + }, + }, + { + Source: "testdata/a/b/a.txt", + Destination: "usr/local/test/testdata/a/b/a.txt", + Info: config.FileInfo{ + Owner: "carlos", + Group: "users", + Mode: 0o755, + MTime: now, + }, + }, + { + Source: "testdata/a/b/c/d.txt", + Destination: "usr/local/test/testdata/a/b/c/d.txt", + Info: config.FileInfo{ + Owner: "carlos", + Group: "users", + Mode: 0o755, + MTime: now, + }, + }, + }, result) + }) + + t.Run("match multiple files within tree specific destination stripping parents", func(t *testing.T) { + result, err := Eval(tmpl, []config.File{ + { + Source: "./testdata/a", + Destination: "usr/local/test", + StripParent: true, + Info: config.FileInfo{ + Owner: "carlos", + Group: "users", + Mode: 0o755, + MTime: now, + }, + }, + }) + + require.NoError(t, err) + require.Equal(t, []config.File{ + { + Source: "testdata/a/a.txt", + Destination: "usr/local/test/a.txt", + Info: config.FileInfo{ + Owner: "carlos", + Group: "users", + Mode: 0o755, + MTime: now, + }, + }, + { + Source: "testdata/a/b/c/d.txt", + Destination: "usr/local/test/d.txt", + Info: config.FileInfo{ + Owner: "carlos", + Group: "users", + Mode: 0o755, + MTime: now, + }, + }, + }, result) + }) +} diff --git a/internal/pipe/archive/archive.go b/internal/pipe/archive/archive.go index d2297d58f16..566f4a21d70 100644 --- a/internal/pipe/archive/archive.go +++ b/internal/pipe/archive/archive.go @@ -8,12 +8,11 @@ import ( "fmt" "os" "path/filepath" - "sort" "strings" "sync" "github.com/apex/log" - "github.com/goreleaser/fileglob" + "github.com/goreleaser/goreleaser/internal/archivefiles" "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/internal/ids" "github.com/goreleaser/goreleaser/internal/semerrgroup" @@ -171,7 +170,7 @@ func doCreate(ctx *context.Context, arch config.Archive, binaries []*artifact.Ar a = NewEnhancedArchive(a, wrap) defer a.Close() - files, err := findFiles(template, arch.Files) + files, err := archivefiles.Eval(template, arch.Files) if err != nil { return fmt.Errorf("failed to find files to archive: %w", err) } @@ -262,66 +261,6 @@ func skip(ctx *context.Context, archive config.Archive, binaries []*artifact.Art return nil } -func findFiles(template *tmpl.Template, files []config.File) ([]config.File, error) { - var result []config.File - for _, f := range files { - replaced, err := template.Apply(f.Source) - if err != nil { - return result, fmt.Errorf("failed to apply template %s: %w", f.Source, err) - } - - files, err := fileglob.Glob(replaced) - if err != nil { - return result, fmt.Errorf("globbing failed for pattern %s: %w", f.Source, err) - } - - for _, file := range files { - result = append(result, config.File{ - Source: file, - Destination: destinationFor(f, file), - Info: f.Info, - }) - } - } - - sort.Slice(result, func(i, j int) bool { - return result[i].Destination < result[j].Destination - }) - - return unique(result), nil -} - -// remove duplicates -func unique(in []config.File) []config.File { - var result []config.File - exist := map[string]string{} - for _, f := range in { - if current := exist[f.Destination]; current != "" { - log.Warnf( - "file '%s' already exists in archive as '%s' - '%s' will be ignored", - f.Destination, - current, - f.Source, - ) - continue - } - exist[f.Destination] = f.Source - result = append(result, f) - } - - return result -} - -func destinationFor(f config.File, path string) string { - if f.Destination == "" { - return path - } - if f.StripParent { - return filepath.Join(f.Destination, filepath.Base(path)) - } - return filepath.Join(f.Destination, path) -} - func packageFormat(archive config.Archive, platform string) string { for _, override := range archive.FormatOverrides { if strings.HasPrefix(platform, override.Goos) { diff --git a/internal/pipe/archive/archive_test.go b/internal/pipe/archive/archive_test.go index dab315b7450..fa3685f176d 100644 --- a/internal/pipe/archive/archive_test.go +++ b/internal/pipe/archive/archive_test.go @@ -9,11 +9,9 @@ import ( "os" "path/filepath" "testing" - "time" "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/internal/testlib" - "github.com/goreleaser/goreleaser/internal/tmpl" "github.com/goreleaser/goreleaser/pkg/archive" "github.com/goreleaser/goreleaser/pkg/config" "github.com/goreleaser/goreleaser/pkg/context" @@ -953,128 +951,6 @@ func TestSeveralArchivesWithTheSameID(t *testing.T) { require.EqualError(t, Pipe{}.Default(ctx), "found 2 archives with the ID 'a', please fix your config") } -func TestFindFiles(t *testing.T) { - now := time.Now().Truncate(time.Second) - tmpl := tmpl.New(context.New(config.Project{})) - - t.Run("single file", func(t *testing.T) { - result, err := findFiles(tmpl, []config.File{ - { - Source: "./testdata/**/d.txt", - Destination: "var/foobar/d.txt", - }, - }) - - require.NoError(t, err) - require.Equal(t, []config.File{ - { - Source: "testdata/a/b/c/d.txt", - Destination: "var/foobar/d.txt/testdata/a/b/c/d.txt", - }, - }, result) - }) - - t.Run("match multiple files within tree without destination", func(t *testing.T) { - result, err := findFiles(tmpl, []config.File{{Source: "./testdata/a"}}) - - require.NoError(t, err) - require.Equal(t, []config.File{ - {Source: "testdata/a/a.txt", Destination: "testdata/a/a.txt"}, - {Source: "testdata/a/b/a.txt", Destination: "testdata/a/b/a.txt"}, - {Source: "testdata/a/b/c/d.txt", Destination: "testdata/a/b/c/d.txt"}, - }, result) - }) - - t.Run("match multiple files within tree specific destination", func(t *testing.T) { - result, err := findFiles(tmpl, []config.File{ - { - Source: "./testdata/a", - Destination: "usr/local/test", - Info: config.FileInfo{ - Owner: "carlos", - Group: "users", - Mode: 0o755, - MTime: now, - }, - }, - }) - - require.NoError(t, err) - require.Equal(t, []config.File{ - { - Source: "testdata/a/a.txt", - Destination: "usr/local/test/testdata/a/a.txt", - Info: config.FileInfo{ - Owner: "carlos", - Group: "users", - Mode: 0o755, - MTime: now, - }, - }, - { - Source: "testdata/a/b/a.txt", - Destination: "usr/local/test/testdata/a/b/a.txt", - Info: config.FileInfo{ - Owner: "carlos", - Group: "users", - Mode: 0o755, - MTime: now, - }, - }, - { - Source: "testdata/a/b/c/d.txt", - Destination: "usr/local/test/testdata/a/b/c/d.txt", - Info: config.FileInfo{ - Owner: "carlos", - Group: "users", - Mode: 0o755, - MTime: now, - }, - }, - }, result) - }) - - t.Run("match multiple files within tree specific destination stripping parents", func(t *testing.T) { - result, err := findFiles(tmpl, []config.File{ - { - Source: "./testdata/a", - Destination: "usr/local/test", - StripParent: true, - Info: config.FileInfo{ - Owner: "carlos", - Group: "users", - Mode: 0o755, - MTime: now, - }, - }, - }) - - require.NoError(t, err) - require.Equal(t, []config.File{ - { - Source: "testdata/a/a.txt", - Destination: "usr/local/test/a.txt", - Info: config.FileInfo{ - Owner: "carlos", - Group: "users", - Mode: 0o755, - MTime: now, - }, - }, - { - Source: "testdata/a/b/c/d.txt", - Destination: "usr/local/test/d.txt", - Info: config.FileInfo{ - Owner: "carlos", - Group: "users", - Mode: 0o755, - MTime: now, - }, - }, - }, result) - }) -} - func TestArchive_globbing(t *testing.T) { assertGlob := func(t *testing.T, files []config.File, expected []string) { t.Helper()