From c0a91454307b9300631b3a19eb36f7544ea00f4b Mon Sep 17 00:00:00 2001 From: Carlos A Becker Date: Wed, 21 Dec 2022 22:21:49 -0300 Subject: [PATCH 01/12] feat: better archives relative paths Signed-off-by: Carlos A Becker --- internal/archivefiles/archivefiles.go | 61 ++++++++++++++++++++-- internal/archivefiles/archivefiles_test.go | 14 +++++ internal/static/config.yaml | 2 +- pkg/config/config.go | 9 ++-- 4 files changed, 77 insertions(+), 9 deletions(-) diff --git a/internal/archivefiles/archivefiles.go b/internal/archivefiles/archivefiles.go index fd8b96cefdd..6f93390a1df 100644 --- a/internal/archivefiles/archivefiles.go +++ b/internal/archivefiles/archivefiles.go @@ -2,7 +2,10 @@ package archivefiles import ( + "errors" "fmt" + "io/fs" + "os" "path/filepath" "sort" "time" @@ -46,10 +49,20 @@ func Eval(template *tmpl.Template, files []config.File) ([]config.File, error) { } } + // the prefix may not be a complete path or may use glob patterns, in that case use the parent directory + prefix := replaced + if _, err := os.Stat(prefix); errors.Is(err, fs.ErrNotExist) || fileglob.ContainsMatchers(prefix) { + prefix = filepath.Dir(longestCommonPrefix(files)) + } + for _, file := range files { + dst, err := destinationFor(f, prefix, file) + if err != nil { + return nil, err + } result = append(result, config.File{ Source: file, - Destination: destinationFor(f, file), + Destination: dst, Info: f.Info, }) } @@ -83,9 +96,49 @@ func unique(in []config.File) []config.File { return result } -func destinationFor(f config.File, path string) string { +func destinationFor(f config.File, prefix, path string) (string, error) { if f.StripParent { - return filepath.Join(f.Destination, filepath.Base(path)) + return filepath.Join(f.Destination, filepath.Base(path)), nil + } + + if f.RelativeParent { + relpath, err := filepath.Rel(prefix, path) + if err != nil { + // since prefix is a prefix of src a relative path should always be found + return "", err + } + return filepath.ToSlash(filepath.Join(f.Destination, relpath)), nil + } + + return filepath.Join(f.Destination, path), nil +} + +// longestCommonPrefix returns the longest prefix of all strings the argument +// slice. If the slice is empty the empty string is returned. +// copied from nfpm +func longestCommonPrefix(strs []string) string { + if len(strs) == 0 { + return "" + } + lcp := strs[0] + for _, str := range strs { + lcp = strlcp(lcp, str) + } + return lcp +} + +// copied from nfpm +func strlcp(a, b string) string { + var min int + if len(a) > len(b) { + min = len(b) + } else { + min = len(a) + } + for i := 0; i < min; i++ { + if a[i] != b[i] { + return a[0:i] + } } - return filepath.Join(f.Destination, path) + return a[0:min] } diff --git a/internal/archivefiles/archivefiles_test.go b/internal/archivefiles/archivefiles_test.go index 48717bdb24c..3282559eeed 100644 --- a/internal/archivefiles/archivefiles_test.go +++ b/internal/archivefiles/archivefiles_test.go @@ -107,6 +107,20 @@ func TestEval(t *testing.T) { }, result) }) + t.Run("relativeparent is set", func(t *testing.T) { + result, err := Eval(tmpl, []config.File{{ + Source: "./testdata/a/**/*", + Destination: "foo/bar", + RelativeParent: true, + }}) + + require.NoError(t, err) + require.Equal(t, []config.File{ + {Source: "testdata/a/b/a.txt", Destination: "foo/bar/a.txt"}, + {Source: "testdata/a/b/c/d.txt", Destination: "foo/bar/c/d.txt"}, + }, result) + }) + t.Run("strip parent plays nicely with destination omitted", func(t *testing.T) { result, err := Eval(tmpl, []config.File{{Source: "./testdata/a/b", StripParent: true}}) diff --git a/internal/static/config.yaml b/internal/static/config.yaml index b399c7c4395..a65edfee51e 100644 --- a/internal/static/config.yaml +++ b/internal/static/config.yaml @@ -23,7 +23,7 @@ archives: {{- if eq .Arch "amd64" }}x86_64 {{- else if eq .Arch "386" }}i386 {{- else }}{{ .Arch }}{{ end }} - {{- if .Arm }}v{{ .Arm }}{{ end }}' + {{- if .Arm }}v{{ .Arm }}{{ end }} # use zip for windows archives format_overrides: - goos: windows diff --git a/pkg/config/config.go b/pkg/config/config.go index e23528cc1d4..9e277bc3d5f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -420,10 +420,11 @@ type FormatOverride struct { // File is a file inside an archive. type File struct { - Source string `yaml:"src,omitempty" json:"src,omitempty"` - Destination string `yaml:"dst,omitempty" json:"dst,omitempty"` - StripParent bool `yaml:"strip_parent,omitempty" json:"strip_parent,omitempty"` - Info FileInfo `yaml:"info,omitempty" json:"info,omitempty"` + Source string `yaml:"src,omitempty" json:"src,omitempty"` + Destination string `yaml:"dst,omitempty" json:"dst,omitempty"` + StripParent bool `yaml:"strip_parent,omitempty" json:"strip_parent,omitempty"` + RelativeParent bool `yaml:"relative,omitempty" json:"relative,omitempty"` + Info FileInfo `yaml:"info,omitempty" json:"info,omitempty"` } // FileInfo is the file info of a file. From 459cc1384de2f1c01329e161dae6c427cee6f8f0 Mon Sep 17 00:00:00 2001 From: Carlos A Becker Date: Wed, 21 Dec 2022 22:29:20 -0300 Subject: [PATCH 02/12] docs: parent Signed-off-by: Carlos A Becker --- www/docs/customization/archive.md | 10 ++++++++++ www/docs/customization/source.md | 11 ++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/www/docs/customization/archive.md b/www/docs/customization/archive.md index 74f258c2903..058a2a62fb8 100644 --- a/www/docs/customization/archive.md +++ b/www/docs/customization/archive.md @@ -89,9 +89,19 @@ archives: # a more complete example, check the globbing deep dive below - src: '*.md' dst: docs + # Strip parent folders when adding files to the archive. # Default: false strip_parent: true + + # This will make the destination paths be relative to the longest common + # prefix between all the files matched and the source glob. + # Enabling this essentially mimic the behavior of nfpm's contents section. + # + # Default: false + # Since: v1.14. + relative: true + # File info. # Not all fields are supported by all formats available formats. # Defaults to the file info of the actual file if not provided. diff --git a/www/docs/customization/source.md b/www/docs/customization/source.md index 31fa2018f99..5e62c9f66d0 100644 --- a/www/docs/customization/source.md +++ b/www/docs/customization/source.md @@ -38,9 +38,19 @@ source: # a more complete example, check the globbing deep dive below - src: '*.md' dst: docs + # Strip parent folders when adding files to the archive. # Default: false strip_parent: true + + # This will make the destination paths be relative to the longest common + # prefix between all the files matched and the source glob. + # Enabling this essentially mimic the behavior of nfpm's contents section. + # + # Default: false + # Since: v1.14. + relative: true + # File info. # Not all fields are supported by all formats available formats. # Defaults to the file info of the actual file if not provided. @@ -50,7 +60,6 @@ source: mode: 0644 # format is `time.RFC3339Nano` mtime: 2008-01-02T15:04:05Z - ``` !!! tip From e6a60c9835b577dc476eb8029d0aeb9a0143ed9e Mon Sep 17 00:00:00 2001 From: Carlos A Becker Date: Thu, 22 Dec 2022 09:43:50 -0300 Subject: [PATCH 03/12] test: improve test coverage Signed-off-by: Carlos A Becker --- internal/archivefiles/archivefiles_test.go | 38 +++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/internal/archivefiles/archivefiles_test.go b/internal/archivefiles/archivefiles_test.go index 3282559eeed..cd0860f3cb9 100644 --- a/internal/archivefiles/archivefiles_test.go +++ b/internal/archivefiles/archivefiles_test.go @@ -14,11 +14,47 @@ import ( func TestEval(t *testing.T) { now := time.Now().Truncate(time.Second) ctx := context.New(config.Project{ - Env: []string{"OWNER=carlos"}, + Env: []string{"OWNER=carlos", "FOLDER=d"}, }) ctx.Git.CommitDate = now tmpl := tmpl.New(ctx) + t.Run("invalid glob", func(t *testing.T) { + _, err := Eval(tmpl, []config.File{ + { + Source: "../testdata/**/nope.txt", + Destination: "var/foobar/d.txt", + }, + }) + require.Error(t, err) + }) + + t.Run("templated src", func(t *testing.T) { + result, err := Eval(tmpl, []config.File{ + { + Source: "./testdata/**/{{ .Env.FOLDER }}.txt", + Destination: "var/foobar/d.txt", + }, + }) + 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", + }, + }, result) + }) + + t.Run("templated src error", func(t *testing.T) { + _, err := Eval(tmpl, []config.File{ + { + Source: "./testdata/**/{{ .Env.NOPE }}.txt", + Destination: "var/foobar/d.txt", + }, + }) + testlib.RequireTemplateError(t, err) + }) + t.Run("templated info", func(t *testing.T) { result, err := Eval(tmpl, []config.File{ { From bd3f865df3a153dc366f3cf8363e95a571354368 Mon Sep 17 00:00:00 2001 From: Carlos A Becker Date: Thu, 22 Dec 2022 09:46:09 -0300 Subject: [PATCH 04/12] test: more tests Signed-off-by: Carlos A Becker --- internal/archivefiles/archivefiles_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/internal/archivefiles/archivefiles_test.go b/internal/archivefiles/archivefiles_test.go index cd0860f3cb9..35cb7ba04c0 100644 --- a/internal/archivefiles/archivefiles_test.go +++ b/internal/archivefiles/archivefiles_test.go @@ -157,6 +157,17 @@ func TestEval(t *testing.T) { }, result) }) + t.Run("relativeparent no results", func(t *testing.T) { + result, err := Eval(tmpl, []config.File{{ + Source: "./testdata/abc/**/*", + Destination: "foo/bar", + RelativeParent: true, + }}) + + require.NoError(t, err) + require.Empty(t, result) + }) + t.Run("strip parent plays nicely with destination omitted", func(t *testing.T) { result, err := Eval(tmpl, []config.File{{Source: "./testdata/a/b", StripParent: true}}) From 5a6a3d4d8d8c1469d386164b403311e2ee323bd1 Mon Sep 17 00:00:00 2001 From: Carlos A Becker Date: Thu, 22 Dec 2022 09:49:58 -0300 Subject: [PATCH 05/12] test: more tests Signed-off-by: Carlos A Becker --- internal/archivefiles/archivefiles_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/internal/archivefiles/archivefiles_test.go b/internal/archivefiles/archivefiles_test.go index 35cb7ba04c0..3155bb134dc 100644 --- a/internal/archivefiles/archivefiles_test.go +++ b/internal/archivefiles/archivefiles_test.go @@ -288,3 +288,16 @@ func TestEval(t *testing.T) { }, result) }) } + +func TestStrlcp(t *testing.T) { + for k, v := range map[string][2]string{ + "/var/": {"/var/lib/foo", "/var/share/aaa"}, + "/var/lib/": {"/var/lib/foo", "/var/lib/share/aaa"}, + "/usr/share/": {"/usr/share/lib", "/usr/share/bin"}, + "/usr/": {"/usr/share/lib", "/usr/bin"}, + } { + t.Run(k, func(t *testing.T) { + require.Equal(t, k, strlcp(v[0], v[1])) + }) + } +} From 93a45198be30313789249e0bcfb66a693286bdd9 Mon Sep 17 00:00:00 2001 From: Carlos A Becker Date: Thu, 22 Dec 2022 09:52:15 -0300 Subject: [PATCH 06/12] feat: rename field Signed-off-by: Carlos A Becker --- internal/archivefiles/archivefiles.go | 2 +- internal/archivefiles/archivefiles_test.go | 12 ++++++------ pkg/config/config.go | 11 ++++++----- www/docs/customization/archive.md | 4 ++-- www/docs/customization/source.md | 4 ++-- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/internal/archivefiles/archivefiles.go b/internal/archivefiles/archivefiles.go index 6f93390a1df..68ed78e477b 100644 --- a/internal/archivefiles/archivefiles.go +++ b/internal/archivefiles/archivefiles.go @@ -101,7 +101,7 @@ func destinationFor(f config.File, prefix, path string) (string, error) { return filepath.Join(f.Destination, filepath.Base(path)), nil } - if f.RelativeParent { + if f.RelativeLongestCommonPath { relpath, err := filepath.Rel(prefix, path) if err != nil { // since prefix is a prefix of src a relative path should always be found diff --git a/internal/archivefiles/archivefiles_test.go b/internal/archivefiles/archivefiles_test.go index 3155bb134dc..a1dec62ec24 100644 --- a/internal/archivefiles/archivefiles_test.go +++ b/internal/archivefiles/archivefiles_test.go @@ -145,9 +145,9 @@ func TestEval(t *testing.T) { t.Run("relativeparent is set", func(t *testing.T) { result, err := Eval(tmpl, []config.File{{ - Source: "./testdata/a/**/*", - Destination: "foo/bar", - RelativeParent: true, + Source: "./testdata/a/**/*", + Destination: "foo/bar", + RelativeLongestCommonPath: true, }}) require.NoError(t, err) @@ -159,9 +159,9 @@ func TestEval(t *testing.T) { t.Run("relativeparent no results", func(t *testing.T) { result, err := Eval(tmpl, []config.File{{ - Source: "./testdata/abc/**/*", - Destination: "foo/bar", - RelativeParent: true, + Source: "./testdata/abc/**/*", + Destination: "foo/bar", + RelativeLongestCommonPath: true, }}) require.NoError(t, err) diff --git a/pkg/config/config.go b/pkg/config/config.go index 9e277bc3d5f..c5cb60a4bb9 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -420,11 +420,12 @@ type FormatOverride struct { // File is a file inside an archive. type File struct { - Source string `yaml:"src,omitempty" json:"src,omitempty"` - Destination string `yaml:"dst,omitempty" json:"dst,omitempty"` - StripParent bool `yaml:"strip_parent,omitempty" json:"strip_parent,omitempty"` - RelativeParent bool `yaml:"relative,omitempty" json:"relative,omitempty"` - Info FileInfo `yaml:"info,omitempty" json:"info,omitempty"` + Source string `yaml:"src,omitempty" json:"src,omitempty"` + Destination string `yaml:"dst,omitempty" json:"dst,omitempty"` + Info FileInfo `yaml:"info,omitempty" json:"info,omitempty"` + + StripParent bool `yaml:"strip_parent,omitempty" json:"strip_parent,omitempty"` + RelativeLongestCommonPath bool `yaml:"rlcp,omitempty" json:"rlcp,omitempty"` } // FileInfo is the file info of a file. diff --git a/www/docs/customization/archive.md b/www/docs/customization/archive.md index 058a2a62fb8..53ae8ec5f5d 100644 --- a/www/docs/customization/archive.md +++ b/www/docs/customization/archive.md @@ -95,12 +95,12 @@ archives: strip_parent: true # This will make the destination paths be relative to the longest common - # prefix between all the files matched and the source glob. + # path prefix between all the files matched and the source glob. # Enabling this essentially mimic the behavior of nfpm's contents section. # # Default: false # Since: v1.14. - relative: true + rlcp: true # File info. # Not all fields are supported by all formats available formats. diff --git a/www/docs/customization/source.md b/www/docs/customization/source.md index 5e62c9f66d0..042d3f98d73 100644 --- a/www/docs/customization/source.md +++ b/www/docs/customization/source.md @@ -44,12 +44,12 @@ source: strip_parent: true # This will make the destination paths be relative to the longest common - # prefix between all the files matched and the source glob. + # path prefix between all the files matched and the source glob. # Enabling this essentially mimic the behavior of nfpm's contents section. # # Default: false # Since: v1.14. - relative: true + rlcp: true # File info. # Not all fields are supported by all formats available formats. From 4c07f88b7970e8cdb14b784c6817b9fd6929a7b7 Mon Sep 17 00:00:00 2001 From: Carlos A Becker Date: Fri, 23 Dec 2022 09:22:47 -0300 Subject: [PATCH 07/12] fix: move option, deprecations, etc Signed-off-by: Carlos A Becker --- internal/archivefiles/archivefiles.go | 8 ++-- internal/archivefiles/archivefiles_test.go | 46 +++++++++++----------- internal/pipe/archive/archive.go | 5 ++- internal/pipe/sourcearchive/source.go | 11 +++++- pkg/config/config.go | 6 +-- www/docs/customization/archive.md | 18 +++++---- www/docs/customization/source.md | 17 ++++---- www/docs/deprecations.md | 40 +++++++++++++++++++ 8 files changed, 102 insertions(+), 49 deletions(-) diff --git a/internal/archivefiles/archivefiles.go b/internal/archivefiles/archivefiles.go index 68ed78e477b..e9bc59d040e 100644 --- a/internal/archivefiles/archivefiles.go +++ b/internal/archivefiles/archivefiles.go @@ -17,7 +17,7 @@ import ( ) // Eval evaluates the given list of files to their final form. -func Eval(template *tmpl.Template, files []config.File) ([]config.File, error) { +func Eval(template *tmpl.Template, rlcp bool, files []config.File) ([]config.File, error) { var result []config.File for _, f := range files { replaced, err := template.Apply(f.Source) @@ -56,7 +56,7 @@ func Eval(template *tmpl.Template, files []config.File) ([]config.File, error) { } for _, file := range files { - dst, err := destinationFor(f, prefix, file) + dst, err := destinationFor(f, prefix, file, rlcp) if err != nil { return nil, err } @@ -96,12 +96,12 @@ func unique(in []config.File) []config.File { return result } -func destinationFor(f config.File, prefix, path string) (string, error) { +func destinationFor(f config.File, prefix, path string, rlcp bool) (string, error) { if f.StripParent { return filepath.Join(f.Destination, filepath.Base(path)), nil } - if f.RelativeLongestCommonPath { + if rlcp { relpath, err := filepath.Rel(prefix, path) if err != nil { // since prefix is a prefix of src a relative path should always be found diff --git a/internal/archivefiles/archivefiles_test.go b/internal/archivefiles/archivefiles_test.go index a1dec62ec24..7e07c514415 100644 --- a/internal/archivefiles/archivefiles_test.go +++ b/internal/archivefiles/archivefiles_test.go @@ -20,7 +20,7 @@ func TestEval(t *testing.T) { tmpl := tmpl.New(ctx) t.Run("invalid glob", func(t *testing.T) { - _, err := Eval(tmpl, []config.File{ + _, err := Eval(tmpl, false, []config.File{ { Source: "../testdata/**/nope.txt", Destination: "var/foobar/d.txt", @@ -30,7 +30,7 @@ func TestEval(t *testing.T) { }) t.Run("templated src", func(t *testing.T) { - result, err := Eval(tmpl, []config.File{ + result, err := Eval(tmpl, false, []config.File{ { Source: "./testdata/**/{{ .Env.FOLDER }}.txt", Destination: "var/foobar/d.txt", @@ -46,7 +46,7 @@ func TestEval(t *testing.T) { }) t.Run("templated src error", func(t *testing.T) { - _, err := Eval(tmpl, []config.File{ + _, err := Eval(tmpl, false, []config.File{ { Source: "./testdata/**/{{ .Env.NOPE }}.txt", Destination: "var/foobar/d.txt", @@ -56,7 +56,7 @@ func TestEval(t *testing.T) { }) t.Run("templated info", func(t *testing.T) { - result, err := Eval(tmpl, []config.File{ + result, err := Eval(tmpl, false, []config.File{ { Source: "./testdata/**/d.txt", Destination: "var/foobar/d.txt", @@ -85,7 +85,7 @@ func TestEval(t *testing.T) { t.Run("template info errors", func(t *testing.T) { t.Run("owner", func(t *testing.T) { - _, err := Eval(tmpl, []config.File{{ + _, err := Eval(tmpl, false, []config.File{{ Source: "./testdata/**/d.txt", Destination: "var/foobar/d.txt", Info: config.FileInfo{ @@ -95,7 +95,7 @@ func TestEval(t *testing.T) { testlib.RequireTemplateError(t, err) }) t.Run("group", func(t *testing.T) { - _, err := Eval(tmpl, []config.File{{ + _, err := Eval(tmpl, false, []config.File{{ Source: "./testdata/**/d.txt", Destination: "var/foobar/d.txt", Info: config.FileInfo{ @@ -105,7 +105,7 @@ func TestEval(t *testing.T) { testlib.RequireTemplateError(t, err) }) t.Run("mtime", func(t *testing.T) { - _, err := Eval(tmpl, []config.File{{ + _, err := Eval(tmpl, false, []config.File{{ Source: "./testdata/**/d.txt", Destination: "var/foobar/d.txt", Info: config.FileInfo{ @@ -115,7 +115,7 @@ func TestEval(t *testing.T) { testlib.RequireTemplateError(t, err) }) t.Run("mtime format", func(t *testing.T) { - _, err := Eval(tmpl, []config.File{{ + _, err := Eval(tmpl, false, []config.File{{ Source: "./testdata/**/d.txt", Destination: "var/foobar/d.txt", Info: config.FileInfo{ @@ -127,7 +127,7 @@ func TestEval(t *testing.T) { }) t.Run("single file", func(t *testing.T) { - result, err := Eval(tmpl, []config.File{ + result, err := Eval(tmpl, false, []config.File{ { Source: "./testdata/**/d.txt", Destination: "var/foobar/d.txt", @@ -143,11 +143,10 @@ func TestEval(t *testing.T) { }, result) }) - t.Run("relativeparent is set", func(t *testing.T) { - result, err := Eval(tmpl, []config.File{{ - Source: "./testdata/a/**/*", - Destination: "foo/bar", - RelativeLongestCommonPath: true, + t.Run("rlcp is set", func(t *testing.T) { + result, err := Eval(tmpl, true, []config.File{{ + Source: "./testdata/a/**/*", + Destination: "foo/bar", }}) require.NoError(t, err) @@ -157,11 +156,10 @@ func TestEval(t *testing.T) { }, result) }) - t.Run("relativeparent no results", func(t *testing.T) { - result, err := Eval(tmpl, []config.File{{ - Source: "./testdata/abc/**/*", - Destination: "foo/bar", - RelativeLongestCommonPath: true, + t.Run("rlcp no results", func(t *testing.T) { + result, err := Eval(tmpl, true, []config.File{{ + Source: "./testdata/abc/**/*", + Destination: "foo/bar", }}) require.NoError(t, err) @@ -169,7 +167,7 @@ func TestEval(t *testing.T) { }) t.Run("strip parent plays nicely with destination omitted", func(t *testing.T) { - result, err := Eval(tmpl, []config.File{{Source: "./testdata/a/b", StripParent: true}}) + result, err := Eval(tmpl, false, []config.File{{Source: "./testdata/a/b", StripParent: true}}) require.NoError(t, err) require.Equal(t, []config.File{ @@ -179,7 +177,7 @@ func TestEval(t *testing.T) { }) t.Run("strip parent plays nicely with destination as an empty string", func(t *testing.T) { - result, err := Eval(tmpl, []config.File{{Source: "./testdata/a/b", Destination: "", StripParent: true}}) + result, err := Eval(tmpl, false, []config.File{{Source: "./testdata/a/b", Destination: "", StripParent: true}}) require.NoError(t, err) require.Equal(t, []config.File{ @@ -189,7 +187,7 @@ func TestEval(t *testing.T) { }) t.Run("match multiple files within tree without destination", func(t *testing.T) { - result, err := Eval(tmpl, []config.File{{Source: "./testdata/a"}}) + result, err := Eval(tmpl, false, []config.File{{Source: "./testdata/a"}}) require.NoError(t, err) require.Equal(t, []config.File{ @@ -200,7 +198,7 @@ func TestEval(t *testing.T) { }) t.Run("match multiple files within tree specific destination", func(t *testing.T) { - result, err := Eval(tmpl, []config.File{ + result, err := Eval(tmpl, false, []config.File{ { Source: "./testdata/a", Destination: "usr/local/test", @@ -249,7 +247,7 @@ func TestEval(t *testing.T) { }) t.Run("match multiple files within tree specific destination stripping parents", func(t *testing.T) { - result, err := Eval(tmpl, []config.File{ + result, err := Eval(tmpl, false, []config.File{ { Source: "./testdata/a", Destination: "usr/local/test", diff --git a/internal/pipe/archive/archive.go b/internal/pipe/archive/archive.go index 32c2dd5d0ea..bad6f3a4475 100644 --- a/internal/pipe/archive/archive.go +++ b/internal/pipe/archive/archive.go @@ -78,6 +78,9 @@ func (Pipe) Default(ctx *context.Context) error { if len(archive.Replacements) != 0 { deprecate.Notice(ctx, "archives.replacements") } + if !archive.RLCP { + deprecate.NoticeCustom(ctx, "archives.rclp", "`{{ .Property }}` will be the default soon, check {{ .URL }} for more info") + } ids.Inc(archive.ID) } return ids.Validate() @@ -185,7 +188,7 @@ func doCreate(ctx *context.Context, arch config.Archive, binaries []*artifact.Ar a = NewEnhancedArchive(a, wrap) defer a.Close() - files, err := archivefiles.Eval(template, arch.Files) + files, err := archivefiles.Eval(template, arch.RLCP, arch.Files) if err != nil { return fmt.Errorf("failed to find files to archive: %w", err) } diff --git a/internal/pipe/sourcearchive/source.go b/internal/pipe/sourcearchive/source.go index 102713bb1d3..9ccbf79652d 100644 --- a/internal/pipe/sourcearchive/source.go +++ b/internal/pipe/sourcearchive/source.go @@ -10,6 +10,7 @@ import ( "github.com/caarlos0/log" "github.com/goreleaser/goreleaser/internal/archivefiles" "github.com/goreleaser/goreleaser/internal/artifact" + "github.com/goreleaser/goreleaser/internal/deprecate" "github.com/goreleaser/goreleaser/internal/git" "github.com/goreleaser/goreleaser/internal/tmpl" "github.com/goreleaser/goreleaser/pkg/archive" @@ -68,7 +69,11 @@ func (Pipe) Run(ctx *context.Context) (err error) { Source: f, }) } - files, err := archivefiles.Eval(tmpl.New(ctx), append(ff, ctx.Config.Source.Files...)) + files, err := archivefiles.Eval( + tmpl.New(ctx), + ctx.Config.Source.RLCP, + append(ff, ctx.Config.Source.Files...), + ) if err != nil { return err } @@ -107,5 +112,9 @@ func (Pipe) Default(ctx *context.Context) error { if archive.NameTemplate == "" { archive.NameTemplate = "{{ .ProjectName }}-{{ .Version }}" } + + if !archive.RLCP { + deprecate.NoticeCustom(ctx, "source.rclp", "`{{ .Property }}` will be the default soon, check {{ .URL }} for more info") + } return nil } diff --git a/pkg/config/config.go b/pkg/config/config.go index c5cb60a4bb9..6b546637cca 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -422,10 +422,8 @@ type FormatOverride struct { type File struct { Source string `yaml:"src,omitempty" json:"src,omitempty"` Destination string `yaml:"dst,omitempty" json:"dst,omitempty"` + StripParent bool `yaml:"strip_parent,omitempty" json:"strip_parent,omitempty"` Info FileInfo `yaml:"info,omitempty" json:"info,omitempty"` - - StripParent bool `yaml:"strip_parent,omitempty" json:"strip_parent,omitempty"` - RelativeLongestCommonPath bool `yaml:"rlcp,omitempty" json:"rlcp,omitempty"` } // FileInfo is the file info of a file. @@ -490,6 +488,7 @@ type Archive struct { FormatOverrides []FormatOverride `yaml:"format_overrides,omitempty" json:"format_overrides,omitempty"` WrapInDirectory string `yaml:"wrap_in_directory,omitempty" json:"wrap_in_directory,omitempty" jsonschema:"oneof_type=string;boolean"` StripParentBinaryFolder bool `yaml:"strip_parent_binary_folder,omitempty" json:"strip_parent_binary_folder,omitempty"` + RLCP bool `yaml:"rlcp,omitempty" json:"rlcp,omitempty"` Files []File `yaml:"files,omitempty" json:"files,omitempty"` Meta bool `yaml:"meta,omitempty" json:"meta,omitempty"` AllowDifferentBinaryCount bool `yaml:"allow_different_binary_count,omitempty" json:"allow_different_binary_count,omitempty"` @@ -905,6 +904,7 @@ type Source struct { Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty"` PrefixTemplate string `yaml:"prefix_template,omitempty" json:"prefix_template,omitempty"` Files []File `yaml:"files,omitempty" json:"files,omitempty"` + RLCP bool `yaml:"rlcp,omitempty" json:"rlcp,omitempty"` } // Project includes all project configuration. diff --git a/www/docs/customization/archive.md b/www/docs/customization/archive.md index 53ae8ec5f5d..e56da67cbba 100644 --- a/www/docs/customization/archive.md +++ b/www/docs/customization/archive.md @@ -69,6 +69,16 @@ archives: # Since: v1.11. strip_parent_binary_folder: true + + # This will make the destination paths be relative to the longest common + # path prefix between all the files matched and the source glob. + # Enabling this essentially mimic the behavior of nfpm's contents section. + # It will be the default by June 2023. + # + # Default: false + # Since: v1.14. + rlcp: true + # Can be used to change the archive formats for specific GOOSs. # Most common use case is to archive as zip on Windows. # Default is empty. @@ -94,14 +104,6 @@ archives: # Default: false strip_parent: true - # This will make the destination paths be relative to the longest common - # path prefix between all the files matched and the source glob. - # Enabling this essentially mimic the behavior of nfpm's contents section. - # - # Default: false - # Since: v1.14. - rlcp: true - # File info. # Not all fields are supported by all formats available formats. # Defaults to the file info of the actual file if not provided. diff --git a/www/docs/customization/source.md b/www/docs/customization/source.md index 042d3f98d73..2a393d56c14 100644 --- a/www/docs/customization/source.md +++ b/www/docs/customization/source.md @@ -24,6 +24,15 @@ source: # Defaults to empty prefix_template: '{{ .ProjectName }}-{{ .Version }}/' + # This will make the destination paths be relative to the longest common + # path prefix between all the files matched and the source glob. + # Enabling this essentially mimic the behavior of nfpm's contents section. + # It will be the default by June 2023. + # + # Default: false + # Since: v1.14. + rlcp: true + # Additional files/template/globs you want to add to the source archive. # # Default: empty. @@ -43,14 +52,6 @@ source: # Default: false strip_parent: true - # This will make the destination paths be relative to the longest common - # path prefix between all the files matched and the source glob. - # Enabling this essentially mimic the behavior of nfpm's contents section. - # - # Default: false - # Since: v1.14. - rlcp: true - # File info. # Not all fields are supported by all formats available formats. # Defaults to the file info of the actual file if not provided. diff --git a/www/docs/deprecations.md b/www/docs/deprecations.md index 6ddd12a64b7..8ce95091a5f 100644 --- a/www/docs/deprecations.md +++ b/www/docs/deprecations.md @@ -36,6 +36,46 @@ Description. --> + +### archives.rlcp + +> since 2022-12-23 (v1.14.0) + +This is not so much a deprecation property (yet), as it is a default behavior +change. + +The usage of relative longest common path (`rlcp`) on the destination side of +archive files will be enabled by default by June 2023. Then, this option will be +deprecated, and you will have another 6 months (until December 2023) to remove +it. + +For now, if you want to keep the old behavior, no action is required, but it +would be nice to have your opinion [here][rlcp-discuss]. + +[rlcp-discuss]: https://github.com/goreleaser/goreleaser/discussions/3659 + +If you want to make sure your releases will keep working properly, you can +enable this option and test it out with +`goreleaser release --snapshot --rm-dist`. + +=== "After" + ``` yaml + archives: + rlcp: true + ``` + +### source.rlcp + +> since 2022-12-23 (v1.14.0) + +Same as [`archives.rlcp`](#archivesrlcp). + +=== "After" + ``` yaml + source: + rlcp: true + ``` + ### archives.replacements > since 2022-11-24 (v1.14.0) From 5d15d9c3bdad1fd7b53bda60597f64dfafc9ffc0 Mon Sep 17 00:00:00 2001 From: Carlos A Becker Date: Fri, 23 Dec 2022 09:43:27 -0300 Subject: [PATCH 08/12] fix: improve handling of old behavior --- .goreleaser.yaml | 1 + internal/archivefiles/archivefiles.go | 2 +- internal/archivefiles/archivefiles_test.go | 14 +++++++++++++- internal/pipe/archive/archive.go | 2 +- internal/pipe/sourcearchive/source.go | 2 +- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index c6921bbc00c..a8507b53f1f 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -136,6 +136,7 @@ archives: {{- else if eq .Arch "386" }}i386 {{- else }}{{ .Arch }}{{ end }} {{- if .Arm }}v{{ .Arm }}{{ end }} + rlcp: true format_overrides: - goos: windows format: zip diff --git a/internal/archivefiles/archivefiles.go b/internal/archivefiles/archivefiles.go index e9bc59d040e..11217e6df7e 100644 --- a/internal/archivefiles/archivefiles.go +++ b/internal/archivefiles/archivefiles.go @@ -101,7 +101,7 @@ func destinationFor(f config.File, prefix, path string, rlcp bool) (string, erro return filepath.Join(f.Destination, filepath.Base(path)), nil } - if rlcp { + if rlcp && f.Destination != "" { relpath, err := filepath.Rel(prefix, path) if err != nil { // since prefix is a prefix of src a relative path should always be found diff --git a/internal/archivefiles/archivefiles_test.go b/internal/archivefiles/archivefiles_test.go index 7e07c514415..0bf098d4cad 100644 --- a/internal/archivefiles/archivefiles_test.go +++ b/internal/archivefiles/archivefiles_test.go @@ -143,7 +143,7 @@ func TestEval(t *testing.T) { }, result) }) - t.Run("rlcp is set", func(t *testing.T) { + t.Run("rlcp", func(t *testing.T) { result, err := Eval(tmpl, true, []config.File{{ Source: "./testdata/a/**/*", Destination: "foo/bar", @@ -156,6 +156,18 @@ func TestEval(t *testing.T) { }, result) }) + t.Run("rlcp empty destination", func(t *testing.T) { + result, err := Eval(tmpl, true, []config.File{{ + Source: "./testdata/a/**/*", + }}) + + require.NoError(t, err) + require.Equal(t, []config.File{ + {Source: "testdata/a/b/a.txt", Destination: "testdata/a/b/a.txt"}, + {Source: "testdata/a/b/c/d.txt", Destination: "testdata/a/b/c/d.txt"}, + }, result) + }) + t.Run("rlcp no results", func(t *testing.T) { result, err := Eval(tmpl, true, []config.File{{ Source: "./testdata/abc/**/*", diff --git a/internal/pipe/archive/archive.go b/internal/pipe/archive/archive.go index bad6f3a4475..99167c1c7d8 100644 --- a/internal/pipe/archive/archive.go +++ b/internal/pipe/archive/archive.go @@ -78,7 +78,7 @@ func (Pipe) Default(ctx *context.Context) error { if len(archive.Replacements) != 0 { deprecate.Notice(ctx, "archives.replacements") } - if !archive.RLCP { + if !archive.RLCP && archive.Format != "binary" { deprecate.NoticeCustom(ctx, "archives.rclp", "`{{ .Property }}` will be the default soon, check {{ .URL }} for more info") } ids.Inc(archive.ID) diff --git a/internal/pipe/sourcearchive/source.go b/internal/pipe/sourcearchive/source.go index 9ccbf79652d..f5e048a9cbf 100644 --- a/internal/pipe/sourcearchive/source.go +++ b/internal/pipe/sourcearchive/source.go @@ -113,7 +113,7 @@ func (Pipe) Default(ctx *context.Context) error { archive.NameTemplate = "{{ .ProjectName }}-{{ .Version }}" } - if !archive.RLCP { + if archive.Enabled && !archive.RLCP { deprecate.NoticeCustom(ctx, "source.rclp", "`{{ .Property }}` will be the default soon, check {{ .URL }} for more info") } return nil From a0ac2ca6b8f9b39188c2b1ab8712edc95639bb4d Mon Sep 17 00:00:00 2001 From: Carlos A Becker Date: Fri, 23 Dec 2022 10:16:14 -0300 Subject: [PATCH 09/12] fix: tests Signed-off-by: Carlos A Becker --- .goreleaser.yaml | 26 +++++++++++++------------- internal/pipe/archive/archive.go | 4 +++- internal/pipe/archive/archive_test.go | 2 ++ 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index a8507b53f1f..8c91fc4b5fa 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -18,8 +18,8 @@ builds: - CGO_ENABLED=0 goos: - linux - - darwin - - windows + # - darwin + # - windows goarch: - "386" - amd64 @@ -256,17 +256,17 @@ nfpms: - statically-linked-binary - changelog-file-missing-in-native-package -snapcrafts: - - name_template: '{{ .ProjectName }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' - summary: Deliver Go binaries as fast and easily as possible - description: | - GoReleaser builds Go binaries for several platforms, creates a GitHub - release and then pushes a Homebrew formula to a repository. All that - wrapped in your favorite CI. - grade: stable - confinement: classic - publish: true - +# snapcrafts: +# - name_template: '{{ .ProjectName }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' +# summary: Deliver Go binaries as fast and easily as possible +# description: | +# GoReleaser builds Go binaries for several platforms, creates a GitHub +# release and then pushes a Homebrew formula to a repository. All that +# wrapped in your favorite CI. +# grade: stable +# confinement: classic +# publish: true +# sboms: - artifacts: archive diff --git a/internal/pipe/archive/archive.go b/internal/pipe/archive/archive.go index 99167c1c7d8..51605c5e6b2 100644 --- a/internal/pipe/archive/archive.go +++ b/internal/pipe/archive/archive.go @@ -49,7 +49,9 @@ func (Pipe) String() string { func (Pipe) Default(ctx *context.Context) error { ids := ids.New("archives") if len(ctx.Config.Archives) == 0 { - ctx.Config.Archives = append(ctx.Config.Archives, config.Archive{}) + ctx.Config.Archives = append(ctx.Config.Archives, config.Archive{ + RLCP: true, + }) } for i := range ctx.Config.Archives { archive := &ctx.Config.Archives[i] diff --git a/internal/pipe/archive/archive_test.go b/internal/pipe/archive/archive_test.go index 27c7b4dc953..1e8e73a360d 100644 --- a/internal/pipe/archive/archive_test.go +++ b/internal/pipe/archive/archive_test.go @@ -771,6 +771,7 @@ func TestDefault(t *testing.T) { require.NotEmpty(t, ctx.Config.Archives[0].NameTemplate) require.Equal(t, "tar.gz", ctx.Config.Archives[0].Format) require.NotEmpty(t, ctx.Config.Archives[0].Files) + require.True(t, ctx.Config.Archives[0].RLCP) } func TestDefaultSet(t *testing.T) { @@ -791,6 +792,7 @@ func TestDefaultSet(t *testing.T) { require.NoError(t, Pipe{}.Default(ctx)) require.Equal(t, "foo", ctx.Config.Archives[0].NameTemplate) require.Equal(t, "zip", ctx.Config.Archives[0].Format) + require.False(t, ctx.Config.Archives[0].RLCP) require.Equal(t, config.File{Source: "foo"}, ctx.Config.Archives[0].Files[0]) } From 984fd2f70226959408ca82c6b0e706231dd09174 Mon Sep 17 00:00:00 2001 From: Carlos A Becker Date: Fri, 23 Dec 2022 10:31:34 -0300 Subject: [PATCH 10/12] fix: improve defaults Signed-off-by: Carlos A Becker --- .goreleaser.yaml | 27 +++++++++++++-------------- internal/pipe/archive/archive.go | 10 ++++------ internal/pipe/archive/archive_test.go | 18 +++++++++++++++++- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 8c91fc4b5fa..c6921bbc00c 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -18,8 +18,8 @@ builds: - CGO_ENABLED=0 goos: - linux - # - darwin - # - windows + - darwin + - windows goarch: - "386" - amd64 @@ -136,7 +136,6 @@ archives: {{- else if eq .Arch "386" }}i386 {{- else }}{{ .Arch }}{{ end }} {{- if .Arm }}v{{ .Arm }}{{ end }} - rlcp: true format_overrides: - goos: windows format: zip @@ -256,17 +255,17 @@ nfpms: - statically-linked-binary - changelog-file-missing-in-native-package -# snapcrafts: -# - name_template: '{{ .ProjectName }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' -# summary: Deliver Go binaries as fast and easily as possible -# description: | -# GoReleaser builds Go binaries for several platforms, creates a GitHub -# release and then pushes a Homebrew formula to a repository. All that -# wrapped in your favorite CI. -# grade: stable -# confinement: classic -# publish: true -# +snapcrafts: + - name_template: '{{ .ProjectName }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' + summary: Deliver Go binaries as fast and easily as possible + description: | + GoReleaser builds Go binaries for several platforms, creates a GitHub + release and then pushes a Homebrew formula to a repository. All that + wrapped in your favorite CI. + grade: stable + confinement: classic + publish: true + sboms: - artifacts: archive diff --git a/internal/pipe/archive/archive.go b/internal/pipe/archive/archive.go index 51605c5e6b2..12d7f1c7ffd 100644 --- a/internal/pipe/archive/archive.go +++ b/internal/pipe/archive/archive.go @@ -49,9 +49,7 @@ func (Pipe) String() string { func (Pipe) Default(ctx *context.Context) error { ids := ids.New("archives") if len(ctx.Config.Archives) == 0 { - ctx.Config.Archives = append(ctx.Config.Archives, config.Archive{ - RLCP: true, - }) + ctx.Config.Archives = append(ctx.Config.Archives, config.Archive{}) } for i := range ctx.Config.Archives { archive := &ctx.Config.Archives[i] @@ -61,6 +59,9 @@ func (Pipe) Default(ctx *context.Context) error { if archive.ID == "" { archive.ID = "default" } + if !archive.RLCP && archive.Format != "binary" && len(archive.Files) > 0 { + deprecate.NoticeCustom(ctx, "archives.rclp", "`{{ .Property }}` will be the default soon, check {{ .URL }} for more info") + } if len(archive.Files) == 0 { archive.Files = []config.File{ {Source: "license*"}, @@ -80,9 +81,6 @@ func (Pipe) Default(ctx *context.Context) error { if len(archive.Replacements) != 0 { deprecate.Notice(ctx, "archives.replacements") } - if !archive.RLCP && archive.Format != "binary" { - deprecate.NoticeCustom(ctx, "archives.rclp", "`{{ .Property }}` will be the default soon, check {{ .URL }} for more info") - } ids.Inc(archive.ID) } return ids.Validate() diff --git a/internal/pipe/archive/archive_test.go b/internal/pipe/archive/archive_test.go index 1e8e73a360d..c3dd25cc8f2 100644 --- a/internal/pipe/archive/archive_test.go +++ b/internal/pipe/archive/archive_test.go @@ -771,7 +771,7 @@ func TestDefault(t *testing.T) { require.NotEmpty(t, ctx.Config.Archives[0].NameTemplate) require.Equal(t, "tar.gz", ctx.Config.Archives[0].Format) require.NotEmpty(t, ctx.Config.Archives[0].Files) - require.True(t, ctx.Config.Archives[0].RLCP) + require.False(t, ctx.Config.Archives[0].RLCP) } func TestDefaultSet(t *testing.T) { @@ -796,6 +796,21 @@ func TestDefaultSet(t *testing.T) { require.Equal(t, config.File{Source: "foo"}, ctx.Config.Archives[0].Files[0]) } +func TestDefaultNoFiles(t *testing.T) { + ctx := &context.Context{ + Config: config.Project{ + Archives: []config.Archive{ + { + Format: "tar.gz", + }, + }, + }, + } + require.NoError(t, Pipe{}.Default(ctx)) + require.Equal(t, defaultNameTemplate, ctx.Config.Archives[0].NameTemplate) + require.False(t, ctx.Config.Archives[0].RLCP) +} + func TestDefaultFormatBinary(t *testing.T) { ctx := &context.Context{ Config: config.Project{ @@ -808,6 +823,7 @@ func TestDefaultFormatBinary(t *testing.T) { } require.NoError(t, Pipe{}.Default(ctx)) require.Equal(t, defaultBinaryNameTemplate, ctx.Config.Archives[0].NameTemplate) + require.False(t, ctx.Config.Archives[0].RLCP) } func TestFormatFor(t *testing.T) { From 51192d2bb0ec3fd577ebbf34ec4e31d3ed651b8a Mon Sep 17 00:00:00 2001 From: Carlos A Becker Date: Fri, 23 Dec 2022 10:34:19 -0300 Subject: [PATCH 11/12] docs: fix typo Signed-off-by: Carlos A Becker --- www/docs/deprecations.md | 1 + 1 file changed, 1 insertion(+) diff --git a/www/docs/deprecations.md b/www/docs/deprecations.md index 8ce95091a5f..22d392c312e 100644 --- a/www/docs/deprecations.md +++ b/www/docs/deprecations.md @@ -61,6 +61,7 @@ enable this option and test it out with === "After" ``` yaml archives: + - rlcp: true ``` From 0bb05f46d15a88355bd1f6f096e574ba45db8f8f Mon Sep 17 00:00:00 2001 From: Carlos A Becker Date: Tue, 27 Dec 2022 17:31:24 -0300 Subject: [PATCH 12/12] fix: config; warns Signed-off-by: Carlos A Becker --- .goreleaser.yaml | 1 + internal/pipe/archive/archive.go | 2 +- internal/pipe/sourcearchive/source.go | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index c6921bbc00c..46901cb95ad 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -142,6 +142,7 @@ archives: builds_info: group: root owner: root + rlcp: true files: - README.md - LICENSE.md diff --git a/internal/pipe/archive/archive.go b/internal/pipe/archive/archive.go index 12d7f1c7ffd..3d10f43bd5f 100644 --- a/internal/pipe/archive/archive.go +++ b/internal/pipe/archive/archive.go @@ -60,7 +60,7 @@ func (Pipe) Default(ctx *context.Context) error { archive.ID = "default" } if !archive.RLCP && archive.Format != "binary" && len(archive.Files) > 0 { - deprecate.NoticeCustom(ctx, "archives.rclp", "`{{ .Property }}` will be the default soon, check {{ .URL }} for more info") + deprecate.NoticeCustom(ctx, "archives.rlcp", "`{{ .Property }}` will be the default soon, check {{ .URL }} for more info") } if len(archive.Files) == 0 { archive.Files = []config.File{ diff --git a/internal/pipe/sourcearchive/source.go b/internal/pipe/sourcearchive/source.go index f5e048a9cbf..44d118a9b3c 100644 --- a/internal/pipe/sourcearchive/source.go +++ b/internal/pipe/sourcearchive/source.go @@ -114,7 +114,7 @@ func (Pipe) Default(ctx *context.Context) error { } if archive.Enabled && !archive.RLCP { - deprecate.NoticeCustom(ctx, "source.rclp", "`{{ .Property }}` will be the default soon, check {{ .URL }} for more info") + deprecate.NoticeCustom(ctx, "source.rlcp", "`{{ .Property }}` will be the default soon, check {{ .URL }} for more info") } return nil }