1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-03-17 20:47:50 +02:00

feat: allow to template archives.files.info (#3630)

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>
This commit is contained in:
Carlos Alexandro Becker 2022-12-14 12:16:43 -03:00 committed by GitHub
parent 3cfe215d4c
commit 2e5a8e5a54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 167 additions and 62 deletions

View File

@ -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,

View File

@ -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)

View File

@ -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

View File

@ -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())

View File

@ -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)

View File

@ -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,
},
}))

View File

@ -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,
},
}))

View File

@ -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,
},
}))

View File

@ -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)

View File

@ -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,
},
}))

View File

@ -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.

View File

@ -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)

View File

@ -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.