From e38e76cac81b2716ded293d5f407fd784d40995f Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Tue, 10 May 2022 09:17:30 -0300 Subject: [PATCH] feat: meta archives (#3093) adds support for creating meta archives closes #3085 Signed-off-by: Carlos A Becker --- internal/pipe/archive/archive.go | 48 ++++++++++----- internal/pipe/archive/archive_meta_test.go | 71 ++++++++++++++++++++++ pkg/config/config.go | 1 + www/docs/customization/archive.md | 6 ++ 4 files changed, 110 insertions(+), 16 deletions(-) create mode 100644 internal/pipe/archive/archive_meta_test.go diff --git a/internal/pipe/archive/archive.go b/internal/pipe/archive/archive.go index f6fda422079..096eb05bb35 100644 --- a/internal/pipe/archive/archive.go +++ b/internal/pipe/archive/archive.go @@ -84,6 +84,10 @@ func (Pipe) Run(ctx *context.Context) error { g := semerrgroup.New(ctx.Parallelism) for i, archive := range ctx.Config.Archives { archive := archive + if archive.Meta { + return createMeta(ctx, archive) + } + filter := []artifact.Filter{artifact.Or( artifact.ByType(artifact.Binary), artifact.ByType(artifact.UniversalBinary), @@ -120,11 +124,18 @@ func checkArtifacts(artifacts map[string][]*artifact.Artifact) error { return ErrArchiveDifferentBinaryCount } +func createMeta(ctx *context.Context, arch config.Archive) error { + return doCreate(ctx, arch, nil, arch.Format, tmpl.New(ctx)) +} + func create(ctx *context.Context, arch config.Archive, binaries []*artifact.Artifact) error { + template := tmpl.New(ctx).WithArtifact(binaries[0], arch.Replacements) format := packageFormat(arch, binaries[0].Goos) - folder, err := tmpl.New(ctx). - WithArtifact(binaries[0], arch.Replacements). - Apply(arch.NameTemplate) + return doCreate(ctx, arch, binaries, format, template) +} + +func doCreate(ctx *context.Context, arch config.Archive, binaries []*artifact.Artifact, format string, template *tmpl.Template) error { + folder, err := template.Apply(arch.NameTemplate) if err != nil { return err } @@ -149,8 +160,6 @@ func create(ctx *context.Context, arch config.Archive, binaries []*artifact.Arti log := log.WithField("archive", archivePath) log.Info("creating") - template := tmpl.New(ctx). - WithArtifact(binaries[0], arch.Replacements) wrap, err := template.Apply(wrapFolder(arch)) if err != nil { return err @@ -163,6 +172,9 @@ func create(ctx *context.Context, arch config.Archive, binaries []*artifact.Arti if err != nil { return fmt.Errorf("failed to find files to archive: %w", err) } + if arch.Meta && len(files) == 0 { + return fmt.Errorf("no files found") + } for _, f := range files { if err = a.Add(f); err != nil { return fmt.Errorf("failed to add: '%s' -> '%s': %w", f.Source, f.Destination, err) @@ -178,24 +190,28 @@ func create(ctx *context.Context, arch config.Archive, binaries []*artifact.Arti } bins = append(bins, binary.Name) } - ctx.Artifacts.Add(&artifact.Artifact{ - Type: artifact.UploadableArchive, - Name: folder + "." + format, - Path: archivePath, - Goos: binaries[0].Goos, - Goarch: binaries[0].Goarch, - Goarm: binaries[0].Goarm, - Gomips: binaries[0].Gomips, - Goamd64: binaries[0].Goamd64, + art := &artifact.Artifact{ + Type: artifact.UploadableArchive, + Name: folder + "." + format, + Path: archivePath, Extra: map[string]interface{}{ artifact.ExtraBuilds: binaries, artifact.ExtraID: arch.ID, artifact.ExtraFormat: arch.Format, artifact.ExtraWrappedIn: wrap, artifact.ExtraBinaries: bins, - artifact.ExtraReplaces: binaries[0].Extra[artifact.ExtraReplaces], }, - }) + } + if len(binaries) > 0 { + art.Goos = binaries[0].Goos + art.Goarch = binaries[0].Goarch + art.Goarm = binaries[0].Goarm + art.Gomips = binaries[0].Gomips + art.Goamd64 = binaries[0].Goamd64 + art.Extra[artifact.ExtraReplaces] = binaries[0].Extra[artifact.ExtraReplaces] + } + + ctx.Artifacts.Add(art) return nil } diff --git a/internal/pipe/archive/archive_meta_test.go b/internal/pipe/archive/archive_meta_test.go new file mode 100644 index 00000000000..652da355481 --- /dev/null +++ b/internal/pipe/archive/archive_meta_test.go @@ -0,0 +1,71 @@ +package archive + +import ( + "path/filepath" + "testing" + + "github.com/goreleaser/goreleaser/pkg/config" + "github.com/goreleaser/goreleaser/pkg/context" + "github.com/stretchr/testify/require" +) + +func TestMeta(t *testing.T) { + t.Run("good", func(t *testing.T) { + dist := t.TempDir() + ctx := context.New(config.Project{ + Dist: dist, + Archives: []config.Archive{ + { + Meta: true, + NameTemplate: "foo", + Files: []config.File{ + {Source: "testdata/**/*.txt"}, + }, + }, + }, + }) + + require.NoError(t, Pipe{}.Default(ctx)) + require.NoError(t, Pipe{}.Run(ctx)) + require.Equal( + t, + []string{"testdata/a/a.txt", "testdata/a/b/a.txt", "testdata/a/b/c/d.txt"}, + tarFiles(t, filepath.Join(dist, "foo.tar.gz")), + ) + }) + + t.Run("bad tmpl", func(t *testing.T) { + dist := t.TempDir() + ctx := context.New(config.Project{ + Dist: dist, + Archives: []config.Archive{ + { + Meta: true, + NameTemplate: "foo{{.Os}}", + Files: []config.File{ + {Source: "testdata/**/*.txt"}, + }, + }, + }, + }) + + require.NoError(t, Pipe{}.Default(ctx)) + require.EqualError(t, Pipe{}.Run(ctx), `template: tmpl:1:5: executing "tmpl" at <.Os>: map has no entry for key "Os"`) + }) + + t.Run("no files", func(t *testing.T) { + dist := t.TempDir() + ctx := context.New(config.Project{ + Dist: dist, + Archives: []config.Archive{ + { + Meta: true, + NameTemplate: "foo", + }, + }, + }) + + require.NoError(t, Pipe{}.Default(ctx)) + require.EqualError(t, Pipe{}.Run(ctx), `no files found`) + }) +} diff --git a/pkg/config/config.go b/pkg/config/config.go index 82b1a3ee87d..4663575dc45 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -516,6 +516,7 @@ type Archive struct { FormatOverrides []FormatOverride `yaml:"format_overrides,omitempty"` WrapInDirectory string `yaml:"wrap_in_directory,omitempty"` Files []File `yaml:"files,omitempty"` + Meta bool `yaml:"meta,omitempty"` AllowDifferentBinaryCount bool `yaml:"allow_different_binary_count,omitempty"` } diff --git a/www/docs/customization/archive.md b/www/docs/customization/archive.md index 13d219bcd50..53700effec7 100644 --- a/www/docs/customization/archive.md +++ b/www/docs/customization/archive.md @@ -15,6 +15,7 @@ archives: id: my-archive # Builds reference which build instances should be archived in this archive. + # Default is empty, which includes all builds. builds: - default @@ -24,6 +25,11 @@ archives: # Default is `tar.gz`. format: zip + # This will create an archive without any binaries, only the files are there. + # The name template must not contain any references to `Os`, `Arch` and etc, since the archive will be meta. + # Defaul is false. + meta: true + # Archive name template. # Defaults: # - if format is `tar.gz`, `tar.xz`, `gz` or `zip`: