From 9b8246452066c0010ddf8f998a2d3e5ce15c0072 Mon Sep 17 00:00:00 2001 From: Carlos A Becker Date: Sun, 11 Dec 2022 14:11:36 -0300 Subject: [PATCH] feat: allow to template archives.files.info Signed-off-by: Carlos A Becker --- internal/archivefiles/archivefiles.go | 20 +++ internal/archivefiles/archivefiles_test.go | 134 ++++++++++++++++----- pkg/archive/gzip/gzip.go | 4 +- pkg/archive/gzip/gzip_test.go | 2 +- pkg/archive/tar/tar.go | 4 +- pkg/archive/tar/tar_test.go | 8 +- pkg/archive/targz/targz_test.go | 8 +- pkg/archive/tarxz/tarxz_test.go | 8 +- pkg/archive/zip/zip.go | 4 +- pkg/archive/zip/zip_test.go | 8 +- pkg/config/config.go | 9 +- pkg/config/config_archive_files_test.go | 9 +- www/docs/customization/archive.md | 11 +- 13 files changed, 167 insertions(+), 62 deletions(-) diff --git a/internal/archivefiles/archivefiles.go b/internal/archivefiles/archivefiles.go index da91d2eb295..fd8b96cefdd 100644 --- a/internal/archivefiles/archivefiles.go +++ b/internal/archivefiles/archivefiles.go @@ -5,6 +5,7 @@ import ( "fmt" "path/filepath" "sort" + "time" "github.com/caarlos0/log" "github.com/goreleaser/fileglob" @@ -26,6 +27,25 @@ func Eval(template *tmpl.Template, files []config.File) ([]config.File, error) { return result, fmt.Errorf("globbing failed for pattern %s: %w", replaced, err) } + f.Info.Owner, err = template.Apply(f.Info.Owner) + if err != nil { + return result, fmt.Errorf("failed to apply template %s: %w", f.Info.Owner, err) + } + f.Info.Group, err = template.Apply(f.Info.Group) + if err != nil { + return result, fmt.Errorf("failed to apply template %s: %w", f.Info.Group, err) + } + f.Info.MTime, err = template.Apply(f.Info.MTime) + if err != nil { + return result, fmt.Errorf("failed to apply template %s: %w", f.Info.MTime, err) + } + if f.Info.MTime != "" { + f.Info.ParsedMTime, err = time.Parse(time.RFC3339Nano, f.Info.MTime) + if err != nil { + return result, fmt.Errorf("failed to parse %s: %w", f.Info.MTime, err) + } + } + for _, file := range files { result = append(result, config.File{ Source: file, diff --git a/internal/archivefiles/archivefiles_test.go b/internal/archivefiles/archivefiles_test.go index 3c99662c6e4..48717bdb24c 100644 --- a/internal/archivefiles/archivefiles_test.go +++ b/internal/archivefiles/archivefiles_test.go @@ -4,6 +4,7 @@ import ( "testing" "time" + "github.com/goreleaser/goreleaser/internal/testlib" "github.com/goreleaser/goreleaser/internal/tmpl" "github.com/goreleaser/goreleaser/pkg/config" "github.com/goreleaser/goreleaser/pkg/context" @@ -12,7 +13,82 @@ import ( func TestEval(t *testing.T) { now := time.Now().Truncate(time.Second) - tmpl := tmpl.New(context.New(config.Project{})) + ctx := context.New(config.Project{ + Env: []string{"OWNER=carlos"}, + }) + ctx.Git.CommitDate = now + tmpl := tmpl.New(ctx) + + t.Run("templated info", func(t *testing.T) { + result, err := Eval(tmpl, []config.File{ + { + Source: "./testdata/**/d.txt", + Destination: "var/foobar/d.txt", + Info: config.FileInfo{ + MTime: "{{.CommitDate}}", + Owner: "{{ .Env.OWNER }}", + Group: "{{ .Env.OWNER }}", + }, + }, + }) + + 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", + Info: config.FileInfo{ + MTime: now.UTC().Format(time.RFC3339), + ParsedMTime: now.UTC(), + Owner: "carlos", + Group: "carlos", + }, + }, + }, result) + }) + + t.Run("template info errors", func(t *testing.T) { + t.Run("owner", func(t *testing.T) { + _, err := Eval(tmpl, []config.File{{ + Source: "./testdata/**/d.txt", + Destination: "var/foobar/d.txt", + Info: config.FileInfo{ + Owner: "{{ .Env.NOPE }}", + }, + }}) + testlib.RequireTemplateError(t, err) + }) + t.Run("group", func(t *testing.T) { + _, err := Eval(tmpl, []config.File{{ + Source: "./testdata/**/d.txt", + Destination: "var/foobar/d.txt", + Info: config.FileInfo{ + Group: "{{ .Env.NOPE }}", + }, + }}) + testlib.RequireTemplateError(t, err) + }) + t.Run("mtime", func(t *testing.T) { + _, err := Eval(tmpl, []config.File{{ + Source: "./testdata/**/d.txt", + Destination: "var/foobar/d.txt", + Info: config.FileInfo{ + MTime: "{{ .Env.NOPE }}", + }, + }}) + testlib.RequireTemplateError(t, err) + }) + t.Run("mtime format", func(t *testing.T) { + _, err := Eval(tmpl, []config.File{{ + Source: "./testdata/**/d.txt", + Destination: "var/foobar/d.txt", + Info: config.FileInfo{ + MTime: "2005-123-123", + }, + }}) + require.Error(t, err) + }) + }) t.Run("single file", func(t *testing.T) { result, err := Eval(tmpl, []config.File{ @@ -68,10 +144,10 @@ func TestEval(t *testing.T) { Source: "./testdata/a", Destination: "usr/local/test", Info: config.FileInfo{ - Owner: "carlos", - Group: "users", - Mode: 0o755, - MTime: now, + Owner: "carlos", + Group: "users", + Mode: 0o755, + ParsedMTime: now, }, }, }) @@ -82,30 +158,30 @@ func TestEval(t *testing.T) { Source: "testdata/a/a.txt", Destination: "usr/local/test/testdata/a/a.txt", Info: config.FileInfo{ - Owner: "carlos", - Group: "users", - Mode: 0o755, - MTime: now, + Owner: "carlos", + Group: "users", + Mode: 0o755, + ParsedMTime: 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, + Owner: "carlos", + Group: "users", + Mode: 0o755, + ParsedMTime: 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, + Owner: "carlos", + Group: "users", + Mode: 0o755, + ParsedMTime: now, }, }, }, result) @@ -118,10 +194,10 @@ func TestEval(t *testing.T) { Destination: "usr/local/test", StripParent: true, Info: config.FileInfo{ - Owner: "carlos", - Group: "users", - Mode: 0o755, - MTime: now, + Owner: "carlos", + Group: "users", + Mode: 0o755, + ParsedMTime: now, }, }, }) @@ -132,20 +208,20 @@ func TestEval(t *testing.T) { Source: "testdata/a/a.txt", Destination: "usr/local/test/a.txt", Info: config.FileInfo{ - Owner: "carlos", - Group: "users", - Mode: 0o755, - MTime: now, + Owner: "carlos", + Group: "users", + Mode: 0o755, + ParsedMTime: 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, + Owner: "carlos", + Group: "users", + Mode: 0o755, + ParsedMTime: now, }, }, }, result) diff --git a/pkg/archive/gzip/gzip.go b/pkg/archive/gzip/gzip.go index 5c18f48be97..8c6a13c06d3 100644 --- a/pkg/archive/gzip/gzip.go +++ b/pkg/archive/gzip/gzip.go @@ -48,10 +48,10 @@ func (a Archive) Add(f config.File) error { return nil } a.gw.Header.Name = f.Destination - if f.Info.MTime.IsZero() { + if f.Info.ParsedMTime.IsZero() { a.gw.Header.ModTime = info.ModTime() } else { - a.gw.Header.ModTime = f.Info.MTime + a.gw.Header.ModTime = f.Info.ParsedMTime } _, err = io.Copy(a.gw, file) return err diff --git a/pkg/archive/gzip/gzip_test.go b/pkg/archive/gzip/gzip_test.go index 4b12cb9f769..bf278c17d1a 100644 --- a/pkg/archive/gzip/gzip_test.go +++ b/pkg/archive/gzip/gzip_test.go @@ -63,7 +63,7 @@ func TestGzFileCustomMtime(t *testing.T) { Destination: "sub1/sub2/subfoo.txt", Source: "../testdata/sub1/sub2/subfoo.txt", Info: config.FileInfo{ - MTime: now, + ParsedMTime: now, }, })) require.NoError(t, archive.Close()) diff --git a/pkg/archive/tar/tar.go b/pkg/archive/tar/tar.go index 21497ddd172..a6c5c8b3be6 100644 --- a/pkg/archive/tar/tar.go +++ b/pkg/archive/tar/tar.go @@ -45,8 +45,8 @@ func (a Archive) Add(f config.File) error { return fmt.Errorf("%s: %w", f.Source, err) } header.Name = f.Destination - if !f.Info.MTime.IsZero() { - header.ModTime = f.Info.MTime + if !f.Info.ParsedMTime.IsZero() { + header.ModTime = f.Info.ParsedMTime } if f.Info.Mode != 0 { header.Mode = int64(f.Info.Mode) diff --git a/pkg/archive/tar/tar_test.go b/pkg/archive/tar/tar_test.go index 7b602daeb56..1222324f5e8 100644 --- a/pkg/archive/tar/tar_test.go +++ b/pkg/archive/tar/tar_test.go @@ -114,10 +114,10 @@ func TestTarFileInfo(t *testing.T) { Source: "../testdata/foo.txt", Destination: "nope.txt", Info: config.FileInfo{ - Mode: 0o755, - Owner: "carlos", - Group: "root", - MTime: now, + Mode: 0o755, + Owner: "carlos", + Group: "root", + ParsedMTime: now, }, })) diff --git a/pkg/archive/targz/targz_test.go b/pkg/archive/targz/targz_test.go index 7e7154626b3..b83b2bea87f 100644 --- a/pkg/archive/targz/targz_test.go +++ b/pkg/archive/targz/targz_test.go @@ -119,10 +119,10 @@ func TestTarGzFileInfo(t *testing.T) { Source: "../testdata/foo.txt", Destination: "nope.txt", Info: config.FileInfo{ - Mode: 0o755, - Owner: "carlos", - Group: "root", - MTime: now, + Mode: 0o755, + Owner: "carlos", + Group: "root", + ParsedMTime: now, }, })) diff --git a/pkg/archive/tarxz/tarxz_test.go b/pkg/archive/tarxz/tarxz_test.go index 0029375347f..61b83e55d25 100644 --- a/pkg/archive/tarxz/tarxz_test.go +++ b/pkg/archive/tarxz/tarxz_test.go @@ -118,10 +118,10 @@ func TestTarXzFileInfo(t *testing.T) { Source: "../testdata/foo.txt", Destination: "nope.txt", Info: config.FileInfo{ - Mode: 0o755, - Owner: "carlos", - Group: "root", - MTime: now, + Mode: 0o755, + Owner: "carlos", + Group: "root", + ParsedMTime: now, }, })) diff --git a/pkg/archive/zip/zip.go b/pkg/archive/zip/zip.go index 7005465d14a..cd6513ea910 100644 --- a/pkg/archive/zip/zip.go +++ b/pkg/archive/zip/zip.go @@ -49,8 +49,8 @@ func (a Archive) Add(f config.File) error { } header.Name = f.Destination header.Method = zip.Deflate - if !f.Info.MTime.IsZero() { - header.Modified = f.Info.MTime + if !f.Info.ParsedMTime.IsZero() { + header.Modified = f.Info.ParsedMTime } if f.Info.Mode != 0 { header.SetMode(f.Info.Mode) diff --git a/pkg/archive/zip/zip_test.go b/pkg/archive/zip/zip_test.go index 231027bbd2a..8f04d6442f4 100644 --- a/pkg/archive/zip/zip_test.go +++ b/pkg/archive/zip/zip_test.go @@ -117,10 +117,10 @@ func TestZipFileInfo(t *testing.T) { Source: "../testdata/foo.txt", Destination: "nope.txt", Info: config.FileInfo{ - Mode: 0o755, - Owner: "carlos", - Group: "root", - MTime: now, + Mode: 0o755, + Owner: "carlos", + Group: "root", + ParsedMTime: now, }, })) diff --git a/pkg/config/config.go b/pkg/config/config.go index fbb8fa103a1..23b16570e7b 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -428,10 +428,11 @@ type File struct { // FileInfo is the file info of a file. type FileInfo struct { - Owner string `yaml:"owner,omitempty" json:"owner,omitempty"` - Group string `yaml:"group,omitempty" json:"group,omitempty"` - Mode os.FileMode `yaml:"mode,omitempty" json:"mode,omitempty"` - MTime time.Time `yaml:"mtime,omitempty" json:"mtime,omitempty"` + Owner string `yaml:"owner,omitempty" json:"owner,omitempty"` + Group string `yaml:"group,omitempty" json:"group,omitempty"` + Mode os.FileMode `yaml:"mode,omitempty" json:"mode,omitempty"` + MTime string `yaml:"mtime,omitempty" json:"mtime,omitempty"` + ParsedMTime time.Time `yaml:"-" json:"-"` } // UnmarshalYAML is a custom unmarshaler that wraps strings in arrays. diff --git a/pkg/config/config_archive_files_test.go b/pkg/config/config_archive_files_test.go index b6b13f9c9f3..7efbbe3f2a3 100644 --- a/pkg/config/config_archive_files_test.go +++ b/pkg/config/config_archive_files_test.go @@ -84,10 +84,11 @@ files: Source: "./foobar", Destination: "./barzaz", Info: FileInfo{ - Owner: "carlos", - Group: "users", - Mode: 0o644, - MTime: now, + Owner: "carlos", + Group: "users", + Mode: 0o644, + MTime: now.Format(time.RFC3339Nano), + ParsedMTime: time.Time{}, }, }, }, actual.Files) diff --git a/www/docs/customization/archive.md b/www/docs/customization/archive.md index 230d05be8cd..e648520a210 100644 --- a/www/docs/customization/archive.md +++ b/www/docs/customization/archive.md @@ -83,11 +83,18 @@ archives: # Not all fields are supported by all formats available formats. # Defaults to the file info of the actual file if not provided. info: + # Templateable (since v1.14.0) owner: root + + # Templateable (since v1.14.0) group: root + + # Must be in time.RFC3339Nano format. + # Templateable (since v1.14.0) + mtime: '{{ .CommitDate }}' + + # File mode. mode: 0644 - # format is `time.RFC3339Nano` - mtime: 2008-01-02T15:04:05Z # Before and after hooks for each archive. # Skipped if archive format is binary.