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 }