2017-04-14 15:39:32 -03: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.
2017-01-14 19:47:15 -02:00
package archive
2016-12-28 22:23:39 -02:00
2016-12-28 22:53:56 -02:00
import (
2020-10-10 08:46:16 -03:00
"errors"
2017-09-09 11:10:08 +02:00
"fmt"
2022-09-11 15:32:23 -03:00
"io/fs"
2016-12-29 18:10:11 -02:00
"os"
2017-07-13 20:22:10 -03:00
"path/filepath"
2017-12-17 18:01:58 -02:00
"strings"
2018-11-11 12:11:03 -02:00
"sync"
2016-12-29 18:10:11 -02:00
2022-06-21 21:11:15 -03:00
"github.com/caarlos0/log"
2022-05-11 22:56:25 -03:00
"github.com/goreleaser/goreleaser/internal/archivefiles"
2017-12-17 15:50:09 -02:00
"github.com/goreleaser/goreleaser/internal/artifact"
2019-05-07 07:18:35 -03:00
"github.com/goreleaser/goreleaser/internal/ids"
2019-04-16 10:19:15 -03:00
"github.com/goreleaser/goreleaser/internal/semerrgroup"
2018-07-08 20:47:30 -07:00
"github.com/goreleaser/goreleaser/internal/tmpl"
2018-08-05 10:23:39 -03:00
"github.com/goreleaser/goreleaser/pkg/archive"
2018-11-13 13:48:16 -02:00
"github.com/goreleaser/goreleaser/pkg/config"
2018-08-14 23:50:20 -03:00
"github.com/goreleaser/goreleaser/pkg/context"
2016-12-28 22:53:56 -02:00
)
2016-12-28 22:23:39 -02:00
2017-12-26 21:19:58 -02:00
const (
2022-04-13 21:29:39 -03:00
defaultNameTemplateSuffix = ` {{ .Version }} _ {{ .Os }} _ {{ .Arch }} {{ with .Arm }} v {{ . }} {{ end }} {{ with .Mips }} _ {{ . }} {{ end }} {{ if not ( eq .Amd64 "v1" ) }} {{ .Amd64 }} {{ end }} `
2022-04-11 22:43:22 -03:00
defaultNameTemplate = "{{ .ProjectName }}_" + defaultNameTemplateSuffix
defaultBinaryNameTemplate = "{{ .Binary }}_" + defaultNameTemplateSuffix
2017-12-26 21:19:58 -02:00
)
2017-12-18 21:32:31 -02:00
2020-10-10 08:46:16 -03:00
// ErrArchiveDifferentBinaryCount happens when an archive uses several builds which have different goos/goarch/etc sets,
// causing the archives for some platforms to have more binaries than others.
// GoReleaser breaks in these cases as it will only cause confusion to other users.
2022-09-17 00:14:59 -03:00
var ErrArchiveDifferentBinaryCount = errors . New ( "archive has different count of binaries for each platform, which may cause your users confusion.\nLearn more at https://goreleaser.com/errors/multiple-binaries-archive\n" ) // nolint:revive
2020-10-10 08:46:16 -03:00
2018-11-11 18:17:44 -02:00
// nolint: gochecknoglobals
var lock sync . Mutex
2020-05-26 00:48:10 -03:00
// Pipe for archive.
2018-11-11 18:17:44 -02:00
type Pipe struct { }
2016-12-30 09:27:35 -02:00
2017-12-02 19:53:19 -02:00
func ( Pipe ) String ( ) string {
2018-10-26 14:27:41 -06:00
return "archives"
2016-12-30 09:27:35 -02:00
}
2020-05-26 00:48:10 -03:00
// Default sets the pipe defaults.
2017-12-02 19:53:19 -02:00
func ( Pipe ) Default ( ctx * context . Context ) error {
2021-04-25 14:20:49 -03:00
ids := ids . New ( "archives" )
2019-04-16 10:19:15 -03:00
if len ( ctx . Config . Archives ) == 0 {
2019-12-29 15:02:15 -03:00
ctx . Config . Archives = append ( ctx . Config . Archives , config . Archive { } )
2017-12-02 19:53:19 -02:00
}
2019-04-16 10:19:15 -03:00
for i := range ctx . Config . Archives {
2021-04-25 14:20:49 -03:00
archive := & ctx . Config . Archives [ i ]
2019-04-16 10:19:15 -03:00
if archive . Format == "" {
archive . Format = "tar.gz"
}
if archive . ID == "" {
archive . ID = "default"
}
if len ( archive . Files ) == 0 {
2021-07-21 22:09:02 -03:00
archive . Files = [ ] config . File {
{ Source : "license*" } ,
{ Source : "LICENSE*" } ,
{ Source : "readme*" } ,
{ Source : "README*" } ,
{ Source : "changelog*" } ,
{ Source : "CHANGELOG*" } ,
2019-04-16 10:19:15 -03:00
}
}
if archive . NameTemplate == "" {
archive . NameTemplate = defaultNameTemplate
if archive . Format == "binary" {
archive . NameTemplate = defaultBinaryNameTemplate
}
}
2019-05-07 07:18:35 -03:00
ids . Inc ( archive . ID )
2017-12-26 21:19:58 -02:00
}
2019-05-07 07:18:35 -03:00
return ids . Validate ( )
2017-12-02 19:53:19 -02:00
}
2020-05-26 00:48:10 -03:00
// Run the pipe.
2018-11-11 18:17:44 -02:00
func ( Pipe ) Run ( ctx * context . Context ) error {
2021-04-25 14:20:49 -03:00
g := semerrgroup . New ( ctx . Parallelism )
2020-10-10 08:46:16 -03:00
for i , archive := range ctx . Config . Archives {
2019-04-16 10:19:15 -03:00
archive := archive
2022-05-10 09:17:30 -03:00
if archive . Meta {
return createMeta ( ctx , archive )
}
2022-04-19 22:49:08 -03:00
filter := [ ] artifact . Filter { artifact . Or (
artifact . ByType ( artifact . Binary ) ,
artifact . ByType ( artifact . UniversalBinary ) ,
2022-11-12 03:35:51 +01:00
artifact . ByType ( artifact . Header ) ,
artifact . ByType ( artifact . CArchive ) ,
artifact . ByType ( artifact . CShared ) ,
2022-04-19 22:49:08 -03:00
) }
if len ( archive . Builds ) > 0 {
filter = append ( filter , artifact . ByIDs ( archive . Builds ... ) )
}
artifacts := ctx . Artifacts . Filter ( artifact . And ( filter ... ) ) . GroupByPlatform ( )
2022-09-16 23:47:06 -03:00
if err := checkArtifacts ( artifacts ) ; err != nil && archive . Format != "binary" && ! archive . AllowDifferentBinaryCount {
2020-10-10 08:46:16 -03:00
return fmt . Errorf ( "invalid archive: %d: %w" , i , ErrArchiveDifferentBinaryCount )
}
for group , artifacts := range artifacts {
2019-04-16 10:19:15 -03:00
log . Debugf ( "group %s has %d binaries" , group , len ( artifacts ) )
artifacts := artifacts
2022-08-01 15:58:39 -03:00
if packageFormat ( archive , artifacts [ 0 ] . Goos ) == "binary" {
g . Go ( func ( ) error {
2019-04-16 10:19:15 -03:00
return skip ( ctx , archive , artifacts )
2022-08-01 15:58:39 -03:00
} )
continue
}
g . Go ( func ( ) error {
if err := create ( ctx , archive , artifacts ) ; err != nil {
return err
2019-04-16 10:19:15 -03:00
}
2022-08-01 15:58:39 -03:00
return nil
2019-04-16 10:19:15 -03:00
} )
}
2017-12-26 21:19:58 -02:00
}
return g . Wait ( )
}
2020-10-10 08:46:16 -03:00
func checkArtifacts ( artifacts map [ string ] [ ] * artifact . Artifact ) error {
2021-04-25 14:20:49 -03:00
lens := map [ int ] bool { }
2020-10-10 08:46:16 -03:00
for _ , v := range artifacts {
lens [ len ( v ) ] = true
}
2020-10-11 13:00:42 -03:00
if len ( lens ) <= 1 {
2020-10-10 08:46:16 -03:00
return nil
}
return ErrArchiveDifferentBinaryCount
}
2022-05-10 09:17:30 -03:00
func createMeta ( ctx * context . Context , arch config . Archive ) error {
return doCreate ( ctx , arch , nil , arch . Format , tmpl . New ( ctx ) )
}
2020-05-26 00:48:10 -03:00
func create ( ctx * context . Context , arch config . Archive , binaries [ ] * artifact . Artifact ) error {
2022-05-10 09:17:30 -03:00
template := tmpl . New ( ctx ) . WithArtifact ( binaries [ 0 ] , arch . Replacements )
2021-04-25 14:20:49 -03:00
format := packageFormat ( arch , binaries [ 0 ] . Goos )
2022-05-10 09:17:30 -03:00
return doCreate ( ctx , arch , binaries , format , template )
}
func doCreate ( ctx * context . Context , arch config . Archive , binaries [ ] * artifact . Artifact , format string , template * tmpl . Template ) error {
folder , err := template . Apply ( arch . NameTemplate )
2017-12-17 15:50:09 -02:00
if err != nil {
return err
}
archivePath := filepath . Join ( ctx . Config . Dist , folder + "." + format )
2018-11-11 18:17:44 -02:00
lock . Lock ( )
2021-04-25 14:20:49 -03:00
if err := os . MkdirAll ( filepath . Dir ( archivePath ) , 0 o755 | os . ModeDir ) ; err != nil {
2020-03-28 15:40:16 -03:00
lock . Unlock ( )
return err
}
2022-09-11 15:32:23 -03:00
if _ , err = os . Stat ( archivePath ) ; ! errors . Is ( err , fs . ErrNotExist ) {
2018-11-11 18:17:44 -02:00
lock . Unlock ( )
2018-11-11 12:11:03 -02:00
return fmt . Errorf ( "archive named %s already exists. Check your archive name template" , archivePath )
}
2017-12-17 15:50:09 -02:00
archiveFile , err := os . Create ( archivePath )
if err != nil {
2018-11-11 18:17:44 -02:00
lock . Unlock ( )
2020-11-10 11:15:03 -03:00
return fmt . Errorf ( "failed to create directory %s: %w" , archivePath , err )
2017-12-17 15:50:09 -02:00
}
2018-11-11 18:17:44 -02:00
lock . Unlock ( )
2020-05-26 00:48:10 -03:00
defer archiveFile . Close ( )
2018-11-13 13:48:16 -02:00
2021-04-25 14:20:49 -03:00
log := log . WithField ( "archive" , archivePath )
2018-10-26 16:31:06 -06:00
log . Info ( "creating" )
2018-11-13 13:48:16 -02:00
2020-05-26 00:48:10 -03:00
wrap , err := template . Apply ( wrapFolder ( arch ) )
2018-11-13 13:48:16 -02:00
if err != nil {
return err
2018-11-07 14:15:51 -02:00
}
2022-05-11 21:54:28 -03:00
a , err := archive . New ( archiveFile , format )
if err != nil {
return err
}
a = NewEnhancedArchive ( a , wrap )
2020-05-26 00:48:10 -03:00
defer a . Close ( )
2017-12-17 15:50:09 -02:00
2022-05-11 22:56:25 -03:00
files , err := archivefiles . Eval ( template , arch . Files )
2017-12-17 15:50:09 -02:00
if err != nil {
2020-11-10 11:15:03 -03:00
return fmt . Errorf ( "failed to find files to archive: %w" , err )
2017-12-17 15:50:09 -02:00
}
2022-05-10 09:17:30 -03:00
if arch . Meta && len ( files ) == 0 {
return fmt . Errorf ( "no files found" )
}
2017-12-17 15:50:09 -02:00
for _ , f := range files {
2021-07-21 22:09:02 -03:00
if err = a . Add ( f ) ; err != nil {
return fmt . Errorf ( "failed to add: '%s' -> '%s': %w" , f . Source , f . Destination , err )
2017-07-13 19:46:48 -03:00
}
2017-12-17 15:50:09 -02:00
}
2021-10-08 21:25:53 -03:00
bins := [ ] string { }
2017-12-29 11:47:13 -02:00
for _ , binary := range binaries {
2022-08-15 22:53:36 -03:00
dst := binary . Name
if arch . StripParentBinaryFolder {
dst = filepath . Base ( dst )
}
2021-07-21 22:09:02 -03:00
if err := a . Add ( config . File {
Source : binary . Path ,
2022-08-15 22:53:36 -03:00
Destination : dst ,
2021-07-21 22:09:02 -03:00
} ) ; err != nil {
2022-08-15 22:53:36 -03:00
return fmt . Errorf ( "failed to add: '%s' -> '%s': %w" , binary . Path , dst , err )
2017-07-13 19:46:48 -03:00
}
2021-10-08 21:25:53 -03:00
bins = append ( bins , binary . Name )
2017-05-11 00:05:51 -03:00
}
2022-05-10 09:17:30 -03:00
art := & artifact . Artifact {
Type : artifact . UploadableArchive ,
Name : folder + "." + format ,
Path : archivePath ,
2019-01-01 14:40:17 -02:00
Extra : map [ string ] interface { } {
2021-10-16 23:20:14 -03:00
artifact . ExtraBuilds : binaries ,
artifact . ExtraID : arch . ID ,
artifact . ExtraFormat : arch . Format ,
artifact . ExtraWrappedIn : wrap ,
artifact . ExtraBinaries : bins ,
2019-01-01 14:40:17 -02:00
} ,
2022-05-10 09:17:30 -03:00
}
if len ( binaries ) > 0 {
art . Goos = binaries [ 0 ] . Goos
art . Goarch = binaries [ 0 ] . Goarch
art . Goarm = binaries [ 0 ] . Goarm
art . Gomips = binaries [ 0 ] . Gomips
art . Goamd64 = binaries [ 0 ] . Goamd64
art . Extra [ artifact . ExtraReplaces ] = binaries [ 0 ] . Extra [ artifact . ExtraReplaces ]
}
ctx . Artifacts . Add ( art )
2017-01-14 12:51:09 -02:00
return nil
2016-12-28 22:53:56 -02:00
}
2018-11-13 13:48:16 -02:00
func wrapFolder ( a config . Archive ) string {
switch a . WrapInDirectory {
case "true" :
return a . NameTemplate
case "false" :
return ""
default :
return a . WrapInDirectory
}
}
2019-08-12 17:44:48 -03:00
func skip ( ctx * context . Context , archive config . Archive , binaries [ ] * artifact . Artifact ) error {
2017-12-29 11:47:13 -02:00
for _ , binary := range binaries {
2018-07-08 20:47:30 -07:00
name , err := tmpl . New ( ctx ) .
2019-04-16 10:19:15 -03:00
WithArtifact ( binary , archive . Replacements ) .
Apply ( archive . NameTemplate )
2017-12-17 18:01:58 -02:00
if err != nil {
return err
}
2022-06-23 23:36:19 -03:00
finalName := name + artifact . ExtraOr ( * binary , artifact . ExtraExt , "" )
2021-10-16 23:03:06 -03:00
log . WithField ( "binary" , binary . Name ) .
2021-10-13 22:34:55 -03:00
WithField ( "name" , finalName ) .
Info ( "skip archiving" )
2019-08-12 17:44:48 -03:00
ctx . Artifacts . Add ( & artifact . Artifact {
2022-04-11 22:43:22 -03:00
Type : artifact . UploadableBinary ,
Name : finalName ,
Path : binary . Path ,
Goos : binary . Goos ,
Goarch : binary . Goarch ,
Goarm : binary . Goarm ,
Gomips : binary . Gomips ,
Goamd64 : binary . Goamd64 ,
2019-01-01 14:40:17 -02:00
Extra : map [ string ] interface { } {
2021-12-07 21:53:39 -03:00
artifact . ExtraBuilds : [ ] * artifact . Artifact { binary } ,
artifact . ExtraID : archive . ID ,
artifact . ExtraFormat : archive . Format ,
artifact . ExtraBinary : binary . Name ,
artifact . ExtraReplaces : binaries [ 0 ] . Extra [ artifact . ExtraReplaces ] ,
2019-01-01 14:40:17 -02:00
} ,
} )
2017-07-03 00:57:39 -03:00
}
2017-06-05 16:38:59 +02:00
return nil
}
2019-04-16 10:19:15 -03:00
func packageFormat ( archive config . Archive , platform string ) string {
for _ , override := range archive . FormatOverrides {
2017-12-18 21:15:32 -02:00
if strings . HasPrefix ( platform , override . Goos ) {
return override . Format
}
}
2019-04-16 10:19:15 -03:00
return archive . Format
2017-12-18 21:15:32 -02:00
}
2018-11-07 14:15:51 -02:00
2018-11-11 19:02:45 -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 14: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 {
2018-11-11 19:02:45 -02:00
a archive . Archive
wrap string
files map [ string ] string
2018-11-07 14:15:51 -02:00
}
2020-05-26 00:48:10 -03:00
// Add adds a file.
2021-07-21 22:09:02 -03:00
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 ,
Info : f . Info ,
2018-11-11 19:02:45 -02:00
}
2021-07-21 22:09:02 -03:00
return d . a . Add ( ff )
2018-11-07 14:15:51 -02:00
}
2020-05-26 00:48:10 -03:00
// Close closes the underlying archive.
2018-11-07 14:15:51 -02:00
func ( d EnhancedArchive ) Close ( ) error {
return d . a . Close ( )
}