github.com/droot/goreleaser@v0.66.2-0.20180420030140-c2db5fb17157/pipeline/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  	"github.com/mattn/go-zglob"
    15  	"golang.org/x/sync/errgroup"
    16  
    17  	"github.com/goreleaser/archive"
    18  	"github.com/goreleaser/goreleaser/context"
    19  	"github.com/goreleaser/goreleaser/internal/artifact"
    20  	"github.com/goreleaser/goreleaser/internal/filenametemplate"
    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 "creating 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 _, artifacts := range filtered.GroupByPlatform() {
    67  		artifacts := artifacts
    68  		g.Go(func() error {
    69  			if packageFormat(ctx, artifacts[0].Goos) == "binary" {
    70  				return skip(ctx, artifacts)
    71  			}
    72  			return create(ctx, artifacts)
    73  		})
    74  	}
    75  	return g.Wait()
    76  }
    77  
    78  func create(ctx *context.Context, binaries []artifact.Artifact) error {
    79  	var format = packageFormat(ctx, binaries[0].Goos)
    80  	folder, err := filenametemplate.Apply(
    81  		ctx.Config.Archive.NameTemplate,
    82  		filenametemplate.NewFields(ctx, ctx.Config.Archive.Replacements, binaries...),
    83  	)
    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  	log.WithField("archive", archivePath).Info("creating")
    94  	var a = archive.New(archiveFile)
    95  	defer a.Close() // nolint: errcheck
    96  
    97  	files, err := findFiles(ctx, binaries)
    98  	if err != nil {
    99  		return fmt.Errorf("failed to find files to archive: %s", err.Error())
   100  	}
   101  	for _, f := range files {
   102  		log.Debugf("adding %s", f)
   103  		if err = a.Add(wrap(ctx, filepath.Base(f), folder), f); err != nil {
   104  			return fmt.Errorf("failed to add %s to the archive: %s", f, err.Error())
   105  		}
   106  	}
   107  	for _, binary := range binaries {
   108  		var bin = wrap(ctx, binary.Name, folder)
   109  		log.Debugf("adding %s", bin)
   110  		if err := a.Add(bin, binary.Path); err != nil {
   111  			return fmt.Errorf("failed to add %s -> %s to the archive: %s", binary.Path, binary.Name, err.Error())
   112  		}
   113  	}
   114  	ctx.Artifacts.Add(artifact.Artifact{
   115  		Type:   artifact.UploadableArchive,
   116  		Name:   folder + "." + format,
   117  		Path:   archivePath,
   118  		Goos:   binaries[0].Goos,
   119  		Goarch: binaries[0].Goarch,
   120  		Goarm:  binaries[0].Goarm,
   121  	})
   122  	return nil
   123  }
   124  func skip(ctx *context.Context, binaries []artifact.Artifact) error {
   125  	for _, binary := range binaries {
   126  		log.WithField("binary", binary.Name).Info("skip archiving")
   127  		var fields = filenametemplate.NewFields(ctx, ctx.Config.Archive.Replacements, binary)
   128  		name, err := filenametemplate.Apply(ctx.Config.Archive.NameTemplate, fields)
   129  		if err != nil {
   130  			return err
   131  		}
   132  		binary.Type = artifact.UploadableBinary
   133  		binary.Name = name + binary.Extra["Ext"]
   134  		ctx.Artifacts.Add(binary)
   135  	}
   136  	return nil
   137  }
   138  
   139  func findFiles(ctx *context.Context, binaries []artifact.Artifact) (result []string, err error) {
   140  	for _, globTmpl := range ctx.Config.Archive.Files {
   141  		var glob string
   142  		glob, err = filenametemplate.Apply(
   143  			globTmpl,
   144  			filenametemplate.NewFields(ctx, nil, binaries...),
   145  		)
   146  		if err != nil {
   147  			return
   148  		}
   149  		log.Debugf("adding glob %s \n", glob)
   150  		files, err := zglob.Glob(glob)
   151  		if err != nil {
   152  			return result, fmt.Errorf("globbing failed for pattern %s: %s", glob, err.Error())
   153  		}
   154  		result = append(result, files...)
   155  	}
   156  	// remove duplicates
   157  	unique.Slice(&result, func(i, j int) bool {
   158  		return strings.Compare(result[i], result[j]) < 0
   159  	})
   160  	return
   161  }
   162  
   163  // Wrap archive files with folder if set in config.
   164  func wrap(ctx *context.Context, name, folder string) string {
   165  	if ctx.Config.Archive.WrapInDirectory {
   166  		return filepath.Join(folder, ctx.Config.Archive.WrapInDirectoryPath, name)
   167  	}
   168  	return name
   169  }
   170  
   171  func packageFormat(ctx *context.Context, platform string) string {
   172  	for _, override := range ctx.Config.Archive.FormatOverrides {
   173  		if strings.HasPrefix(platform, override.Goos) {
   174  			return override.Format
   175  		}
   176  	}
   177  	return ctx.Config.Archive.Format
   178  }