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:
parent
3cfe215d4c
commit
2e5a8e5a54
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
},
|
||||
}))
|
||||
|
||||
|
@ -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,
|
||||
},
|
||||
}))
|
||||
|
||||
|
@ -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,
|
||||
},
|
||||
}))
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
},
|
||||
}))
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user