diff --git a/README.md b/README.md index 728306c4f..74bb1f4b6 100644 --- a/README.md +++ b/README.md @@ -61,36 +61,36 @@ By default GoReleaser will build the current directory, but you can change the b ```yml # goreleaser.yml # Build customization -build: - binary: drum-roll - goos: - - windows - - darwin - - linux - goarch: - - amd64 +builds: + - binary: drum-roll + goos: + - windows + - darwin + - linux + goarch: + - amd64 ``` PS: Invalid GOOS/GOARCH combinations will automatically be skipped. This configuration specifies the build operating systems to Windows, Linux and MacOS using 64bit architecture, the name of the binaries is `drum-roll`. -GoReleaser will then archive the result binaries of each Os/Arch into a separate file. The default format is `{{.Binary}}_{{.Os}}_{{.Arch}}`. +GoReleaser will then archive the result binaries of each Os/Arch into a separate file. The default format is `{{.ProjectName}}_{{.Os}}_{{.Arch}}`. You can change the archives name and format. You can also replace the OS and the Architecture with your own. Another useful feature is to add files to archives, this is very useful for integrating assets like resource files. ```yml # goreleaser.yml # Build customization -build: - main: main.go - binary: drum-roll - goos: - - windows - - darwin - - linux - goarch: - - amd64 +builds: + - main: main.go + binary: drum-roll + goos: + - windows + - darwin + - linux + goarch: + - amd64 # Archive customization archive: format: tar.gz @@ -172,76 +172,87 @@ defaults are sensible and fit for most projects. We'll cover all customizations available bellow: +### Project name + +```yml +# goreleaser.yml +# The name of the project. It is used in the name of the brew formula, archives, +# etc. Defaults to the name of the git project. +project_name: myproject +``` + ### Build customization ```yml # goreleaser.yml -build: - # Path to main.go file or main package. - # Default is `.` - main: ./cmd/main.go +builds: + # You can have multiple builds, its a common yaml list + - + # Path to main.go file or main package. + # Default is `.` + main: ./cmd/main.go - # Name of the binary. - # Default is the name of the project directory. - binary: program + # Name of the binary. + # Default is the name of the project directory. + binary: program - # Custom build tags. - # Default is empty - flags: -tags dev + # Custom build tags. + # Default is empty + flags: -tags dev - # Custom ldflags template. - # This is parsed with Golang template engine and the following variables - # are available: - # - Date - # - Commit - # - Tag - # - Version (Tag with the `v` prefix stripped) - # The default is `-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}` - # Date format is `2006-01-02_15:04:05` - ldflags: -s -w -X main.build={{.Version}} + # Custom ldflags template. + # This is parsed with Golang template engine and the following variables + # are available: + # - Date + # - Commit + # - Tag + # - Version (Tag with the `v` prefix stripped) + # The default is `-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}` + # Date format is `2006-01-02_15:04:05` + ldflags: -s -w -X main.build={{.Version}} - # Custom environment variables to be set durign the builds. - # Default is empty - env: - - CGO_ENABLED=0 + # Custom environment variables to be set durign the builds. + # Default is empty + env: + - CGO_ENABLED=0 - # GOOS list to build in. - # For more info refer to https://golang.org/doc/install/source#environment - # Defaults are darwin and linux - goos: - - freebsd - - windows + # GOOS list to build in. + # For more info refer to https://golang.org/doc/install/source#environment + # Defaults are darwin and linux + goos: + - freebsd + - windows - # GOARCH to build in. - # For more info refer to https://golang.org/doc/install/source#environment - # Defaults are 386 and amd64 - goarch: - - amd64 - - arm - - arm64 + # GOARCH to build in. + # For more info refer to https://golang.org/doc/install/source#environment + # Defaults are 386 and amd64 + goarch: + - amd64 + - arm + - arm64 - # GOARM to build in when GOARCH is arm. - # For more info refer to https://golang.org/doc/install/source#environment - # Defaults are 6 - goarm: - - 6 - - 7 + # GOARM to build in when GOARCH is arm. + # For more info refer to https://golang.org/doc/install/source#environment + # Defaults are 6 + goarm: + - 6 + - 7 - # List of combinations of GOOS + GOARCH + GOARM to ignore. - # Default is empty. - ignore: - - goos: darwin - goarch: 386 - - goos: linux - goarch: arm - goarm: 7 + # List of combinations of GOOS + GOARCH + GOARM to ignore. + # Default is empty. + ignore: + - goos: darwin + goarch: 386 + - goos: linux + goarch: arm + goarm: 7 - # Hooks can be used to customize the final binary, for example, to run - # generator or whatever you want. - # Default is both hooks empty. - hooks: - pre: rice embed-go - post: ./script.sh + # Hooks can be used to customize the final binary, for example, to run + # generator or whatever you want. + # Default is both hooks empty. + hooks: + pre: rice embed-go + post: ./script.sh ``` ### Archive customization @@ -252,14 +263,14 @@ archive: # You can change the name of the archive. # This is parsed with Golang template engine and the following variables # are available: - # - Binary + # - ProjectName # - Tag # - Version (Tag with the `v` prefix stripped) # - Os # - Arch # - Arm (ARM version) - # The default is `{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}` - name_template: "{{.Binary}}_{{.Version}}_{{.Os}}_{{.Arch}}" + # The default is `{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}` + name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" # Archive format. Valid options are `tar.gz` and `zip`. # Default is `tar.gz` diff --git a/config/config.go b/config/config.go index 1db76a004..d709c42bd 100644 --- a/config/config.go +++ b/config/config.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "os" + "github.com/apex/log" yaml "gopkg.in/yaml.v1" ) @@ -99,12 +100,16 @@ type Snapshot struct { // Project includes all project configuration type Project struct { - Release Release `yaml:",omitempty"` - Brew Homebrew `yaml:",omitempty"` - Build Build `yaml:",omitempty"` - Archive Archive `yaml:",omitempty"` - FPM FPM `yaml:",omitempty"` - Snapshot Snapshot `yaml:",omitempty"` + ProjectName string `yaml:"project_name,omitempty"` + Release Release `yaml:",omitempty"` + Brew Homebrew `yaml:",omitempty"` + Builds []Build `yaml:",omitempty"` + Archive Archive `yaml:",omitempty"` + FPM FPM `yaml:",omitempty"` + Snapshot Snapshot `yaml:",omitempty"` + + // this is a hack ¯\_(ツ)_/¯ + SingleBuild Build `yaml:"build,omitempty"` // test only property indicating the path to the dist folder Dist string `yaml:"-"` @@ -126,5 +131,6 @@ func LoadReader(fd io.Reader) (config Project, err error) { return config, err } err = yaml.Unmarshal(data, &config) + log.WithField("config", config).Debug("loaded config file") return } diff --git a/context/context.go b/context/context.go index c3d6541e7..d36195401 100644 --- a/context/context.go +++ b/context/context.go @@ -27,7 +27,7 @@ type Context struct { Config config.Project Token string Git GitInfo - Archives map[string]string + Folders map[string]string Artifacts []string ReleaseNotes string Version string @@ -36,23 +36,32 @@ type Context struct { Snapshot bool } -var lock sync.Mutex +var artifactsLock sync.Mutex +var foldersLock sync.Mutex // AddArtifact adds a file to upload list func (ctx *Context) AddArtifact(file string) { - lock.Lock() - defer lock.Unlock() + artifactsLock.Lock() + defer artifactsLock.Unlock() file = strings.TrimPrefix(file, ctx.Config.Dist) file = strings.Replace(file, "/", "", -1) ctx.Artifacts = append(ctx.Artifacts, file) - log.WithField("artifact", file).Info("registered") + log.WithField("artifact", file).Info("new artifact") +} + +// AddFolder adds a built binary to the current context +func (ctx *Context) AddFolder(key, folder string) { + foldersLock.Lock() + defer foldersLock.Unlock() + ctx.Folders[key] = folder + log.WithField("key", key).WithField("folder", folder).Info("new folder") } // New context func New(config config.Project) *Context { return &Context{ - Context: ctx.Background(), - Config: config, - Archives: map[string]string{}, + Context: ctx.Background(), + Config: config, + Folders: map[string]string{}, } } diff --git a/context/context_test.go b/context/context_test.go index 617c81d8c..4a599a754 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -31,3 +31,27 @@ func TestMultipleArtifactAdds(t *testing.T) { assert.Len(ctx.Artifacts, len(list)) assert.Contains(ctx.Artifacts, "a", "b", "c", "d") } + +func TestMultipleFolderAdds(t *testing.T) { + var assert = assert.New(t) + var list = map[string]string{ + "key-a": "folder/a", + "key-b": "folder/b", + "key-c": "folder/c", + "key-d": "folder/d", + } + var ctx = New(config.Project{ + Dist: "dist", + }) + var g errgroup.Group + for k, f := range list { + f := f + k := k + g.Go(func() error { + ctx.AddFolder(k, f) + return nil + }) + } + assert.NoError(g.Wait()) + assert.Len(ctx.Folders, len(list)) +} diff --git a/goreleaser.yml b/goreleaser.yml index b7d8ea538..01c8b5844 100644 --- a/goreleaser.yml +++ b/goreleaser.yml @@ -1,17 +1,18 @@ homepage: &homepage http://goreleaser.github.io description: &description Deliver Go binaries as fast and easily as possible -build: - env: - - CGO_ENABLED=0 - goos: - - linux - - darwin - - windows - goarch: - - 386 - - amd64 - - arm - - arm64 +builds: + - + env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + - windows + goarch: + - 386 + - amd64 + - arm + - arm64 archive: name_template: '{{ .Binary }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' replacements: diff --git a/internal/client/github.go b/internal/client/github.go index 738904a1a..630cff588 100644 --- a/internal/client/github.go +++ b/internal/client/github.go @@ -36,7 +36,7 @@ func (c *githubClient) CreateFile( }, Content: content.Bytes(), Message: github.String( - ctx.Config.Build.Binary + " version " + ctx.Git.CurrentTag, + ctx.Config.ProjectName + " version " + ctx.Git.CurrentTag, ), } diff --git a/internal/name/name.go b/internal/name/name.go new file mode 100644 index 000000000..7bf006ee7 --- /dev/null +++ b/internal/name/name.go @@ -0,0 +1,49 @@ +// Package name provides name template logic for the final archive, formulae, +// etc. +package name + +import ( + "bytes" + "text/template" + + "github.com/goreleaser/goreleaser/context" +) + +type nameData struct { + Os string + Arch string + Arm string + Version string + Tag string + Binary string // deprecated + ProjectName string +} + +// For returns the name for the given context, goos, goarch and goarm. +func For(ctx *context.Context, goos, goarch, goarm string) (string, error) { + var data = nameData{ + Os: replace(ctx.Config.Archive.Replacements, goos), + Arch: replace(ctx.Config.Archive.Replacements, goarch), + Arm: replace(ctx.Config.Archive.Replacements, goarm), + Version: ctx.Version, + Tag: ctx.Git.CurrentTag, + Binary: ctx.Config.ProjectName, + ProjectName: ctx.Config.ProjectName, + } + + var out bytes.Buffer + t, err := template.New(data.Binary).Parse(ctx.Config.Archive.NameTemplate) + if err != nil { + return "", err + } + err = t.Execute(&out, data) + return out.String(), err +} + +func replace(replacements map[string]string, original string) string { + result := replacements[original] + if result == "" { + return original + } + return result +} diff --git a/pipeline/build/name_test.go b/internal/name/name_test.go similarity index 74% rename from pipeline/build/name_test.go rename to internal/name/name_test.go index cc3dee858..80c5f7eff 100644 --- a/pipeline/build/name_test.go +++ b/internal/name/name_test.go @@ -1,4 +1,4 @@ -package build +package name import ( "testing" @@ -20,9 +20,7 @@ func TestNameFor(t *testing.T) { "amd64": "x86_64", }, }, - Build: config.Build{ - Binary: "test", - }, + ProjectName: "test", } var ctx = &context.Context{ Config: config, @@ -32,30 +30,26 @@ func TestNameFor(t *testing.T) { }, } - name, err := nameFor(ctx, buildTarget{"darwin", "amd64", ""}) + name, err := For(ctx, "darwin", "amd64", "") assert.NoError(err) assert.Equal("test_Darwin_x86_64_v1.2.3_1.2.3", name) } func TestInvalidNameTemplate(t *testing.T) { - assert := assert.New(t) - - var config = config.Project{ - Archive: config.Archive{ - NameTemplate: "{{.Binary}_{{.Os}}_{{.Arch}}_{{.Version}}", - }, - Build: config.Build{ - Binary: "test", - }, - } + var assert = assert.New(t) var ctx = &context.Context{ - Config: config, + Config: config.Project{ + Archive: config.Archive{ + NameTemplate: "{{.Binary}_{{.Os}}_{{.Arch}}_{{.Version}}", + }, + ProjectName: "test", + }, Git: context.GitInfo{ CurrentTag: "v1.2.3", }, } - _, err := nameFor(ctx, buildTarget{"darwin", "amd64", ""}) + _, err := For(ctx, "darwin", "amd64", "") assert.Error(err) } @@ -66,19 +60,20 @@ func TestNameDefaltTemplate(t *testing.T) { Archive: config.Archive{ NameTemplate: defaults.NameTemplate, }, - Build: config.Build{ - Binary: "test", - }, + ProjectName: "test", }, Version: "1.2.3", } + type buildTarget struct { + goos, goarch, goarm string + } for key, target := range map[string]buildTarget{ "test_1.2.3_darwin_amd64": {"darwin", "amd64", ""}, "test_1.2.3_linux_arm64": {"linux", "arm64", ""}, "test_1.2.3_linux_armv7": {"linux", "arm", "7"}, } { t.Run(key, func(t *testing.T) { - name, err := nameFor(ctx, target) + name, err := For(ctx, target.goos, target.goarch, target.goarm) assert.NoError(err) assert.Equal(key, name) }) diff --git a/pipeline/archive/archive.go b/pipeline/archive/archive.go index 108b2d68e..1806538a6 100644 --- a/pipeline/archive/archive.go +++ b/pipeline/archive/archive.go @@ -4,6 +4,7 @@ package archive import ( + "io/ioutil" "os" "path/filepath" @@ -11,7 +12,6 @@ import ( "github.com/goreleaser/archive" "github.com/goreleaser/goreleaser/context" "github.com/goreleaser/goreleaser/internal/archiveformat" - "github.com/goreleaser/goreleaser/internal/ext" "github.com/mattn/go-zglob" "golang.org/x/sync/errgroup" ) @@ -27,11 +27,11 @@ func (Pipe) Description() string { // Run the pipe func (Pipe) Run(ctx *context.Context) error { var g errgroup.Group - for platform, archive := range ctx.Archives { - archive := archive + for platform, folder := range ctx.Folders { + folder := folder platform := platform g.Go(func() error { - return create(ctx, platform, archive) + return create(ctx, platform, folder) }) } return g.Wait() @@ -58,10 +58,16 @@ func create(ctx *context.Context, platform, name string) error { return err } } - var binary = ctx.Config.Build.Binary + ext.For(platform) - if err := archive.Add(binary, filepath.Join(folder, binary)); err != nil { + var path = filepath.Join(ctx.Config.Dist, name) + binaries, err := ioutil.ReadDir(path) + if err != nil { return err } + for _, binary := range binaries { + if err := archive.Add(binary.Name(), filepath.Join(path, binary.Name())); err != nil { + return err + } + } ctx.AddArtifact(file.Name()) return nil } diff --git a/pipeline/archive/archive_test.go b/pipeline/archive/archive_test.go index 62ce31e42..38e2a5d95 100644 --- a/pipeline/archive/archive_test.go +++ b/pipeline/archive/archive_test.go @@ -27,23 +27,21 @@ func TestRunPipe(t *testing.T) { }() var dist = filepath.Join(folder, "dist") assert.NoError(os.Mkdir(dist, 0755)) - assert.NoError(os.Mkdir(filepath.Join(dist, "mybin"), 0755)) - _, err = os.Create(filepath.Join(dist, "mybin", "mybin")) + assert.NoError(os.Mkdir(filepath.Join(dist, "mybin_darwin_amd64"), 0755)) + assert.NoError(os.Mkdir(filepath.Join(dist, "mybin_windows_amd64"), 0755)) + _, err = os.Create(filepath.Join(dist, "mybin_darwin_amd64", "mybin")) assert.NoError(err) - _, err = os.Create(filepath.Join(dist, "mybin", "mybin.exe")) + _, err = os.Create(filepath.Join(dist, "mybin_windows_amd64", "mybin.exe")) assert.NoError(err) _, err = os.Create(filepath.Join(folder, "README.md")) assert.NoError(err) var ctx = &context.Context{ - Archives: map[string]string{ - "darwinamd64": "mybin", - "windowsamd64": "mybin", + Folders: map[string]string{ + "darwinamd64": "mybin_darwin_amd64", + "windowsamd64": "mybin_windows_amd64", }, Config: config.Project{ Dist: dist, - Build: config.Build{ - Binary: "mybin", - }, Archive: config.Archive{ Files: []string{ "README.*", @@ -68,9 +66,8 @@ func TestRunPipe(t *testing.T) { func TestRunPipeDistRemoved(t *testing.T) { var assert = assert.New(t) var ctx = &context.Context{ - Archives: map[string]string{ - "darwinamd64": "mybin", - "windowsamd64": "mybin", + Folders: map[string]string{ + "darwinamd64": "whatever", }, Config: config.Project{ Dist: "/path/nope", @@ -85,8 +82,8 @@ func TestRunPipeDistRemoved(t *testing.T) { func TestRunPipeInvalidGlob(t *testing.T) { var assert = assert.New(t) var ctx = &context.Context{ - Archives: map[string]string{ - "windowsamd64": "mybin", + Folders: map[string]string{ + "windowsamd64": "whatever", }, Config: config.Project{ Dist: "/tmp", @@ -113,7 +110,7 @@ func TestRunPipeGlobFailsToAdd(t *testing.T) { assert.NoError(os.MkdirAll(filepath.Join(folder, "folder", "another"), 0755)) var ctx = &context.Context{ - Archives: map[string]string{ + Folders: map[string]string{ "windows386": "mybin", }, Config: config.Project{ @@ -127,18 +124,3 @@ func TestRunPipeGlobFailsToAdd(t *testing.T) { } assert.Error(Pipe{}.Run(ctx)) } - -func TestRunPipeBinaryDontExist(t *testing.T) { - var assert = assert.New(t) - folder, err := ioutil.TempDir("", "archivetest") - assert.NoError(err) - var ctx = &context.Context{ - Archives: map[string]string{ - "windows386": "mybin", - }, - Config: config.Project{ - Dist: folder, - }, - } - assert.Error(Pipe{}.Run(ctx)) -} diff --git a/pipeline/brew/brew.go b/pipeline/brew/brew.go index 5fd94b695..5aceddeb6 100644 --- a/pipeline/brew/brew.go +++ b/pipeline/brew/brew.go @@ -21,6 +21,8 @@ import ( // contain darwin and/or goarch doesn't contain amd64) var ErrNoDarwin64Build = errors.New("brew tap requires a darwin amd64 build") +const platform = "darwinamd64" + const formula = `class {{ .Name }} < Formula desc "{{ .Desc }}" homepage "{{ .Homepage }}" @@ -70,7 +72,6 @@ type templateData struct { Repo config.Repo // FIXME: will not work for anything but github right now. Tag string Version string - Binary string Caveats string File string SHA256 string @@ -106,7 +107,7 @@ func doRun(ctx *context.Context, client client.Client) error { log.Warn("skipped because release is marked as draft") return nil } - path := filepath.Join(ctx.Config.Brew.Folder, ctx.Config.Build.Binary+".rb") + var path = filepath.Join(ctx.Config.Brew.Folder, ctx.Config.ProjectName+".rb") log.WithField("formula", path). WithField("repo", ctx.Config.Brew.GitHub.String()). Info("pushing") @@ -127,7 +128,7 @@ func buildFormula(ctx *context.Context, client client.Client) (bytes.Buffer, err func doBuildFormula(data templateData) (bytes.Buffer, error) { var out bytes.Buffer - tmpl, err := template.New(data.Binary).Parse(formula) + tmpl, err := template.New(data.Name).Parse(formula) if err != nil { return out, err } @@ -136,23 +137,22 @@ func doBuildFormula(data templateData) (bytes.Buffer, error) { } func dataFor(ctx *context.Context, client client.Client) (result templateData, err error) { - var folder = ctx.Archives["darwinamd64"] + var folder = ctx.Folders[platform] if folder == "" { return result, ErrNoDarwin64Build } - var file = folder + "." + archiveformat.For(ctx, "darwinamd64") + var file = folder + "." + archiveformat.For(ctx, platform) sum, err := checksum.SHA256(filepath.Join(ctx.Config.Dist, file)) if err != nil { return } return templateData{ - Name: formulaNameFor(ctx.Config.Build.Binary), + Name: formulaNameFor(ctx.Config.ProjectName), Desc: ctx.Config.Brew.Description, Homepage: ctx.Config.Brew.Homepage, Repo: ctx.Config.Release.GitHub, Tag: ctx.Git.CurrentTag, Version: ctx.Version, - Binary: ctx.Config.Build.Binary, Caveats: ctx.Config.Brew.Caveats, File: file, SHA256: sum, diff --git a/pipeline/brew/brew_test.go b/pipeline/brew/brew_test.go index 146b099d7..e9392f1c6 100644 --- a/pipeline/brew/brew_test.go +++ b/pipeline/brew/brew_test.go @@ -29,7 +29,6 @@ func TestSimpleName(t *testing.T) { } var defaultTemplateData = templateData{ - Binary: "test", Desc: "Some desc", Homepage: "https://google.com", Name: "Test", @@ -104,7 +103,7 @@ func TestRunPipe(t *testing.T) { }, }, }, - Archives: map[string]string{ + Folders: map[string]string{ "darwinamd64": "bin", }, Publish: true, @@ -139,7 +138,7 @@ func TestRunPipeFormatOverride(t *testing.T) { }, }, }, - Archives: map[string]string{ + Folders: map[string]string{ "darwinamd64": "bin", }, Publish: true, @@ -167,7 +166,7 @@ func TestRunPipeArchiveDoesntExist(t *testing.T) { }, }, }, - Archives: map[string]string{ + Folders: map[string]string{ "darwinamd64": "bin", }, Publish: true, diff --git a/pipeline/build/build.go b/pipeline/build/build.go index 944e7181c..49a1e4874 100644 --- a/pipeline/build/build.go +++ b/pipeline/build/build.go @@ -10,8 +10,10 @@ import ( "strings" "github.com/apex/log" + "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" "github.com/goreleaser/goreleaser/internal/ext" + "github.com/goreleaser/goreleaser/internal/name" "golang.org/x/sync/errgroup" ) @@ -25,31 +27,36 @@ func (Pipe) Description() string { // Run the pipe func (Pipe) Run(ctx *context.Context) error { - if err := runHook(ctx.Config.Build.Env, ctx.Config.Build.Hooks.Pre); err != nil { + for _, build := range ctx.Config.Builds { + log.WithField("build", build).Debug("building") + if err := runPipeOnBuild(ctx, build); err != nil { + return err + } + } + return nil +} + +func runPipeOnBuild(ctx *context.Context, build config.Build) error { + if err := runHook(build.Env, build.Hooks.Pre); err != nil { return err } sem := make(chan bool, 4) var g errgroup.Group - for _, target := range buildTargets(ctx) { - name, err := nameFor(ctx, target) - if err != nil { - return err - } - ctx.Archives[target.String()] = name - + for _, target := range buildTargets(build) { sem <- true target := target + build := build g.Go(func() error { defer func() { <-sem }() - return build(ctx, name, target) + return doBuild(ctx, build, target) }) } if err := g.Wait(); err != nil { return err } - return runHook(ctx.Config.Build.Env, ctx.Config.Build.Hooks.Post) + return runHook(build.Env, build.Hooks.Post) } func runHook(env []string, hook string) error { @@ -61,23 +68,28 @@ func runHook(env []string, hook string) error { return run(runtimeTarget, cmd, env) } -func build(ctx *context.Context, name string, target buildTarget) error { - output := filepath.Join( - ctx.Config.Dist, - name, - ctx.Config.Build.Binary+ext.For(target.goos), - ) - log.WithField("binary", output).Info("building") - cmd := []string{"go", "build"} - if ctx.Config.Build.Flags != "" { - cmd = append(cmd, strings.Fields(ctx.Config.Build.Flags)...) - } - flags, err := ldflags(ctx) +func doBuild(ctx *context.Context, build config.Build, target buildTarget) error { + folder, err := name.For(ctx, target.goos, target.goarch, target.goarm) if err != nil { return err } - cmd = append(cmd, "-ldflags="+flags, "-o", output, ctx.Config.Build.Main) - return run(target, cmd, ctx.Config.Build.Env) + ctx.AddFolder(target.String(), folder) + var binary = filepath.Join( + ctx.Config.Dist, + folder, + build.Binary+ext.For(target.goos), + ) + log.WithField("binary", binary).Info("building") + cmd := []string{"go", "build"} + if build.Flags != "" { + cmd = append(cmd, strings.Fields(build.Flags)...) + } + flags, err := ldflags(ctx, build) + if err != nil { + return err + } + cmd = append(cmd, "-ldflags="+flags, "-o", binary, build.Main) + return run(target, cmd, build.Env) } func run(target buildTarget, command, env []string) error { diff --git a/pipeline/build/build_test.go b/pipeline/build/build_test.go index 3053e4a4b..e2f1938b9 100644 --- a/pipeline/build/build_test.go +++ b/pipeline/build/build_test.go @@ -29,16 +29,19 @@ func TestRunInvalidCommand(t *testing.T) { func TestBuild(t *testing.T) { assert := assert.New(t) var config = config.Project{ - Build: config.Build{ - Binary: "testing", - Flags: "-n", - Env: []string{"BLAH=1"}, + Builds: []config.Build{ + { + Binary: "testing", + Flags: "-n", + Env: []string{"BLAH=1"}, + }, }, } var ctx = &context.Context{ - Config: config, + Config: config, + Folders: map[string]string{}, } - assert.NoError(build(ctx, "build_test", runtimeTarget)) + assert.NoError(doBuild(ctx, ctx.Config.Builds[0], runtimeTarget)) } func TestRunFullPipe(t *testing.T) { @@ -50,25 +53,27 @@ func TestRunFullPipe(t *testing.T) { var post = filepath.Join(folder, "post") var config = config.Project{ Dist: folder, - Build: config.Build{ - Binary: "testing", - Flags: "-v", - Ldflags: "-X main.test=testing", - Hooks: config.Hooks{ - Pre: "touch " + pre, - Post: "touch " + post, - }, - Goos: []string{ - runtime.GOOS, - }, - Goarch: []string{ - runtime.GOARCH, + Builds: []config.Build{ + { + Binary: "testing", + Flags: "-v", + Ldflags: "-X main.test=testing", + Hooks: config.Hooks{ + Pre: "touch " + pre, + Post: "touch " + post, + }, + Goos: []string{ + runtime.GOOS, + }, + Goarch: []string{ + runtime.GOARCH, + }, }, }, } var ctx = &context.Context{ - Config: config, - Archives: map[string]string{}, + Config: config, + Folders: map[string]string{}, } assert.NoError(Pipe{}.Run(ctx)) assert.True(exists(binary), binary) @@ -83,25 +88,27 @@ func TestRunPipeArmBuilds(t *testing.T) { var binary = filepath.Join(folder, "armtesting") var config = config.Project{ Dist: folder, - Build: config.Build{ - Binary: "armtesting", - Flags: "-v", - Ldflags: "-X main.test=armtesting", - Goos: []string{ - "linux", - }, - Goarch: []string{ - "arm", - "arm64", - }, - Goarm: []string{ - "6", + Builds: []config.Build{ + { + Binary: "armtesting", + Flags: "-v", + Ldflags: "-X main.test=armtesting", + Goos: []string{ + "linux", + }, + Goarch: []string{ + "arm", + "arm64", + }, + Goarm: []string{ + "6", + }, }, }, } var ctx = &context.Context{ - Config: config, - Archives: map[string]string{}, + Config: config, + Folders: map[string]string{}, } assert.NoError(Pipe{}.Run(ctx)) assert.True(exists(binary), binary) @@ -110,19 +117,21 @@ func TestRunPipeArmBuilds(t *testing.T) { func TestBuildFailed(t *testing.T) { assert := assert.New(t) var config = config.Project{ - Build: config.Build{ - Flags: "-flag-that-dont-exists-to-force-failure", - Goos: []string{ - runtime.GOOS, - }, - Goarch: []string{ - runtime.GOARCH, + Builds: []config.Build{ + { + Flags: "-flag-that-dont-exists-to-force-failure", + Goos: []string{ + runtime.GOOS, + }, + Goarch: []string{ + runtime.GOARCH, + }, }, }, } var ctx = &context.Context{ - Config: config, - Archives: map[string]string{}, + Config: config, + Folders: map[string]string{}, } assert.Error(Pipe{}.Run(ctx)) } @@ -130,19 +139,21 @@ func TestBuildFailed(t *testing.T) { func TestRunPipeWithInvalidOS(t *testing.T) { assert := assert.New(t) var config = config.Project{ - Build: config.Build{ - Flags: "-v", - Goos: []string{ - "windows", - }, - Goarch: []string{ - "arm", + Builds: []config.Build{ + { + Flags: "-v", + Goos: []string{ + "windows", + }, + Goarch: []string{ + "arm", + }, }, }, } var ctx = &context.Context{ - Config: config, - Archives: map[string]string{}, + Config: config, + Folders: map[string]string{}, } assert.NoError(Pipe{}.Run(ctx)) } @@ -151,14 +162,16 @@ func TestRunInvalidNametemplate(t *testing.T) { var assert = assert.New(t) var ctx = &context.Context{ Config: config.Project{ - Build: config.Build{ - Binary: "nametest", - Flags: "-v", - Goos: []string{ - runtime.GOOS, - }, - Goarch: []string{ - runtime.GOARCH, + Builds: []config.Build{ + { + Binary: "nametest", + Flags: "-v", + Goos: []string{ + runtime.GOOS, + }, + Goarch: []string{ + runtime.GOARCH, + }, }, }, Archive: config.Archive{ @@ -172,17 +185,19 @@ func TestRunInvalidNametemplate(t *testing.T) { func TestRunInvalidLdflags(t *testing.T) { var assert = assert.New(t) var ctx = &context.Context{ - Archives: map[string]string{}, + Folders: map[string]string{}, Config: config.Project{ - Build: config.Build{ - Binary: "nametest", - Flags: "-v", - Ldflags: "-s -w -X main.version={{.Version}", - Goos: []string{ - runtime.GOOS, - }, - Goarch: []string{ - runtime.GOARCH, + Builds: []config.Build{ + { + Binary: "nametest", + Flags: "-v", + Ldflags: "-s -w -X main.version={{.Version}", + Goos: []string{ + runtime.GOOS, + }, + Goarch: []string{ + runtime.GOARCH, + }, }, }, }, @@ -193,26 +208,28 @@ func TestRunInvalidLdflags(t *testing.T) { func TestRunPipeFailingHooks(t *testing.T) { assert := assert.New(t) var config = config.Project{ - Build: config.Build{ - Hooks: config.Hooks{}, - Goos: []string{ - runtime.GOOS, - }, - Goarch: []string{ - runtime.GOARCH, + Builds: []config.Build{ + { + Hooks: config.Hooks{}, + Goos: []string{ + runtime.GOOS, + }, + Goarch: []string{ + runtime.GOARCH, + }, }, }, } var ctx = &context.Context{ - Config: config, - Archives: map[string]string{}, + Config: config, + Folders: map[string]string{}, } t.Run("pre-hook", func(t *testing.T) { - ctx.Config.Build.Hooks.Pre = "exit 1" + ctx.Config.Builds[0].Hooks.Pre = "exit 1" assert.Error(Pipe{}.Run(ctx)) }) t.Run("post-hook", func(t *testing.T) { - ctx.Config.Build.Hooks.Post = "exit 1" + ctx.Config.Builds[0].Hooks.Post = "exit 1" assert.Error(Pipe{}.Run(ctx)) }) } diff --git a/pipeline/build/ldflags.go b/pipeline/build/ldflags.go index 2dacfb3ed..cf6633616 100644 --- a/pipeline/build/ldflags.go +++ b/pipeline/build/ldflags.go @@ -5,6 +5,7 @@ import ( "text/template" "time" + "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" ) @@ -15,7 +16,7 @@ type ldflagsData struct { Version string } -func ldflags(ctx *context.Context) (string, error) { +func ldflags(ctx *context.Context, build config.Build) (string, error) { var data = ldflagsData{ Commit: ctx.Git.Commit, Tag: ctx.Git.CurrentTag, @@ -23,7 +24,7 @@ func ldflags(ctx *context.Context) (string, error) { Date: time.Now().UTC().Format(time.RFC3339), } var out bytes.Buffer - t, err := template.New("ldflags").Parse(ctx.Config.Build.Ldflags) + t, err := template.New("ldflags").Parse(build.Ldflags) if err != nil { return "", err } diff --git a/pipeline/build/ldflags_test.go b/pipeline/build/ldflags_test.go index 4db97cc7a..ef01039ec 100644 --- a/pipeline/build/ldflags_test.go +++ b/pipeline/build/ldflags_test.go @@ -11,8 +11,10 @@ import ( func TestLdFlagsFullTemplate(t *testing.T) { assert := assert.New(t) var config = config.Project{ - Build: config.Build{ - Ldflags: "-s -w -X main.version={{.Version}} -X main.tag={{.Tag}} -X main.date={{.Date}} -X main.commit={{.Commit}}", + Builds: []config.Build{ + { + Ldflags: "-s -w -X main.version={{.Version}} -X main.tag={{.Tag}} -X main.date={{.Date}} -X main.commit={{.Commit}}", + }, }, } var ctx = &context.Context{ @@ -23,7 +25,7 @@ func TestLdFlagsFullTemplate(t *testing.T) { Version: "1.2.3", Config: config, } - flags, err := ldflags(ctx) + flags, err := ldflags(ctx, ctx.Config.Builds[0]) assert.NoError(err) assert.Contains(flags, "-s -w") assert.Contains(flags, "-X main.version=1.2.3") @@ -35,14 +37,14 @@ func TestLdFlagsFullTemplate(t *testing.T) { func TestInvalidTemplate(t *testing.T) { assert := assert.New(t) var config = config.Project{ - Build: config.Build{ - Ldflags: "{invalid{.Template}}}{{}}}", + Builds: []config.Build{ + {Ldflags: "{invalid{.Template}}}{{}}}"}, }, } var ctx = &context.Context{ Config: config, } - flags, err := ldflags(ctx) + flags, err := ldflags(ctx, ctx.Config.Builds[0]) assert.Error(err) assert.Equal(flags, "") } diff --git a/pipeline/build/name.go b/pipeline/build/name.go deleted file mode 100644 index 89c582b17..000000000 --- a/pipeline/build/name.go +++ /dev/null @@ -1,44 +0,0 @@ -package build - -import ( - "bytes" - "text/template" - - "github.com/goreleaser/goreleaser/context" -) - -type nameData struct { - Os string - Arch string - Arm string - Version string - Tag string - Binary string -} - -func nameFor(ctx *context.Context, target buildTarget) (string, error) { - var data = nameData{ - Os: replace(ctx.Config.Archive.Replacements, target.goos), - Arch: replace(ctx.Config.Archive.Replacements, target.goarch), - Arm: replace(ctx.Config.Archive.Replacements, target.goarm), - Version: ctx.Version, - Tag: ctx.Git.CurrentTag, - Binary: ctx.Config.Build.Binary, - } - - var out bytes.Buffer - t, err := template.New(data.Binary).Parse(ctx.Config.Archive.NameTemplate) - if err != nil { - return "", err - } - err = t.Execute(&out, data) - return out.String(), err -} - -func replace(replacements map[string]string, original string) string { - result := replacements[original] - if result == "" { - return original - } - return result -} diff --git a/pipeline/build/target.go b/pipeline/build/target.go index 1bdce6894..826c39d88 100644 --- a/pipeline/build/target.go +++ b/pipeline/build/target.go @@ -5,7 +5,7 @@ import ( "runtime" "github.com/apex/log" - "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/config" ) var runtimeTarget = buildTarget{runtime.GOOS, runtime.GOARCH, ""} @@ -23,14 +23,14 @@ func (t buildTarget) PrettyString() string { return fmt.Sprintf("%v/%v%v", t.goos, t.goarch, t.goarm) } -func buildTargets(ctx *context.Context) (targets []buildTarget) { - for _, target := range allBuildTargets(ctx) { +func buildTargets(build config.Build) (targets []buildTarget) { + for _, target := range allBuildTargets(build) { if !valid(target) { log.WithField("target", target.PrettyString()). Warn("skipped invalid build") continue } - if ignored(ctx, target) { + if ignored(build, target) { log.WithField("target", target.PrettyString()). Warn("skipped ignored build") continue @@ -40,11 +40,11 @@ func buildTargets(ctx *context.Context) (targets []buildTarget) { return } -func allBuildTargets(ctx *context.Context) (targets []buildTarget) { - for _, goos := range ctx.Config.Build.Goos { - for _, goarch := range ctx.Config.Build.Goarch { +func allBuildTargets(build config.Build) (targets []buildTarget) { + for _, goos := range build.Goos { + for _, goarch := range build.Goarch { if goarch == "arm" { - for _, goarm := range ctx.Config.Build.Goarm { + for _, goarm := range build.Goarm { targets = append(targets, buildTarget{goos, goarch, goarm}) } continue @@ -55,8 +55,8 @@ func allBuildTargets(ctx *context.Context) (targets []buildTarget) { return } -func ignored(ctx *context.Context, target buildTarget) bool { - for _, ig := range ctx.Config.Build.Ignore { +func ignored(build config.Build, target buildTarget) bool { + for _, ig := range build.Ignore { var ignored = buildTarget{ig.Goos, ig.Goarch, ig.Goarm} if ignored == target { return true diff --git a/pipeline/build/target_test.go b/pipeline/build/target_test.go index cd6b45158..242f91579 100644 --- a/pipeline/build/target_test.go +++ b/pipeline/build/target_test.go @@ -5,40 +5,35 @@ import ( "testing" "github.com/goreleaser/goreleaser/config" - "github.com/goreleaser/goreleaser/context" "github.com/stretchr/testify/assert" ) func TestAllBuildTargets(t *testing.T) { var assert = assert.New(t) - var ctx = &context.Context{ - Config: config.Project{ - Build: config.Build{ - Goos: []string{ - "linux", - "darwin", - "freebsd", - }, - Goarch: []string{ - "386", - "amd64", - "arm", - "arm64", - }, - Goarm: []string{ - "6", - "7", - }, - Ignore: []config.IgnoredBuild{ - { - Goos: "darwin", - Goarch: "386", - }, { - Goos: "linux", - Goarch: "arm", - Goarm: "7", - }, - }, + var build = config.Build{ + Goos: []string{ + "linux", + "darwin", + "freebsd", + }, + Goarch: []string{ + "386", + "amd64", + "arm", + "arm64", + }, + Goarm: []string{ + "6", + "7", + }, + Ignore: []config.IgnoredBuild{ + { + Goos: "darwin", + Goarch: "386", + }, { + Goos: "linux", + Goarch: "arm", + Goarm: "7", }, }, } @@ -52,7 +47,7 @@ func TestAllBuildTargets(t *testing.T) { {"freebsd", "amd64", ""}, {"freebsd", "arm", "6"}, {"freebsd", "arm", "7"}, - }, buildTargets(ctx)) + }, buildTargets(build)) } func TestValidGoosGoarchCombos(t *testing.T) { diff --git a/pipeline/checksums/checksums.go b/pipeline/checksums/checksums.go index abf33b39a..717b79a21 100644 --- a/pipeline/checksums/checksums.go +++ b/pipeline/checksums/checksums.go @@ -26,7 +26,7 @@ func (Pipe) Run(ctx *context.Context) (err error) { file, err := os.OpenFile( filepath.Join( ctx.Config.Dist, - fmt.Sprintf("%v_checksums.txt", ctx.Config.Build.Binary), + fmt.Sprintf("%v_checksums.txt", ctx.Config.ProjectName), ), os.O_APPEND|os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644, diff --git a/pipeline/checksums/checksums_test.go b/pipeline/checksums/checksums_test.go index b050d9946..4cd8c5bd9 100644 --- a/pipeline/checksums/checksums_test.go +++ b/pipeline/checksums/checksums_test.go @@ -24,10 +24,8 @@ func TestPipe(t *testing.T) { assert.NoError(ioutil.WriteFile(file, []byte("some string"), 0644)) var ctx = &context.Context{ Config: config.Project{ - Dist: folder, - Build: config.Build{ - Binary: binary, - }, + Dist: folder, + ProjectName: binary, }, } ctx.AddArtifact(file) diff --git a/pipeline/defaults/defaults.go b/pipeline/defaults/defaults.go index d38f5b2e9..0289b52e6 100644 --- a/pipeline/defaults/defaults.go +++ b/pipeline/defaults/defaults.go @@ -4,7 +4,10 @@ package defaults import ( "fmt" + "strings" + "github.com/apex/log" + "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" ) @@ -31,14 +34,44 @@ func (Pipe) Run(ctx *context.Context) error { if err := setReleaseDefaults(ctx); err != nil { return err } + if ctx.Config.ProjectName == "" { + ctx.Config.ProjectName = ctx.Config.Release.GitHub.Name + } setBuildDefaults(ctx) if ctx.Config.Brew.Install == "" { - ctx.Config.Brew.Install = fmt.Sprintf( - `bin.install "%s"`, - ctx.Config.Build.Binary, - ) + var installs []string + for _, build := range ctx.Config.Builds { + if !isBrewBuild(build) { + continue + } + installs = append( + installs, + fmt.Sprintf(`bin.install "%s"`, build.Binary), + ) + } + ctx.Config.Brew.Install = strings.Join(installs, "\n") } - return setArchiveDefaults(ctx) + err := setArchiveDefaults(ctx) + log.WithField("config", ctx.Config).Debug("defaults set") + return err +} + +func isBrewBuild(build config.Build) bool { + for _, ignore := range build.Ignore { + if ignore.Goos == "darwin" && ignore.Goarch == "amd64" { + return false + } + } + return contains(build.Goos, "darwin") && contains(build.Goarch, "amd64") +} + +func contains(ss []string, s string) bool { + for _, zs := range ss { + if zs == s { + return true + } + } + return false } func setReleaseDefaults(ctx *context.Context) error { @@ -54,24 +87,36 @@ func setReleaseDefaults(ctx *context.Context) error { } func setBuildDefaults(ctx *context.Context) { - if ctx.Config.Build.Binary == "" { - ctx.Config.Build.Binary = ctx.Config.Release.GitHub.Name + for i, build := range ctx.Config.Builds { + ctx.Config.Builds[i] = buildWithDefaults(ctx, build) } - if ctx.Config.Build.Main == "" { - ctx.Config.Build.Main = "." + if len(ctx.Config.Builds) == 0 { + ctx.Config.Builds = []config.Build{ + buildWithDefaults(ctx, ctx.Config.SingleBuild), + } } - if len(ctx.Config.Build.Goos) == 0 { - ctx.Config.Build.Goos = []string{"linux", "darwin"} +} + +func buildWithDefaults(ctx *context.Context, build config.Build) config.Build { + if build.Binary == "" { + build.Binary = ctx.Config.Release.GitHub.Name } - if len(ctx.Config.Build.Goarch) == 0 { - ctx.Config.Build.Goarch = []string{"amd64", "386"} + if build.Main == "" { + build.Main = "." } - if len(ctx.Config.Build.Goarm) == 0 { - ctx.Config.Build.Goarm = []string{"6"} + if len(build.Goos) == 0 { + build.Goos = []string{"linux", "darwin"} } - if ctx.Config.Build.Ldflags == "" { - ctx.Config.Build.Ldflags = "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}" + if len(build.Goarch) == 0 { + build.Goarch = []string{"amd64", "386"} } + if len(build.Goarm) == 0 { + build.Goarm = []string{"6"} + } + if build.Ldflags == "" { + build.Ldflags = "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}" + } + return build } func setArchiveDefaults(ctx *context.Context) error { diff --git a/pipeline/defaults/defaults_test.go b/pipeline/defaults/defaults_test.go index bdd9051a9..9d4ff3b49 100644 --- a/pipeline/defaults/defaults_test.go +++ b/pipeline/defaults/defaults_test.go @@ -15,33 +15,32 @@ func TestDescription(t *testing.T) { } func TestFillBasicData(t *testing.T) { - assert := assert.New(t) - + var assert = assert.New(t) var ctx = &context.Context{ Config: config.Project{}, } assert.NoError(Pipe{}.Run(ctx)) - assert.Equal("goreleaser", ctx.Config.Release.GitHub.Owner) assert.Equal("goreleaser", ctx.Config.Release.GitHub.Name) - assert.Equal("goreleaser", ctx.Config.Build.Binary) - assert.Equal(".", ctx.Config.Build.Main) + assert.NotEmpty(ctx.Config.Builds) + assert.Equal("goreleaser", ctx.Config.Builds[0].Binary) + assert.Equal(".", ctx.Config.Builds[0].Main) + assert.Contains(ctx.Config.Builds[0].Goos, "darwin") + assert.Contains(ctx.Config.Builds[0].Goos, "linux") + assert.Contains(ctx.Config.Builds[0].Goarch, "386") + assert.Contains(ctx.Config.Builds[0].Goarch, "amd64") assert.Equal("tar.gz", ctx.Config.Archive.Format) - assert.Contains(ctx.Config.Build.Goos, "darwin") - assert.Contains(ctx.Config.Build.Goos, "linux") - assert.Contains(ctx.Config.Build.Goarch, "386") - assert.Contains(ctx.Config.Build.Goarch, "amd64") assert.Contains(ctx.Config.Brew.Install, "bin.install \"goreleaser\"") assert.NotEmpty( ctx.Config.Archive.NameTemplate, - ctx.Config.Build.Ldflags, + ctx.Config.Builds[0].Ldflags, ctx.Config.Archive.Files, ) } func TestFillPartial(t *testing.T) { - assert := assert.New(t) + var assert = assert.New(t) var ctx = &context.Context{ Config: config.Project{ @@ -56,10 +55,36 @@ func TestFillPartial(t *testing.T) { "glob/*", }, }, + Builds: []config.Build{ + {Binary: "testreleaser"}, + {Goos: []string{"linux"}}, + { + Binary: "another", + Ignore: []config.IgnoredBuild{ + {Goos: "darwin", Goarch: "amd64"}, + }, + }, + }, }, } assert.NoError(Pipe{}.Run(ctx)) assert.Len(ctx.Config.Archive.Files, 1) + assert.Equal(`bin.install "testreleaser"`, ctx.Config.Brew.Install) +} + +func TestFillSingleBuild(t *testing.T) { + var assert = assert.New(t) + + var ctx = &context.Context{ + Config: config.Project{ + SingleBuild: config.Build{ + Main: "testreleaser", + }, + }, + } + assert.NoError(Pipe{}.Run(ctx)) + assert.Len(ctx.Config.Builds, 1) + assert.Equal(ctx.Config.Builds[0].Binary, "goreleaser") } func TestNotAGitRepo(t *testing.T) { diff --git a/pipeline/fpm/fpm.go b/pipeline/fpm/fpm.go index b859826d0..0f8c8c1cf 100644 --- a/pipeline/fpm/fpm.go +++ b/pipeline/fpm/fpm.go @@ -3,19 +3,17 @@ package fpm import ( "errors" + "fmt" + "io/ioutil" "os/exec" "path/filepath" + "strings" "github.com/apex/log" "github.com/goreleaser/goreleaser/context" "golang.org/x/sync/errgroup" ) -var goarchToUnix = map[string]string{ - "386": "i386", - "amd64": "x86_64", -} - // ErrNoFPM is shown when fpm cannot be found in $PATH var ErrNoFPM = errors.New("fpm not present in $PATH") @@ -40,31 +38,38 @@ func (Pipe) Run(ctx *context.Context) error { var g errgroup.Group for _, format := range ctx.Config.FPM.Formats { - format := format - for _, goarch := range ctx.Config.Build.Goarch { - if ctx.Archives["linux"+goarch] == "" { + for key, folder := range ctx.Folders { + if !strings.Contains(key, "linux") { + log.WithField("key", key).Debug("skipped non-linux builds for fpm") continue } - archive := ctx.Archives["linux"+goarch] - arch := goarchToUnix[goarch] + folder := folder + format := format + arch := archFor(key) g.Go(func() error { - return create(ctx, format, archive, arch) + return create(ctx, format, folder, arch) }) } } return g.Wait() } -func create(ctx *context.Context, format, archive, arch string) error { - var path = filepath.Join(ctx.Config.Dist, archive) +func archFor(key string) string { + if strings.Contains(key, "386") { + return "i386" + } + return "x86_64" +} + +func create(ctx *context.Context, format, folder, arch string) error { + var path = filepath.Join(ctx.Config.Dist, folder) var file = path + "." + format - var name = ctx.Config.Build.Binary - log.WithField("file", file).Info("Creating") + log.WithField("file", file).Info("creating fpm archive") var options = []string{ "--input-type", "dir", "--output-type", format, - "--name", name, + "--name", ctx.Config.ProjectName, "--version", ctx.Version, "--architecture", arch, "--chdir", path, @@ -94,9 +99,21 @@ func create(ctx *context.Context, format, archive, arch string) error { options = append(options, "--conflicts", conflict) } - // This basically tells fpm to put the binary in the /usr/local/bin - // binary=/usr/local/bin/binary - options = append(options, name+"="+filepath.Join("/usr/local/bin", name)) + files, err := ioutil.ReadDir(path) + if err != nil { + return err + } + for _, file := range files { + // XXX: skip non executable files here? + // This basically tells fpm to put the binary in the /usr/local/bin + // binary=/usr/local/bin/binary + log.WithField("file", file.Name()).Debug("passed binary to fpm") + options = append(options, fmt.Sprintf( + "%s=%s", + file.Name(), + filepath.Join("/usr/local/bin", file.Name()), + )) + } if out, err := exec.Command("fpm", options...).CombinedOutput(); err != nil { return errors.New(string(out)) diff --git a/pipeline/fpm/fpm_test.go b/pipeline/fpm/fpm_test.go index 49f0ca59e..e376c6614 100644 --- a/pipeline/fpm/fpm_test.go +++ b/pipeline/fpm/fpm_test.go @@ -33,18 +33,14 @@ func TestRunPipe(t *testing.T) { _, err = os.Create(filepath.Join(dist, "mybin", "mybin")) assert.NoError(err) var ctx = &context.Context{ - Archives: map[string]string{ - "linuxamd64": "mybin", + Folders: map[string]string{ + "linuxamd64": "mybin", + "linux386": "mybin", + "darwinamd64": "anotherbin", }, Config: config.Project{ - Dist: dist, - Build: config.Build{ - Goarch: []string{ - "amd64", - "i386", - }, - Binary: "mybin", - }, + ProjectName: "mybin", + Dist: dist, FPM: config.FPM{ Formats: []string{"deb"}, Dependencies: []string{"make"}, @@ -85,18 +81,11 @@ func TestCreateFileDoesntExist(t *testing.T) { assert.NoError(os.Mkdir(dist, 0755)) assert.NoError(os.Mkdir(filepath.Join(dist, "mybin"), 0755)) var ctx = &context.Context{ - Archives: map[string]string{ + Folders: map[string]string{ "linuxamd64": "mybin", }, Config: config.Project{ Dist: dist, - Build: config.Build{ - Goarch: []string{ - "amd64", - "i386", - }, - Binary: "mybin", - }, FPM: config.FPM{ Formats: []string{"deb"}, }, @@ -104,3 +93,12 @@ func TestCreateFileDoesntExist(t *testing.T) { } assert.Error(Pipe{}.Run(ctx)) } + +func TestCreatePathDoesntExist(t *testing.T) { + var assert = assert.New(t) + var ctx = &context.Context{} + assert.Contains( + create(ctx, "tar.gz", "/nope/noooo", "windowsarm64").Error(), + "no such file", + ) +}