github.phpd.cn/goreleaser/goreleaser@v0.92.0/internal/pipe/archive/archive.go (about)

     1  // Package archive implements the pipe interface with the intent of
     2  // archiving and compressing the binaries, readme, and other artifacts. It
     3  // also provides an Archive interface which represents an archiving format.
     4  package archive
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/apex/log"
    13  	"github.com/campoy/unique"
    14  	zglob "github.com/mattn/go-zglob"
    15  	"golang.org/x/sync/errgroup"
    16  
    17  	"github.com/goreleaser/goreleaser/internal/artifact"
    18  	"github.com/goreleaser/goreleaser/internal/tmpl"
    19  	"github.com/goreleaser/goreleaser/pkg/archive"
    20  	"github.com/goreleaser/goreleaser/pkg/context"
    21  )
    22  
    23  const (
    24  	defaultNameTemplate       = "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
    25  	defaultBinaryNameTemplate = "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
    26  )
    27  
    28  // Pipe for archive
    29  type Pipe struct{}
    30  
    31  func (Pipe) String() string {
    32  	return "archives"
    33  }
    34  
    35  // Default sets the pipe defaults
    36  func (Pipe) Default(ctx *context.Context) error {
    37  	var archive = &ctx.Config.Archive
    38  	if archive.Format == "" {
    39  		archive.Format = "tar.gz"
    40  	}
    41  	if len(archive.Files) == 0 {
    42  		archive.Files = []string{
    43  			"licence*",
    44  			"LICENCE*",
    45  			"license*",
    46  			"LICENSE*",
    47  			"readme*",
    48  			"README*",
    49  			"changelog*",
    50  			"CHANGELOG*",
    51  		}
    52  	}
    53  	if archive.NameTemplate == "" {
    54  		archive.NameTemplate = defaultNameTemplate
    55  		if archive.Format == "binary" {
    56  			archive.NameTemplate = defaultBinaryNameTemplate
    57  		}
    58  	}
    59  	return nil
    60  }
    61  
    62  // Run the pipe
    63  func (Pipe) Run(ctx *context.Context) error {
    64  	var g errgroup.Group
    65  	var filtered = ctx.Artifacts.Filter(artifact.ByType(artifact.Binary))
    66  	for group, artifacts := range filtered.GroupByPlatform() {
    67  		log.Debugf("group %s has %d binaries", group, len(artifacts))
    68  		artifacts := artifacts
    69  		g.Go(func() error {
    70  			if packageFormat(ctx, artifacts[0].Goos) == "binary" {
    71  				return skip(ctx, artifacts)
    72  			}
    73  			return create(ctx, artifacts)
    74  		})
    75  	}
    76  	return g.Wait()
    77  }
    78  
    79  func create(ctx *context.Context, binaries []artifact.Artifact) error {
    80  	var format = packageFormat(ctx, binaries[0].Goos)
    81  	folder, err := tmpl.New(ctx).
    82  		WithArtifact(binaries[0], ctx.Config.Archive.Replacements).
    83  		Apply(ctx.Config.Archive.NameTemplate)
    84  	if err != nil {
    85  		return err
    86  	}
    87  	archivePath := filepath.Join(ctx.Config.Dist, folder+"."+format)
    88  	archiveFile, err := os.Create(archivePath)
    89  	if err != nil {
    90  		return fmt.Errorf("failed to create directory %s: %s", archivePath, err.Error())
    91  	}
    92  	defer archiveFile.Close() // nolint: errcheck
    93  	var log = log.WithField("archive", archivePath)
    94  	log.Info("creating")
    95  	var a = archive.New(archiveFile)
    96  	defer a.Close() // nolint: errcheck
    97  
    98  	files, err := findFiles(ctx)
    99  	if err != nil {
   100  		return fmt.Errorf("failed to find files to archive: %s", err.Error())
   101  	}
   102  	for _, f := range files {
   103  		log.Debugf("adding %s", f)
   104  		if err = a.Add(wrap(ctx, f, folder), f); err != nil {
   105  			return fmt.Errorf("failed to add %s to the archive: %s", f, err.Error())
   106  		}
   107  	}
   108  	for _, binary := range binaries {
   109  		var bin = wrap(ctx, binary.Name, folder)
   110  		log.Debugf("adding %s", bin)
   111  		if err := a.Add(bin, binary.Path); err != nil {
   112  			return fmt.Errorf("failed to add %s -> %s to the archive: %s", binary.Path, binary.Name, err.Error())
   113  		}
   114  	}
   115  	ctx.Artifacts.Add(artifact.Artifact{
   116  		Type:   artifact.UploadableArchive,
   117  		Name:   folder + "." + format,
   118  		Path:   archivePath,
   119  		Goos:   binaries[0].Goos,
   120  		Goarch: binaries[0].Goarch,
   121  		Goarm:  binaries[0].Goarm,
   122  	})
   123  	return nil
   124  }
   125  
   126  func skip(ctx *context.Context, binaries []artifact.Artifact) error {
   127  	for _, binary := range binaries {
   128  		log.WithField("binary", binary.Name).Info("skip archiving")
   129  		name, err := tmpl.New(ctx).
   130  			WithArtifact(binary, ctx.Config.Archive.Replacements).
   131  			Apply(ctx.Config.Archive.NameTemplate)
   132  		if err != nil {
   133  			return err
   134  		}
   135  		binary.Type = artifact.UploadableBinary
   136  		binary.Name = name + binary.Extra["Ext"]
   137  		ctx.Artifacts.Add(binary)
   138  	}
   139  	return nil
   140  }
   141  
   142  func findFiles(ctx *context.Context) (result []string, err error) {
   143  	for _, glob := range ctx.Config.Archive.Files {
   144  		files, err := zglob.Glob(glob)
   145  		if err != nil {
   146  			return result, fmt.Errorf("globbing failed for pattern %s: %s", glob, err.Error())
   147  		}
   148  		result = append(result, files...)
   149  	}
   150  	// remove duplicates
   151  	unique.Slice(&result, func(i, j int) bool {
   152  		return strings.Compare(result[i], result[j]) < 0
   153  	})
   154  	return
   155  }
   156  
   157  // Wrap archive files with folder if set in config.
   158  func wrap(ctx *context.Context, name, folder string) string {
   159  	if ctx.Config.Archive.WrapInDirectory {
   160  		return filepath.Join(folder, name)
   161  	}
   162  	return name
   163  }
   164  
   165  func packageFormat(ctx *context.Context, platform string) string {
   166  	for _, override := range ctx.Config.Archive.FormatOverrides {
   167  		if strings.HasPrefix(platform, override.Goos) {
   168  			return override.Format
   169  		}
   170  	}
   171  	return ctx.Config.Archive.Format
   172  }