mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-03-17 20:47:50 +02:00
fix: use git-archive under the hood (#3904)
This reverts back to using `git archive` for the source archives... but will keep supporting extra files. ##### How it works: Basically, we run `git archive` as before. Then, we make a backup of the generated archive, and create a new one copying by reading from the backup and writing into the new one. Finally, we write the extra files to the new one as well. This only happens if the configuration does have extra files, otherwise, just the simple `git archive` will be run. PS: we can't just append to the archive because weird tar format paddings et al. --------- Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Signed-off-by: Carlos A Becker <caarlos0@users.noreply.github.com>
This commit is contained in:
parent
57d3bdd965
commit
0eb3e7975c
@ -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,
|
||||
|
@ -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"),
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
86
internal/testlib/archive.go
Normal file
86
internal/testlib/archive.go
Normal file
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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"))
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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"`
|
||||
|
@ -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.
|
||||
|
@ -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'
|
||||
|
Loading…
x
Reference in New Issue
Block a user