From e67975ea99fb9a4c50b91b096f8b83f197034c3f Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Thu, 25 Aug 2022 01:10:26 -0300 Subject: [PATCH] feat: add extra files to source archives (#3102) closes https://github.com/goreleaser/goreleaser/issues/2911 Signed-off-by: Carlos A Becker --- internal/pipe/sourcearchive/source.go | 49 ++++++++++++++++++++++ internal/pipe/sourcearchive/source_test.go | 45 +++++++++++++++++++- pkg/config/config.go | 9 ++-- www/docs/customization/source.md | 11 +++++ 4 files changed, 109 insertions(+), 5 deletions(-) diff --git a/internal/pipe/sourcearchive/source.go b/internal/pipe/sourcearchive/source.go index dfd433b5e4b..339d718e447 100644 --- a/internal/pipe/sourcearchive/source.go +++ b/internal/pipe/sourcearchive/source.go @@ -5,9 +5,11 @@ import ( "path/filepath" "github.com/caarlos0/log" + "github.com/goreleaser/goreleaser/internal/archivefiles" "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/internal/git" "github.com/goreleaser/goreleaser/internal/tmpl" + "github.com/goreleaser/goreleaser/pkg/config" "github.com/goreleaser/goreleaser/pkg/context" ) @@ -34,7 +36,9 @@ func (Pipe) Run(ctx *context.Context) (err error) { args := []string{ "archive", "-o", path, + "--format", ctx.Config.Source.Format, } + if ctx.Config.Source.PrefixTemplate != "" { prefix, err := tmpl.New(ctx).Apply(ctx.Config.Source.PrefixTemplate) if err != nil { @@ -42,9 +46,19 @@ func (Pipe) Run(ctx *context.Context) (err error) { } args = append(args, "--prefix", prefix) } + + files, err := evalFiles(ctx) + if err != nil { + return err + } + for _, f := range files { + args = append(args, "--add-file", f) + } + args = append(args, ctx.Git.FullCommit) out, err := git.Clean(git.Run(ctx, args...)) log.Debug(out) + ctx.Artifacts.Add(&artifact.Artifact{ Type: artifact.UploadableSourceArchive, Name: filename, @@ -56,6 +70,41 @@ func (Pipe) Run(ctx *context.Context) (err error) { return err } +// to reuse the archivefiles packages, we do something funky: +// - convert the []string to []config.File +// - eval it in archivefiles +// - convert it back to []string +// +// we also handle files already tracked, as if we add them again, +// they'll get duplicated in the archive. +func evalFiles(ctx *context.Context) ([]string, error) { + var files []config.File + for _, f := range ctx.Config.Source.Files { + files = append(files, config.File{ + Source: f, + }) + } + addFiles, err := archivefiles.Eval(tmpl.New(ctx), files) + if err != nil { + return nil, err + } + + var result []string + for _, f := range addFiles { + if isTracked(ctx, f.Source) { + continue + } + result = append(result, f.Source) + } + return result, nil +} + +// check if file is tracked, and, if it is we should not add it to the archive again. +func isTracked(ctx *context.Context, path string) bool { + _, err := git.Run(ctx, "ls-files", "--error-unmatch", path) + return err == nil +} + // Default sets the pipe defaults. func (Pipe) Default(ctx *context.Context) error { archive := &ctx.Config.Source diff --git a/internal/pipe/sourcearchive/source_test.go b/internal/pipe/sourcearchive/source_test.go index 33b7d621d4f..b28926f7418 100644 --- a/internal/pipe/sourcearchive/source_test.go +++ b/internal/pipe/sourcearchive/source_test.go @@ -1,6 +1,7 @@ package sourcearchive import ( + "archive/zip" "os" "path/filepath" "testing" @@ -20,9 +21,12 @@ func TestArchive(t *testing.T) { testlib.GitInit(t) require.NoError(t, os.WriteFile("code.txt", []byte("not really code"), 0o655)) + require.NoError(t, os.WriteFile("code.py", []byte("print 1"), 0o655)) require.NoError(t, os.WriteFile("README.md", []byte("# my dope fake project"), 0o655)) testlib.GitAdd(t) testlib.GitCommit(t, "feat: first") + require.NoError(t, os.WriteFile("added-later.txt", []byte("this file was added later"), 0o655)) + require.NoError(t, os.WriteFile("ignored.md", []byte("never added"), 0o655)) ctx := context.New(config.Project{ ProjectName: "foo", @@ -31,6 +35,9 @@ func TestArchive(t *testing.T) { Format: format, Enabled: true, PrefixTemplate: "{{ .ProjectName }}-{{ .Version }}/", + Files: []string{ + "*.txt", + }, }, }) ctx.Git.FullCommit = "HEAD" @@ -49,13 +56,49 @@ func TestArchive(t *testing.T) { artifact.ExtraFormat: format, }, }, *artifacts[0]) - stat, err := os.Stat(filepath.Join(tmp, "dist", "foo-1.0.0."+format)) + path := filepath.Join(tmp, "dist", "foo-1.0.0."+format) + stat, err := os.Stat(path) require.NoError(t, err) require.Greater(t, stat.Size(), int64(100)) + + if format != "zip" { + return + } + + f, err := os.Open(path) + require.NoError(t, err) + z, err := zip.NewReader(f, stat.Size()) + require.NoError(t, err) + + var paths []string + for _, zf := range z.File { + paths = append(paths, zf.Name) + } + require.Equal(t, []string{ + "foo-1.0.0/", + "foo-1.0.0/README.md", + "foo-1.0.0/code.py", + "foo-1.0.0/code.txt", + "foo-1.0.0/added-later.txt", + }, paths) }) } } +func TestInvalidFormat(t *testing.T) { + ctx := context.New(config.Project{ + Dist: t.TempDir(), + ProjectName: "foo", + Source: config.Source{ + Format: "7z", + Enabled: true, + PrefixTemplate: "{{ .ProjectName }}-{{ .Version }}/", + }, + }) + require.NoError(t, Pipe{}.Default(ctx)) + require.EqualError(t, Pipe{}.Run(ctx), "fatal: Unknown archive format '7z'") +} + func TestDefault(t *testing.T) { ctx := context.New(config.Project{}) require.NoError(t, Pipe{}.Default(ctx)) diff --git a/pkg/config/config.go b/pkg/config/config.go index 7df34ef6341..6dcde36c472 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -876,10 +876,11 @@ type Publisher struct { // Source configuration. type Source struct { - NameTemplate string `yaml:"name_template,omitempty" json:"name_template,omitempty"` - Format string `yaml:"format,omitempty" json:"format,omitempty"` - Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty"` - PrefixTemplate string `yaml:"prefix_template,omitempty" json:"prefix_template,omitempty"` + NameTemplate string `yaml:"name_template,omitempty" json:"name_template,omitempty"` + Format string `yaml:"format,omitempty" json:"format,omitempty"` + Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty"` + PrefixTemplate string `yaml:"prefix_template,omitempty" json:"prefix_template,omitempty"` + Files []string `yaml:"files,omitempty" json:"files,omitempty"` } // Project includes all project configuration. diff --git a/www/docs/customization/source.md b/www/docs/customization/source.md index 50b09b52332..ef1f51815a6 100644 --- a/www/docs/customization/source.md +++ b/www/docs/customization/source.md @@ -23,6 +23,17 @@ source: # String to prepend to each filename in the archive. # Defaults to empty prefix_template: '{{ .ProjectName }}-{{ .Version }}/' + + # Additional files/template/globs you want to add to the source archive. + # Will use --add-file of git-archive. + # Defaults to empty. + files: + - LICENSE.txt + - README_{{.Os}}.md + - CHANGELOG.md + - docs/* + - design/*.png + - templates/**/* ``` !!! tip