Skip to content

Commit

Permalink
feat: allow to template archives.files.info (#3630)
Browse files Browse the repository at this point in the history
this allows to template the owner, group and mtime in file infos inside
archives.

should help towards reproducible builds!

goes well with #3618

Signed-off-by: Carlos A Becker <caarlos0@users.noreply.github.com>
  • Loading branch information
caarlos0 committed Dec 14, 2022
1 parent 3cfe215 commit 2e5a8e5
Show file tree
Hide file tree
Showing 13 changed files with 167 additions and 62 deletions.
20 changes: 20 additions & 0 deletions internal/archivefiles/archivefiles.go
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"path/filepath"
"sort"
"time"

"github.com/caarlos0/log"
"github.com/goreleaser/fileglob"
Expand All @@ -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,
Expand Down
134 changes: 105 additions & 29 deletions internal/archivefiles/archivefiles_test.go
Expand Up @@ -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"
Expand All @@ -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{
Expand Down Expand Up @@ -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,
},
},
})
Expand All @@ -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)
Expand All @@ -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,
},
},
})
Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions pkg/archive/gzip/gzip.go
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pkg/archive/gzip/gzip_test.go
Expand Up @@ -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())
Expand Down
4 changes: 2 additions & 2 deletions pkg/archive/tar/tar.go
Expand Up @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions pkg/archive/tar/tar_test.go
Expand Up @@ -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,
},
}))

Expand Down
8 changes: 4 additions & 4 deletions pkg/archive/targz/targz_test.go
Expand Up @@ -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,
},
}))

Expand Down
8 changes: 4 additions & 4 deletions pkg/archive/tarxz/tarxz_test.go
Expand Up @@ -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,
},
}))

Expand Down
4 changes: 2 additions & 2 deletions pkg/archive/zip/zip.go
Expand Up @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions pkg/archive/zip/zip_test.go
Expand Up @@ -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,
},
}))

Expand Down
9 changes: 5 additions & 4 deletions pkg/config/config.go
Expand Up @@ -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.
Expand Down
9 changes: 5 additions & 4 deletions pkg/config/config_archive_files_test.go
Expand Up @@ -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)
Expand Down
11 changes: 9 additions & 2 deletions www/docs/customization/archive.md
Expand Up @@ -96,11 +96,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.
Expand Down

0 comments on commit 2e5a8e5

Please sign in to comment.