diff --git a/internal/pipe/archive/archive.go b/internal/pipe/archive/archive.go index 982c8aa08..39be864b2 100644 --- a/internal/pipe/archive/archive.go +++ b/internal/pipe/archive/archive.go @@ -317,10 +317,6 @@ type EnhancedArchive struct { func (d EnhancedArchive) Add(f config.File) error { name := strings.ReplaceAll(filepath.Join(d.wrap, f.Destination), "\\", "/") log.Debugf("adding file: %s as %s", f.Source, name) - if _, ok := d.files[f.Destination]; ok { - return fmt.Errorf("file %s already exists in the archive", f.Destination) - } - d.files[f.Destination] = name ff := config.File{ Source: f.Source, Destination: name, diff --git a/internal/pipe/archive/archive_meta_test.go b/internal/pipe/archive/archive_meta_test.go index d2d1d5102..cb7244a72 100644 --- a/internal/pipe/archive/archive_meta_test.go +++ b/internal/pipe/archive/archive_meta_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/goreleaser/goreleaser/internal/testctx" + "github.com/goreleaser/goreleaser/internal/testlib" "github.com/goreleaser/goreleaser/pkg/config" "github.com/stretchr/testify/require" ) @@ -30,7 +31,7 @@ func TestMeta(t *testing.T) { require.Equal( t, []string{"testdata/a/a.txt", "testdata/a/b/a.txt", "testdata/a/b/c/d.txt"}, - tarFiles(t, filepath.Join(dist, "foo.tar.gz")), + testlib.LsArchive(t, filepath.Join(dist, "foo.tar.gz"), "tar.gz"), ) }) diff --git a/internal/pipe/archive/archive_test.go b/internal/pipe/archive/archive_test.go index 5b9a6d228..93169b751 100644 --- a/internal/pipe/archive/archive_test.go +++ b/internal/pipe/archive/archive_test.go @@ -2,10 +2,10 @@ package archive import ( "archive/tar" - "archive/zip" "compress/gzip" "fmt" "io" + "io/fs" "os" "path/filepath" "testing" @@ -235,7 +235,7 @@ func TestRunPipe(t *testing.T) { "foo/bar/foobar/blah.txt", expectBin, }, - tarFiles(t, filepath.Join(dist, name)), + testlib.LsArchive(t, filepath.Join(dist, name), "tar.gz"), ) header := tarInfo(t, filepath.Join(dist, name), expectBin) @@ -252,7 +252,7 @@ func TestRunPipe(t *testing.T) { "foo/bar/foobar/blah.txt", expectBin + ".exe", }, - zipFiles(t, filepath.Join(dist, "foobar_0.0.1_windows_amd64.zip")), + testlib.LsArchive(t, filepath.Join(dist, "foobar_0.0.1_windows_amd64.zip"), "zip"), ) } }) @@ -355,21 +355,6 @@ func TestRunPipeNoBinaries(t *testing.T) { require.NoError(t, Pipe{}.Run(ctx)) } -func zipFiles(t *testing.T, path string) []string { - t.Helper() - f, err := os.Open(path) - require.NoError(t, err) - info, err := f.Stat() - require.NoError(t, err) - r, err := zip.NewReader(f, info.Size()) - require.NoError(t, err) - paths := make([]string, len(r.File)) - for i, zf := range r.File { - paths[i] = zf.Name - } - return paths -} - func tarInfo(t *testing.T, path, name string) *tar.Header { t.Helper() f, err := os.Open(path) @@ -391,27 +376,6 @@ func tarInfo(t *testing.T, path, name string) *tar.Header { return nil } -func tarFiles(t *testing.T, path string) []string { - t.Helper() - f, err := os.Open(path) - require.NoError(t, err) - defer f.Close() - gr, err := gzip.NewReader(f) - require.NoError(t, err) - defer gr.Close() - r := tar.NewReader(gr) - var paths []string - for { - next, err := r.Next() - if err == io.EOF { - break - } - require.NoError(t, err) - paths = append(paths, next.Name) - } - return paths -} - func TestRunPipeBinary(t *testing.T) { folder := testlib.Mktmp(t) dist := filepath.Join(folder, "dist") @@ -742,22 +706,11 @@ func TestRunPipeWrap(t *testing.T) { require.Len(t, archives, 1) require.Equal(t, "foo_macOS", artifact.ExtraOr(*archives[0], artifact.ExtraWrappedIn, "")) - // Check archive contents - f, err = os.Open(filepath.Join(dist, "foo.tar.gz")) - require.NoError(t, err) - defer func() { require.NoError(t, f.Close()) }() - gr, err := gzip.NewReader(f) - require.NoError(t, err) - defer func() { require.NoError(t, gr.Close()) }() - r := tar.NewReader(gr) - for _, n := range []string{"README.md", "mybin"} { - h, err := r.Next() - if err == io.EOF { - break - } - require.NoError(t, err) - require.Equal(t, filepath.Join("foo_macOS", n), h.Name) - } + require.ElementsMatch( + t, + []string{"foo_macOS/README.md", "foo_macOS/mybin"}, + testlib.LsArchive(t, filepath.Join(dist, "foo.tar.gz"), "tar.gz"), + ) } func TestDefault(t *testing.T) { @@ -999,10 +952,10 @@ func TestDuplicateFilesInsideArchive(t *testing.T) { Source: ff.Name(), Destination: "foo", })) - require.EqualError(t, a.Add(config.File{ + require.ErrorIs(t, a.Add(config.File{ Source: ff.Name(), Destination: "foo", - }), "file foo already exists in the archive") + }), fs.ErrExist) } func TestWrapInDirectory(t *testing.T) { @@ -1068,7 +1021,7 @@ func TestArchive_globbing(t *testing.T) { }) require.NoError(t, Pipe{}.Run(ctx)) - require.Equal(t, append(expected, "foobin"), tarFiles(t, filepath.Join(dist, "foo.tar.gz"))) + require.Equal(t, append(expected, "foobin"), testlib.LsArchive(t, filepath.Join(dist, "foo.tar.gz"), "tar.gz")) } t.Run("exact src file", func(t *testing.T) { diff --git a/internal/pipe/sourcearchive/source.go b/internal/pipe/sourcearchive/source.go index 44d118a9b..bb839c3c0 100644 --- a/internal/pipe/sourcearchive/source.go +++ b/internal/pipe/sourcearchive/source.go @@ -5,16 +5,15 @@ import ( "fmt" "os" "path/filepath" - "strings" "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/gio" "github.com/goreleaser/goreleaser/internal/git" "github.com/goreleaser/goreleaser/internal/tmpl" "github.com/goreleaser/goreleaser/pkg/archive" - "github.com/goreleaser/goreleaser/pkg/config" "github.com/goreleaser/goreleaser/pkg/context" ) @@ -30,49 +29,70 @@ func (Pipe) Skip(ctx *context.Context) bool { } // Run the pipe. -func (Pipe) Run(ctx *context.Context) (err error) { +func (Pipe) Run(ctx *context.Context) error { + format := ctx.Config.Source.Format + if format != "zip" && format != "tar" && format != "tgz" && format != "tar.gz" { + return fmt.Errorf("invalid source archive format: %s", format) + } name, err := tmpl.New(ctx).Apply(ctx.Config.Source.NameTemplate) if err != nil { return err } - filename := name + "." + ctx.Config.Source.Format + filename := name + "." + format path := filepath.Join(ctx.Config.Dist, filename) log.WithField("file", filename).Info("creating source archive") - - out, err := git.Run(ctx, "ls-files") - if err != nil { - return fmt.Errorf("could not list source files: %w", err) + args := []string{ + "archive", + "-o", path, } - prefix, err := tmpl.New(ctx).Apply(ctx.Config.Source.PrefixTemplate) - if err != nil { + prefix := "" + if ctx.Config.Source.PrefixTemplate != "" { + pt, err := tmpl.New(ctx).Apply(ctx.Config.Source.PrefixTemplate) + if err != nil { + return err + } + prefix = pt + args = append(args, "--prefix", prefix) + } + args = append(args, ctx.Git.FullCommit) + + if _, err := git.Clean(git.Run(ctx, args...)); err != nil { return err } - af, err := os.Create(path) + if len(ctx.Config.Source.Files) == 0 { + return nil + } + + oldPath := path + ".bkp" + if err := gio.Copy(path, oldPath); err != nil { + return fmt.Errorf("failed make a backup of %q: %w", path, err) + } + + // i could spend a lot of time trying to figure out how to append to a tar, + // tgz and zip file... but... this seems easy enough :) + of, err := os.Open(oldPath) if err != nil { - return fmt.Errorf("could not create archive: %w", err) + return fmt.Errorf("could not open %q: %w", oldPath, err) + } + defer of.Close() + + af, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0o644) + if err != nil { + return fmt.Errorf("could not open archive: %w", err) } defer af.Close() //nolint:errcheck - arch, err := archive.New(af, ctx.Config.Source.Format) + arch, err := archive.Copying(of, af, format) if err != nil { return err } - var ff []config.File - for _, f := range strings.Split(out, "\n") { - if strings.TrimSpace(f) == "" { - continue - } - ff = append(ff, config.File{ - Source: f, - }) - } files, err := archivefiles.Eval( tmpl.New(ctx), ctx.Config.Source.RLCP, - append(ff, ctx.Config.Source.Files...), + ctx.Config.Source.Files, ) if err != nil { return err @@ -96,7 +116,7 @@ func (Pipe) Run(ctx *context.Context) (err error) { Name: filename, Path: path, Extra: map[string]interface{}{ - artifact.ExtraFormat: ctx.Config.Source.Format, + artifact.ExtraFormat: format, }, }) return err diff --git a/internal/pipe/sourcearchive/source_test.go b/internal/pipe/sourcearchive/source_test.go index 6312b78f4..7111810b7 100644 --- a/internal/pipe/sourcearchive/source_test.go +++ b/internal/pipe/sourcearchive/source_test.go @@ -1,7 +1,6 @@ package sourcearchive import ( - "archive/zip" "os" "path/filepath" "testing" @@ -21,13 +20,14 @@ func TestArchive(t *testing.T) { require.NoError(t, os.Mkdir("dist", 0o744)) testlib.GitInit(t) - require.NoError(t, os.WriteFile("code.txt", []byte("not really code"), 0o655)) + require.NoError(t, os.WriteFile("code.rb", []byte("not really code"), 0o655)) require.NoError(t, os.WriteFile("code.py", []byte("print 1"), 0o655)) require.NoError(t, os.WriteFile("README.md", []byte("# my dope fake project"), 0o655)) testlib.GitAdd(t) testlib.GitCommit(t, "feat: first") require.NoError(t, os.WriteFile("added-later.txt", []byte("this file was added later"), 0o655)) require.NoError(t, os.WriteFile("ignored.md", []byte("never added"), 0o655)) + require.NoError(t, os.WriteFile("code.txt", []byte("not really code"), 0o655)) require.NoError(t, os.MkdirAll("subfolder", 0o755)) require.NoError(t, os.WriteFile("subfolder/file.md", []byte("a file within a folder, added later"), 0o655)) @@ -67,17 +67,22 @@ func TestArchive(t *testing.T) { require.NoError(t, err) require.Greater(t, stat.Size(), int64(100)) - if format != "zip" { - return - } - - require.ElementsMatch(t, []string{ + expected := []string{ + "foo-1.0.0/", "foo-1.0.0/README.md", "foo-1.0.0/code.py", + "foo-1.0.0/code.rb", "foo-1.0.0/code.txt", "foo-1.0.0/added-later.txt", "foo-1.0.0/subfolder/file.md", - }, lsZip(t, path)) + } + + // zips wont have the parent dir + if format == "zip" { + expected = expected[1:] + } + + require.ElementsMatch(t, expected, testlib.LsArchive(t, path, format)) }) } } @@ -93,7 +98,7 @@ func TestInvalidFormat(t *testing.T) { }, }) require.NoError(t, Pipe{}.Default(ctx)) - require.EqualError(t, Pipe{}.Run(ctx), "invalid archive format: 7z") + require.EqualError(t, Pipe{}.Run(ctx), "invalid source archive format: 7z") } func TestDefault(t *testing.T) { @@ -112,6 +117,7 @@ func TestInvalidNameTemplate(t *testing.T) { NameTemplate: "{{ .foo }-asdda", }, }) + require.NoError(t, Pipe{}.Default(ctx)) testlib.RequireTemplateError(t, Pipe{}.Run(ctx)) } @@ -175,20 +181,3 @@ func TestSkip(t *testing.T) { func TestString(t *testing.T) { require.NotEmpty(t, Pipe{}.String()) } - -func lsZip(tb testing.TB, path string) []string { - tb.Helper() - - stat, err := os.Stat(path) - require.NoError(tb, err) - f, err := os.Open(path) - require.NoError(tb, err) - z, err := zip.NewReader(f, stat.Size()) - require.NoError(tb, err) - - var paths []string - for _, zf := range z.File { - paths = append(paths, zf.Name) - } - return paths -} diff --git a/internal/testlib/archive.go b/internal/testlib/archive.go new file mode 100644 index 000000000..b7665f951 --- /dev/null +++ b/internal/testlib/archive.go @@ -0,0 +1,86 @@ +package testlib + +import ( + "archive/tar" + "archive/zip" + "compress/gzip" + "io" + "os" + "testing" + + "github.com/stretchr/testify/require" + "github.com/ulikunitz/xz" +) + +// LsArchive return the file list of a given archive in a given formatkj +func LsArchive(tb testing.TB, path, format string) []string { + tb.Helper() + f := openFile(tb, path) + switch format { + case "tar.gz", "tgz": + return doLsTar(openGzip(tb, f)) + case "tar.xz", "txz": + return doLsTar(openXz(tb, f)) + case "tar": + return doLsTar(f) + case "zip": + return lsZip(tb, f) + case "gz": + return []string{openGzip(tb, f).Header.Name} + default: + tb.Errorf("invalid format: %s", format) + return nil + } +} + +func openGzip(tb testing.TB, r io.Reader) *gzip.Reader { + tb.Helper() + gz, err := gzip.NewReader(r) + require.NoError(tb, err) + return gz +} + +func openXz(tb testing.TB, r io.Reader) *xz.Reader { + tb.Helper() + xz, err := xz.NewReader(r) + require.NoError(tb, err) + return xz +} + +func lsZip(tb testing.TB, f *os.File) []string { + tb.Helper() + + stat, err := f.Stat() + require.NoError(tb, err) + z, err := zip.NewReader(f, stat.Size()) + require.NoError(tb, err) + + var paths []string + for _, zf := range z.File { + paths = append(paths, zf.Name) + } + return paths +} + +func doLsTar(f io.Reader) []string { + z := tar.NewReader(f) + var paths []string + for { + h, err := z.Next() + if h == nil || err == io.EOF { + break + } + if h.Format == tar.FormatPAX { + continue + } + paths = append(paths, h.Name) + } + return paths +} + +func openFile(tb testing.TB, path string) *os.File { + tb.Helper() + f, err := os.Open(path) + require.NoError(tb, err) + return f +} diff --git a/pkg/archive/archive.go b/pkg/archive/archive.go index 1ff3ef064..189eebe89 100644 --- a/pkg/archive/archive.go +++ b/pkg/archive/archive.go @@ -4,6 +4,7 @@ package archive import ( "fmt" "io" + "os" "github.com/goreleaser/goreleaser/pkg/archive/gzip" "github.com/goreleaser/goreleaser/pkg/archive/tar" @@ -35,3 +36,17 @@ func New(w io.Writer, format string) (Archive, error) { } return nil, fmt.Errorf("invalid archive format: %s", format) } + +// Copying copies the source archive into a new one, which can be appended at. +// Source needs to be in the specified format. +func Copying(r *os.File, w io.Writer, format string) (Archive, error) { + switch format { + case "tar.gz", "tgz": + return targz.Copying(r, w) + case "tar": + return tar.Copying(r, w) + case "zip": + return zip.Copying(r, w) + } + return nil, fmt.Errorf("invalid archive format: %s", format) +} diff --git a/pkg/archive/archive_test.go b/pkg/archive/archive_test.go index 651819d22..340e9a10e 100644 --- a/pkg/archive/archive_test.go +++ b/pkg/archive/archive_test.go @@ -3,8 +3,10 @@ package archive import ( "io" "os" + "path/filepath" "testing" + "github.com/goreleaser/goreleaser/internal/testlib" "github.com/goreleaser/goreleaser/pkg/config" "github.com/stretchr/testify/require" ) @@ -19,11 +21,11 @@ func TestArchive(t *testing.T) { for _, format := range []string{"tar.gz", "zip", "gz", "tar.xz", "tar"} { format := format t.Run(format, func(t *testing.T) { - archive, err := New(io.Discard, format) + f1, err := os.Create(filepath.Join(t.TempDir(), "1.tar")) + require.NoError(t, err) + + archive, err := New(f1, format) require.NoError(t, err) - t.Cleanup(func() { - require.NoError(t, archive.Close()) - }) require.NoError(t, archive.Add(config.File{ Source: empty.Name(), Destination: "empty.txt", @@ -32,6 +34,31 @@ func TestArchive(t *testing.T) { Source: empty.Name() + "_nope", Destination: "dont.txt", })) + require.NoError(t, archive.Close()) + require.NoError(t, f1.Close()) + + if format == "tar.xz" || format == "gz" { + _, err := Copying(f1, io.Discard, format) + require.Error(t, err) + return + } + + f1, err = os.Open(f1.Name()) + require.NoError(t, err) + f2, err := os.Create(filepath.Join(t.TempDir(), "2.tar")) + require.NoError(t, err) + + a, err := Copying(f1, f2, format) + require.NoError(t, err) + require.NoError(t, a.Add(config.File{ + Source: empty.Name(), + Destination: "added_later.txt", + })) + require.NoError(t, a.Close()) + require.NoError(t, f1.Close()) + require.NoError(t, f2.Close()) + + require.Equal(t, []string{"empty.txt", "added_later.txt"}, testlib.LsArchive(t, f2.Name(), format)) }) } diff --git a/pkg/archive/tar/tar.go b/pkg/archive/tar/tar.go index a6c5c8b3b..e3a23ee43 100644 --- a/pkg/archive/tar/tar.go +++ b/pkg/archive/tar/tar.go @@ -5,6 +5,7 @@ import ( "archive/tar" "fmt" "io" + "io/fs" "os" "github.com/goreleaser/goreleaser/pkg/config" @@ -12,16 +13,39 @@ import ( // Archive as tar. type Archive struct { - tw *tar.Writer + tw *tar.Writer + files map[string]bool } // New tar archive. func New(target io.Writer) Archive { return Archive{ - tw: tar.NewWriter(target), + tw: tar.NewWriter(target), + files: map[string]bool{}, } } +// Copying creates a new tar with the contents of the given tar. +func Copying(source io.Reader, target io.Writer) (Archive, error) { + w := New(target) + r := tar.NewReader(source) + for { + h, err := r.Next() + if err == io.EOF || h == nil { + break + } + + w.files[h.Name] = true + if err := w.tw.WriteHeader(h); err != nil { + return w, err + } + if _, err := io.Copy(w.tw, r); err != nil { + return w, err + } + } + return w, nil +} + // Close all closeables. func (a Archive) Close() error { return a.tw.Close() @@ -29,6 +53,10 @@ func (a Archive) Close() error { // Add file to the archive. func (a Archive) Add(f config.File) error { + if _, ok := a.files[f.Destination]; ok { + return &fs.PathError{Err: fs.ErrExist, Path: f.Destination, Op: "add"} + } + a.files[f.Destination] = true info, err := os.Lstat(f.Source) // #nosec if err != nil { return fmt.Errorf("%s: %w", f.Source, err) diff --git a/pkg/archive/tar/tar_test.go b/pkg/archive/tar/tar_test.go index 1222324f5..221cd6c48 100644 --- a/pkg/archive/tar/tar_test.go +++ b/pkg/archive/tar/tar_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + "github.com/goreleaser/goreleaser/internal/testlib" "github.com/goreleaser/goreleaser/pkg/config" "github.com/stretchr/testify/require" ) @@ -58,6 +59,11 @@ func TestTarFile(t *testing.T) { Destination: "link.txt", })) + require.ErrorIs(t, archive.Add(config.File{ + Source: "../testdata/regular.txt", + Destination: "link.txt", + }), fs.ErrExist) + require.NoError(t, archive.Close()) require.Error(t, archive.Add(config.File{ Source: "tar.go", @@ -158,3 +164,34 @@ func TestTarInvalidLink(t *testing.T) { Destination: "badlink.txt", })) } + +func TestCopying(t *testing.T) { + f1, err := os.Create(filepath.Join(t.TempDir(), "1.tar")) + require.NoError(t, err) + f2, err := os.Create(filepath.Join(t.TempDir(), "2.tar")) + require.NoError(t, err) + + t1 := New(f1) + require.NoError(t, t1.Add(config.File{ + Source: "../testdata/foo.txt", + Destination: "foo.txt", + })) + require.NoError(t, t1.Close()) + require.NoError(t, f1.Close()) + + f1, err = os.Open(f1.Name()) + require.NoError(t, err) + + t2, err := Copying(f1, f2) + require.NoError(t, err) + require.NoError(t, t2.Add(config.File{ + Source: "../testdata/sub1/executable", + Destination: "executable", + })) + require.NoError(t, t2.Close()) + require.NoError(t, f2.Close()) + require.NoError(t, f1.Close()) + + require.Equal(t, []string{"foo.txt"}, testlib.LsArchive(t, f1.Name(), "tar")) + require.Equal(t, []string{"foo.txt", "executable"}, testlib.LsArchive(t, f2.Name(), "tar")) +} diff --git a/pkg/archive/targz/targz.go b/pkg/archive/targz/targz.go index 983cf529c..160c317ec 100644 --- a/pkg/archive/targz/targz.go +++ b/pkg/archive/targz/targz.go @@ -27,6 +27,20 @@ func New(target io.Writer) Archive { } } +func Copying(source io.Reader, target io.Writer) (Archive, error) { + // the error will be nil since the compression level is valid + gw, _ := gzip.NewWriterLevel(target, gzip.BestCompression) + srcgz, err := gzip.NewReader(source) + if err != nil { + return Archive{}, err + } + tw, err := tar.Copying(srcgz, gw) + return Archive{ + gw: gw, + tw: &tw, + }, err +} + // Close all closeables. func (a Archive) Close() error { if err := a.tw.Close(); err != nil { diff --git a/pkg/archive/zip/zip.go b/pkg/archive/zip/zip.go index cd6513ea9..2d9fbfc78 100644 --- a/pkg/archive/zip/zip.go +++ b/pkg/archive/zip/zip.go @@ -7,6 +7,7 @@ import ( "compress/flate" "fmt" "io" + "io/fs" "os" "path/filepath" @@ -15,7 +16,8 @@ import ( // Archive zip struct. type Archive struct { - z *zip.Writer + z *zip.Writer + files map[string]bool } // New zip archive. @@ -25,10 +27,51 @@ func New(target io.Writer) Archive { return flate.NewWriter(out, flate.BestCompression) }) return Archive{ - z: compressor, + z: compressor, + files: map[string]bool{}, } } +// New zip archive. +func Copying(source *os.File, target io.Writer) (Archive, error) { + info, err := source.Stat() + if err != nil { + return Archive{}, err + } + r, err := zip.NewReader(source, info.Size()) + if err != nil { + return Archive{}, err + } + w := New(target) + for _, zf := range r.File { + if zf.Mode().IsDir() { + continue + } + w.files[zf.Name] = true + hdr := zip.FileHeader{ + Name: zf.Name, + UncompressedSize64: zf.UncompressedSize64, + UncompressedSize: zf.UncompressedSize, + CreatorVersion: zf.CreatorVersion, + ExternalAttrs: zf.ExternalAttrs, + } + ww, err := w.z.CreateHeader(&hdr) + if err != nil { + return Archive{}, fmt.Errorf("creating %q header in target: %w", zf.Name, err) + } + rr, err := zf.Open() + if err != nil { + return Archive{}, fmt.Errorf("opening %q from source: %w", zf.Name, err) + } + defer rr.Close() + if _, err = io.Copy(ww, rr); err != nil { + return Archive{}, fmt.Errorf("copy from %q source to target: %w", zf.Name, err) + } + _ = rr.Close() + } + return w, nil +} + // Close all closeables. func (a Archive) Close() error { return a.z.Close() @@ -36,6 +79,10 @@ func (a Archive) Close() error { // Add a file to the zip archive. func (a Archive) Add(f config.File) error { + if _, ok := a.files[f.Destination]; ok { + return &fs.PathError{Err: fs.ErrExist, Path: f.Destination, Op: "add"} + } + a.files[f.Destination] = true info, err := os.Lstat(f.Source) // #nosec if err != nil { return err diff --git a/pkg/archive/zip/zip_test.go b/pkg/archive/zip/zip_test.go index 8f04d6442..37366008f 100644 --- a/pkg/archive/zip/zip_test.go +++ b/pkg/archive/zip/zip_test.go @@ -59,6 +59,11 @@ func TestZipFile(t *testing.T) { Destination: "link.txt", })) + require.ErrorIs(t, archive.Add(config.File{ + Source: "../testdata/regular.txt", + Destination: "link.txt", + }), fs.ErrExist) + require.NoError(t, archive.Close()) require.Error(t, archive.Add(config.File{ Source: "tar.go", diff --git a/pkg/config/config.go b/pkg/config/config.go index 278fcd398..0e10be13c 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -933,7 +933,7 @@ type Publisher struct { // Source configuration. type Source struct { NameTemplate string `yaml:"name_template,omitempty" json:"name_template,omitempty"` - Format string `yaml:"format,omitempty" json:"format,omitempty"` + Format string `yaml:"format,omitempty" json:"format,omitempty" jsonschema:"enum=tar,enum=tgz,enum=tar.gz,enum=zip,default=tar.gz"` 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"` diff --git a/www/docs/customization/archive.md b/www/docs/customization/archive.md index 416998fe3..6b83284eb 100644 --- a/www/docs/customization/archive.md +++ b/www/docs/customization/archive.md @@ -139,10 +139,17 @@ archives: # # Default: copied from the source file 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 # Before and after hooks for each archive. diff --git a/www/docs/customization/source.md b/www/docs/customization/source.md index 8e4066e15..d28699416 100644 --- a/www/docs/customization/source.md +++ b/www/docs/customization/source.md @@ -16,7 +16,8 @@ source: name_template: '{{ .ProjectName }}' # Format of the archive. - # Any format git-archive supports, this supports too. + # + # Valid formats are: tar, tgz, tar.gz, and zip. # # Default: 'tar.gz' format: 'tar'