1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-01-10 03:47:03 +02:00
goreleaser/internal/pipe/archive/archive.go

240 lines
6.3 KiB
Go
Raw Normal View History

2017-04-14 20:39:32 +02:00
// Package archive implements the pipe interface with the intent of
// archiving and compressing the binaries, readme, and other artifacts. It
// also provides an Archive interface which represents an archiving format.
package archive
2016-12-29 02:23:39 +02:00
2016-12-29 02:53:56 +02:00
import (
"fmt"
2016-12-29 22:10:11 +02:00
"os"
2017-07-14 01:22:10 +02:00
"path/filepath"
2017-12-17 22:01:58 +02:00
"strings"
"sync"
2016-12-29 22:10:11 +02:00
2017-06-22 05:09:14 +02:00
"github.com/apex/log"
"github.com/campoy/unique"
zglob "github.com/mattn/go-zglob"
2017-12-17 19:50:09 +02:00
"golang.org/x/sync/errgroup"
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/goreleaser/goreleaser/internal/tmpl"
"github.com/goreleaser/goreleaser/pkg/archive"
"github.com/goreleaser/goreleaser/pkg/config"
"github.com/goreleaser/goreleaser/pkg/context"
2016-12-29 02:53:56 +02:00
)
2016-12-29 02:23:39 +02:00
const (
defaultNameTemplate = "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
defaultBinaryNameTemplate = "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
)
2017-12-19 01:32:31 +02:00
2018-11-11 22:17:44 +02:00
// nolint: gochecknoglobals
var lock sync.Mutex
// Pipe for archive
2018-11-11 22:17:44 +02:00
type Pipe struct{}
2016-12-30 13:27:35 +02:00
func (Pipe) String() string {
return "archives"
2016-12-30 13:27:35 +02:00
}
// Default sets the pipe defaults
func (Pipe) Default(ctx *context.Context) error {
var archive = &ctx.Config.Archive
if archive.Format == "" {
archive.Format = "tar.gz"
}
if len(archive.Files) == 0 {
archive.Files = []string{
"licence*",
"LICENCE*",
"license*",
"LICENSE*",
"readme*",
"README*",
"changelog*",
"CHANGELOG*",
}
}
if archive.NameTemplate == "" {
archive.NameTemplate = defaultNameTemplate
if archive.Format == "binary" {
archive.NameTemplate = defaultBinaryNameTemplate
}
}
return nil
}
// Run the pipe
2018-11-11 22:17:44 +02:00
func (Pipe) Run(ctx *context.Context) error {
var g errgroup.Group // TODO: use semerrgroup here
var filtered = ctx.Artifacts.Filter(artifact.ByType(artifact.Binary))
2018-10-10 18:14:12 +02:00
for group, artifacts := range filtered.GroupByPlatform() {
log.Debugf("group %s has %d binaries", group, len(artifacts))
artifacts := artifacts
g.Go(func() error {
if packageFormat(ctx, artifacts[0].Goos) == "binary" {
return skip(ctx, artifacts)
}
2018-11-11 22:17:44 +02:00
return create(ctx, artifacts)
})
}
return g.Wait()
}
2018-11-11 22:17:44 +02:00
func create(ctx *context.Context, binaries []artifact.Artifact) error {
var format = packageFormat(ctx, binaries[0].Goos)
folder, err := tmpl.New(ctx).
WithArtifact(binaries[0], ctx.Config.Archive.Replacements).
Apply(ctx.Config.Archive.NameTemplate)
2017-12-17 19:50:09 +02:00
if err != nil {
return err
}
archivePath := filepath.Join(ctx.Config.Dist, folder+"."+format)
2018-11-11 22:17:44 +02:00
lock.Lock()
if _, err = os.Stat(archivePath); !os.IsNotExist(err) {
2018-11-11 22:17:44 +02:00
lock.Unlock()
return fmt.Errorf("archive named %s already exists. Check your archive name template", archivePath)
}
2017-12-17 19:50:09 +02:00
archiveFile, err := os.Create(archivePath)
if err != nil {
2018-11-11 22:17:44 +02:00
lock.Unlock()
2017-12-17 19:50:09 +02:00
return fmt.Errorf("failed to create directory %s: %s", archivePath, err.Error())
}
2018-11-11 22:17:44 +02:00
lock.Unlock()
2017-12-18 03:04:29 +02:00
defer archiveFile.Close() // nolint: errcheck
2018-10-10 17:49:50 +02:00
var log = log.WithField("archive", archivePath)
log.Info("creating")
wrap, err := tmpl.New(ctx).
WithArtifact(binaries[0], ctx.Config.Archive.Replacements).
Apply(wrapFolder(ctx.Config.Archive))
if err != nil {
return err
2018-11-07 18:15:51 +02:00
}
var a = NewEnhancedArchive(archive.New(archiveFile), wrap)
2017-12-18 03:04:29 +02:00
defer a.Close() // nolint: errcheck
2017-12-17 19:50:09 +02:00
files, err := findFiles(ctx)
if err != nil {
return fmt.Errorf("failed to find files to archive: %s", err.Error())
}
for _, f := range files {
2018-11-07 18:15:51 +02:00
if err = a.Add(f, f); err != nil {
2017-12-17 19:50:09 +02:00
return fmt.Errorf("failed to add %s to the archive: %s", f, err.Error())
}
2017-12-17 19:50:09 +02:00
}
for _, binary := range binaries {
2018-11-07 18:15:51 +02:00
if err := a.Add(binary.Name, binary.Path); err != nil {
2017-12-17 19:50:09 +02:00
return fmt.Errorf("failed to add %s -> %s to the archive: %s", binary.Path, binary.Name, err.Error())
}
2017-05-11 05:05:51 +02:00
}
2017-12-17 19:50:09 +02:00
ctx.Artifacts.Add(artifact.Artifact{
Type: artifact.UploadableArchive,
2017-12-17 19:50:09 +02:00
Name: folder + "." + format,
Path: archivePath,
Goos: binaries[0].Goos,
Goarch: binaries[0].Goarch,
Goarm: binaries[0].Goarm,
Extra: map[string]interface{}{
"Builds": binaries,
},
2017-12-17 19:50:09 +02:00
})
2017-01-14 16:51:09 +02:00
return nil
2016-12-29 02:53:56 +02:00
}
func wrapFolder(a config.Archive) string {
switch a.WrapInDirectory {
case "true":
return a.NameTemplate
case "false":
return ""
default:
return a.WrapInDirectory
}
}
func skip(ctx *context.Context, binaries []artifact.Artifact) error {
for _, binary := range binaries {
log.WithField("binary", binary.Name).Info("skip archiving")
name, err := tmpl.New(ctx).
WithArtifact(binary, ctx.Config.Archive.Replacements).
Apply(ctx.Config.Archive.NameTemplate)
2017-12-17 22:01:58 +02:00
if err != nil {
return err
}
ctx.Artifacts.Add(artifact.Artifact{
Type: artifact.UploadableBinary,
Name: name + binary.ExtraOr("Ext", "").(string),
Path: binary.Path,
Goos: binary.Goos,
Goarch: binary.Goarch,
Goarm: binary.Goarm,
Extra: map[string]interface{}{
"Builds": []artifact.Artifact{binary},
},
})
2017-07-03 05:57:39 +02:00
}
return nil
}
2017-05-11 05:05:51 +02:00
func findFiles(ctx *context.Context) (result []string, err error) {
for _, glob := range ctx.Config.Archive.Files {
2017-05-11 17:43:04 +02:00
files, err := zglob.Glob(glob)
2017-05-11 05:05:51 +02:00
if err != nil {
return result, fmt.Errorf("globbing failed for pattern %s: %s", glob, err.Error())
2017-05-11 05:05:51 +02:00
}
result = append(result, files...)
}
// remove duplicates
unique.Slice(&result, func(i, j int) bool {
return strings.Compare(result[i], result[j]) < 0
})
2017-05-11 05:05:51 +02:00
return
}
func packageFormat(ctx *context.Context, platform string) string {
for _, override := range ctx.Config.Archive.FormatOverrides {
if strings.HasPrefix(platform, override.Goos) {
return override.Format
}
}
return ctx.Config.Archive.Format
}
2018-11-07 18:15:51 +02:00
// NewEnhancedArchive enhances a pre-existing archive.Archive instance
// with this pipe specifics.
func NewEnhancedArchive(a archive.Archive, wrap string) archive.Archive {
return EnhancedArchive{
a: a,
wrap: wrap,
files: map[string]string{},
}
}
2018-11-07 18:15:51 +02:00
// EnhancedArchive is an archive.Archive implementation which decorates an
// archive.Archive adding wrap directory support, logging and windows
// backslash fixes.
type EnhancedArchive struct {
a archive.Archive
wrap string
files map[string]string
2018-11-07 18:15:51 +02:00
}
// Add adds a file
func (d EnhancedArchive) Add(name, path string) error {
name = strings.Replace(filepath.Join(d.wrap, name), "\\", "/", -1)
log.Debugf("adding file: %s as %s", path, name)
if _, ok := d.files[name]; ok {
return fmt.Errorf("file %s already exists in the archive", name)
}
d.files[name] = path
2018-11-07 18:15:51 +02:00
return d.a.Add(name, path)
}
// Close closes the underlying archive
2018-11-07 18:15:51 +02:00
func (d EnhancedArchive) Close() error {
return d.a.Close()
}