github.com/marianogappa/goreleaser@v0.26.2-0.20170715090149-96acd0a9fc46/pipeline/fpm/fpm.go (about)

     1  // Package fpm implements the Pipe interface providing FPM bindings.
     2  package fpm
     3  
     4  import (
     5  	"errors"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/apex/log"
    13  	"github.com/goreleaser/goreleaser/context"
    14  	"golang.org/x/sync/errgroup"
    15  )
    16  
    17  // ErrNoFPM is shown when fpm cannot be found in $PATH
    18  var ErrNoFPM = errors.New("fpm not present in $PATH")
    19  
    20  // Pipe for fpm packaging
    21  type Pipe struct{}
    22  
    23  // Description of the pipe
    24  func (Pipe) Description() string {
    25  	return "Creating Linux packages with fpm"
    26  }
    27  
    28  // Run the pipe
    29  func (Pipe) Run(ctx *context.Context) error {
    30  	if len(ctx.Config.FPM.Formats) == 0 {
    31  		log.Info("no output formats configured, skipping")
    32  		return nil
    33  	}
    34  	if ctx.Config.Archive.Format == "binary" {
    35  		log.Info("skipped because archive format is binary")
    36  		return nil
    37  	}
    38  	_, err := exec.LookPath("fpm")
    39  	if err != nil {
    40  		return ErrNoFPM
    41  	}
    42  
    43  	var g errgroup.Group
    44  	for _, format := range ctx.Config.FPM.Formats {
    45  		for key, folder := range ctx.Folders {
    46  			if !strings.Contains(key, "linux") {
    47  				log.WithField("key", key).Debug("skipped non-linux builds for fpm")
    48  				continue
    49  			}
    50  			folder := folder
    51  			format := format
    52  			arch := archFor(key)
    53  			g.Go(func() error {
    54  				return create(ctx, format, folder, arch)
    55  			})
    56  		}
    57  	}
    58  	return g.Wait()
    59  }
    60  
    61  func archFor(key string) string {
    62  	if strings.Contains(key, "386") {
    63  		return "i386"
    64  	}
    65  	return "x86_64"
    66  }
    67  
    68  func create(ctx *context.Context, format, folder, arch string) error {
    69  	var path = filepath.Join(ctx.Config.Dist, folder)
    70  	var file = path + "." + format
    71  	log.WithField("file", file).Info("creating fpm archive")
    72  
    73  	var options = []string{
    74  		"--input-type", "dir",
    75  		"--output-type", format,
    76  		"--name", ctx.Config.ProjectName,
    77  		"--version", ctx.Version,
    78  		"--architecture", arch,
    79  		"--chdir", path,
    80  		"--package", file,
    81  		"--force",
    82  	}
    83  
    84  	if ctx.Config.FPM.Vendor != "" {
    85  		options = append(options, "--vendor", ctx.Config.FPM.Vendor)
    86  	}
    87  	if ctx.Config.FPM.Homepage != "" {
    88  		options = append(options, "--url", ctx.Config.FPM.Homepage)
    89  	}
    90  	if ctx.Config.FPM.Maintainer != "" {
    91  		options = append(options, "--maintainer", ctx.Config.FPM.Maintainer)
    92  	}
    93  	if ctx.Config.FPM.Description != "" {
    94  		options = append(options, "--description", ctx.Config.FPM.Description)
    95  	}
    96  	if ctx.Config.FPM.License != "" {
    97  		options = append(options, "--license", ctx.Config.FPM.License)
    98  	}
    99  	for _, dep := range ctx.Config.FPM.Dependencies {
   100  		options = append(options, "--depends", dep)
   101  	}
   102  	for _, conflict := range ctx.Config.FPM.Conflicts {
   103  		options = append(options, "--conflicts", conflict)
   104  	}
   105  
   106  	files, err := ioutil.ReadDir(path)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	for _, file := range files {
   111  		// XXX: skip non executable files here?
   112  		// This basically tells fpm to put the binary in the /usr/local/bin
   113  		// binary=/usr/local/bin/binary
   114  		log.WithField("file", file.Name()).Debug("passed binary to fpm")
   115  		options = append(options, fmt.Sprintf(
   116  			"%s=%s",
   117  			file.Name(),
   118  			filepath.Join("/usr/local/bin", file.Name()),
   119  		))
   120  	}
   121  
   122  	if out, err := exec.Command("fpm", options...).CombinedOutput(); err != nil {
   123  		return errors.New(string(out))
   124  	}
   125  	ctx.AddArtifact(file)
   126  	return nil
   127  }