1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-07-09 01:16:05 +02:00

Merge pull request #267 from goreleaser/builds

multiple builds
This commit is contained in:
Carlos Alexandro Becker
2017-07-03 09:05:18 -03:00
committed by GitHub
25 changed files with 590 additions and 442 deletions

View File

@ -61,8 +61,8 @@ By default GoReleaser will build the current directory, but you can change the b
```yml ```yml
# goreleaser.yml # goreleaser.yml
# Build customization # Build customization
build: builds:
binary: drum-roll - binary: drum-roll
goos: goos:
- windows - windows
- darwin - darwin
@ -75,15 +75,15 @@ 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`. 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. 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. Another useful feature is to add files to archives, this is very useful for integrating assets like resource files.
```yml ```yml
# goreleaser.yml # goreleaser.yml
# Build customization # Build customization
build: builds:
main: main.go - main: main.go
binary: drum-roll binary: drum-roll
goos: goos:
- windows - windows
@ -172,11 +172,22 @@ defaults are sensible and fit for most projects.
We'll cover all customizations available bellow: 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 ### Build customization
```yml ```yml
# goreleaser.yml # goreleaser.yml
build: builds:
# You can have multiple builds, its a common yaml list
-
# Path to main.go file or main package. # Path to main.go file or main package.
# Default is `.` # Default is `.`
main: ./cmd/main.go main: ./cmd/main.go
@ -252,14 +263,14 @@ archive:
# You can change the name of the archive. # You can change the name of the archive.
# This is parsed with Golang template engine and the following variables # This is parsed with Golang template engine and the following variables
# are available: # are available:
# - Binary # - ProjectName
# - Tag # - Tag
# - Version (Tag with the `v` prefix stripped) # - Version (Tag with the `v` prefix stripped)
# - Os # - Os
# - Arch # - Arch
# - Arm (ARM version) # - Arm (ARM version)
# The default is `{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}` # The default is `{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}`
name_template: "{{.Binary}}_{{.Version}}_{{.Os}}_{{.Arch}}" name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
# Archive format. Valid options are `tar.gz` and `zip`. # Archive format. Valid options are `tar.gz` and `zip`.
# Default is `tar.gz` # Default is `tar.gz`

View File

@ -7,6 +7,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"github.com/apex/log"
yaml "gopkg.in/yaml.v1" yaml "gopkg.in/yaml.v1"
) )
@ -99,13 +100,17 @@ type Snapshot struct {
// Project includes all project configuration // Project includes all project configuration
type Project struct { type Project struct {
ProjectName string `yaml:"project_name,omitempty"`
Release Release `yaml:",omitempty"` Release Release `yaml:",omitempty"`
Brew Homebrew `yaml:",omitempty"` Brew Homebrew `yaml:",omitempty"`
Build Build `yaml:",omitempty"` Builds []Build `yaml:",omitempty"`
Archive Archive `yaml:",omitempty"` Archive Archive `yaml:",omitempty"`
FPM FPM `yaml:",omitempty"` FPM FPM `yaml:",omitempty"`
Snapshot Snapshot `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 // test only property indicating the path to the dist folder
Dist string `yaml:"-"` Dist string `yaml:"-"`
} }
@ -126,5 +131,6 @@ func LoadReader(fd io.Reader) (config Project, err error) {
return config, err return config, err
} }
err = yaml.Unmarshal(data, &config) err = yaml.Unmarshal(data, &config)
log.WithField("config", config).Debug("loaded config file")
return return
} }

View File

@ -27,7 +27,7 @@ type Context struct {
Config config.Project Config config.Project
Token string Token string
Git GitInfo Git GitInfo
Archives map[string]string Folders map[string]string
Artifacts []string Artifacts []string
ReleaseNotes string ReleaseNotes string
Version string Version string
@ -36,16 +36,25 @@ type Context struct {
Snapshot bool Snapshot bool
} }
var lock sync.Mutex var artifactsLock sync.Mutex
var foldersLock sync.Mutex
// AddArtifact adds a file to upload list // AddArtifact adds a file to upload list
func (ctx *Context) AddArtifact(file string) { func (ctx *Context) AddArtifact(file string) {
lock.Lock() artifactsLock.Lock()
defer lock.Unlock() defer artifactsLock.Unlock()
file = strings.TrimPrefix(file, ctx.Config.Dist) file = strings.TrimPrefix(file, ctx.Config.Dist)
file = strings.Replace(file, "/", "", -1) file = strings.Replace(file, "/", "", -1)
ctx.Artifacts = append(ctx.Artifacts, file) 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 // New context
@ -53,6 +62,6 @@ func New(config config.Project) *Context {
return &Context{ return &Context{
Context: ctx.Background(), Context: ctx.Background(),
Config: config, Config: config,
Archives: map[string]string{}, Folders: map[string]string{},
} }
} }

View File

@ -31,3 +31,27 @@ func TestMultipleArtifactAdds(t *testing.T) {
assert.Len(ctx.Artifacts, len(list)) assert.Len(ctx.Artifacts, len(list))
assert.Contains(ctx.Artifacts, "a", "b", "c", "d") 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))
}

View File

@ -1,6 +1,7 @@
homepage: &homepage http://goreleaser.github.io homepage: &homepage http://goreleaser.github.io
description: &description Deliver Go binaries as fast and easily as possible description: &description Deliver Go binaries as fast and easily as possible
build: builds:
-
env: env:
- CGO_ENABLED=0 - CGO_ENABLED=0
goos: goos:

View File

@ -36,7 +36,7 @@ func (c *githubClient) CreateFile(
}, },
Content: content.Bytes(), Content: content.Bytes(),
Message: github.String( Message: github.String(
ctx.Config.Build.Binary + " version " + ctx.Git.CurrentTag, ctx.Config.ProjectName + " version " + ctx.Git.CurrentTag,
), ),
} }

49
internal/name/name.go Normal file
View File

@ -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
}

View File

@ -1,4 +1,4 @@
package build package name
import ( import (
"testing" "testing"
@ -20,9 +20,7 @@ func TestNameFor(t *testing.T) {
"amd64": "x86_64", "amd64": "x86_64",
}, },
}, },
Build: config.Build{ ProjectName: "test",
Binary: "test",
},
} }
var ctx = &context.Context{ var ctx = &context.Context{
Config: config, 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.NoError(err)
assert.Equal("test_Darwin_x86_64_v1.2.3_1.2.3", name) assert.Equal("test_Darwin_x86_64_v1.2.3_1.2.3", name)
} }
func TestInvalidNameTemplate(t *testing.T) { func TestInvalidNameTemplate(t *testing.T) {
assert := assert.New(t) var assert = assert.New(t)
var ctx = &context.Context{
var config = config.Project{ Config: config.Project{
Archive: config.Archive{ Archive: config.Archive{
NameTemplate: "{{.Binary}_{{.Os}}_{{.Arch}}_{{.Version}}", NameTemplate: "{{.Binary}_{{.Os}}_{{.Arch}}_{{.Version}}",
}, },
Build: config.Build{ ProjectName: "test",
Binary: "test",
}, },
}
var ctx = &context.Context{
Config: config,
Git: context.GitInfo{ Git: context.GitInfo{
CurrentTag: "v1.2.3", CurrentTag: "v1.2.3",
}, },
} }
_, err := nameFor(ctx, buildTarget{"darwin", "amd64", ""}) _, err := For(ctx, "darwin", "amd64", "")
assert.Error(err) assert.Error(err)
} }
@ -66,19 +60,20 @@ func TestNameDefaltTemplate(t *testing.T) {
Archive: config.Archive{ Archive: config.Archive{
NameTemplate: defaults.NameTemplate, NameTemplate: defaults.NameTemplate,
}, },
Build: config.Build{ ProjectName: "test",
Binary: "test",
},
}, },
Version: "1.2.3", Version: "1.2.3",
} }
type buildTarget struct {
goos, goarch, goarm string
}
for key, target := range map[string]buildTarget{ for key, target := range map[string]buildTarget{
"test_1.2.3_darwin_amd64": {"darwin", "amd64", ""}, "test_1.2.3_darwin_amd64": {"darwin", "amd64", ""},
"test_1.2.3_linux_arm64": {"linux", "arm64", ""}, "test_1.2.3_linux_arm64": {"linux", "arm64", ""},
"test_1.2.3_linux_armv7": {"linux", "arm", "7"}, "test_1.2.3_linux_armv7": {"linux", "arm", "7"},
} { } {
t.Run(key, func(t *testing.T) { 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.NoError(err)
assert.Equal(key, name) assert.Equal(key, name)
}) })

View File

@ -4,6 +4,7 @@
package archive package archive
import ( import (
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@ -11,7 +12,6 @@ import (
"github.com/goreleaser/archive" "github.com/goreleaser/archive"
"github.com/goreleaser/goreleaser/context" "github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/archiveformat" "github.com/goreleaser/goreleaser/internal/archiveformat"
"github.com/goreleaser/goreleaser/internal/ext"
"github.com/mattn/go-zglob" "github.com/mattn/go-zglob"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
@ -27,11 +27,11 @@ func (Pipe) Description() string {
// Run the pipe // Run the pipe
func (Pipe) Run(ctx *context.Context) error { func (Pipe) Run(ctx *context.Context) error {
var g errgroup.Group var g errgroup.Group
for platform, archive := range ctx.Archives { for platform, folder := range ctx.Folders {
archive := archive folder := folder
platform := platform platform := platform
g.Go(func() error { g.Go(func() error {
return create(ctx, platform, archive) return create(ctx, platform, folder)
}) })
} }
return g.Wait() return g.Wait()
@ -58,10 +58,16 @@ func create(ctx *context.Context, platform, name string) error {
return err return err
} }
} }
var binary = ctx.Config.Build.Binary + ext.For(platform) var path = filepath.Join(ctx.Config.Dist, name)
if err := archive.Add(binary, filepath.Join(folder, binary)); err != nil { binaries, err := ioutil.ReadDir(path)
if err != nil {
return err 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()) ctx.AddArtifact(file.Name())
return nil return nil
} }

View File

@ -27,23 +27,21 @@ func TestRunPipe(t *testing.T) {
}() }()
var dist = filepath.Join(folder, "dist") var dist = filepath.Join(folder, "dist")
assert.NoError(os.Mkdir(dist, 0755)) assert.NoError(os.Mkdir(dist, 0755))
assert.NoError(os.Mkdir(filepath.Join(dist, "mybin"), 0755)) assert.NoError(os.Mkdir(filepath.Join(dist, "mybin_darwin_amd64"), 0755))
_, err = os.Create(filepath.Join(dist, "mybin", "mybin")) assert.NoError(os.Mkdir(filepath.Join(dist, "mybin_windows_amd64"), 0755))
_, err = os.Create(filepath.Join(dist, "mybin_darwin_amd64", "mybin"))
assert.NoError(err) 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) assert.NoError(err)
_, err = os.Create(filepath.Join(folder, "README.md")) _, err = os.Create(filepath.Join(folder, "README.md"))
assert.NoError(err) assert.NoError(err)
var ctx = &context.Context{ var ctx = &context.Context{
Archives: map[string]string{ Folders: map[string]string{
"darwinamd64": "mybin", "darwinamd64": "mybin_darwin_amd64",
"windowsamd64": "mybin", "windowsamd64": "mybin_windows_amd64",
}, },
Config: config.Project{ Config: config.Project{
Dist: dist, Dist: dist,
Build: config.Build{
Binary: "mybin",
},
Archive: config.Archive{ Archive: config.Archive{
Files: []string{ Files: []string{
"README.*", "README.*",
@ -68,9 +66,8 @@ func TestRunPipe(t *testing.T) {
func TestRunPipeDistRemoved(t *testing.T) { func TestRunPipeDistRemoved(t *testing.T) {
var assert = assert.New(t) var assert = assert.New(t)
var ctx = &context.Context{ var ctx = &context.Context{
Archives: map[string]string{ Folders: map[string]string{
"darwinamd64": "mybin", "darwinamd64": "whatever",
"windowsamd64": "mybin",
}, },
Config: config.Project{ Config: config.Project{
Dist: "/path/nope", Dist: "/path/nope",
@ -85,8 +82,8 @@ func TestRunPipeDistRemoved(t *testing.T) {
func TestRunPipeInvalidGlob(t *testing.T) { func TestRunPipeInvalidGlob(t *testing.T) {
var assert = assert.New(t) var assert = assert.New(t)
var ctx = &context.Context{ var ctx = &context.Context{
Archives: map[string]string{ Folders: map[string]string{
"windowsamd64": "mybin", "windowsamd64": "whatever",
}, },
Config: config.Project{ Config: config.Project{
Dist: "/tmp", Dist: "/tmp",
@ -113,7 +110,7 @@ func TestRunPipeGlobFailsToAdd(t *testing.T) {
assert.NoError(os.MkdirAll(filepath.Join(folder, "folder", "another"), 0755)) assert.NoError(os.MkdirAll(filepath.Join(folder, "folder", "another"), 0755))
var ctx = &context.Context{ var ctx = &context.Context{
Archives: map[string]string{ Folders: map[string]string{
"windows386": "mybin", "windows386": "mybin",
}, },
Config: config.Project{ Config: config.Project{
@ -127,18 +124,3 @@ func TestRunPipeGlobFailsToAdd(t *testing.T) {
} }
assert.Error(Pipe{}.Run(ctx)) 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))
}

View File

@ -21,6 +21,8 @@ import (
// contain darwin and/or goarch doesn't contain amd64) // contain darwin and/or goarch doesn't contain amd64)
var ErrNoDarwin64Build = errors.New("brew tap requires a darwin amd64 build") var ErrNoDarwin64Build = errors.New("brew tap requires a darwin amd64 build")
const platform = "darwinamd64"
const formula = `class {{ .Name }} < Formula const formula = `class {{ .Name }} < Formula
desc "{{ .Desc }}" desc "{{ .Desc }}"
homepage "{{ .Homepage }}" homepage "{{ .Homepage }}"
@ -70,7 +72,6 @@ type templateData struct {
Repo config.Repo // FIXME: will not work for anything but github right now. Repo config.Repo // FIXME: will not work for anything but github right now.
Tag string Tag string
Version string Version string
Binary string
Caveats string Caveats string
File string File string
SHA256 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") log.Warn("skipped because release is marked as draft")
return nil 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). log.WithField("formula", path).
WithField("repo", ctx.Config.Brew.GitHub.String()). WithField("repo", ctx.Config.Brew.GitHub.String()).
Info("pushing") Info("pushing")
@ -127,7 +128,7 @@ func buildFormula(ctx *context.Context, client client.Client) (bytes.Buffer, err
func doBuildFormula(data templateData) (bytes.Buffer, error) { func doBuildFormula(data templateData) (bytes.Buffer, error) {
var out bytes.Buffer var out bytes.Buffer
tmpl, err := template.New(data.Binary).Parse(formula) tmpl, err := template.New(data.Name).Parse(formula)
if err != nil { if err != nil {
return out, err 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) { func dataFor(ctx *context.Context, client client.Client) (result templateData, err error) {
var folder = ctx.Archives["darwinamd64"] var folder = ctx.Folders[platform]
if folder == "" { if folder == "" {
return result, ErrNoDarwin64Build 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)) sum, err := checksum.SHA256(filepath.Join(ctx.Config.Dist, file))
if err != nil { if err != nil {
return return
} }
return templateData{ return templateData{
Name: formulaNameFor(ctx.Config.Build.Binary), Name: formulaNameFor(ctx.Config.ProjectName),
Desc: ctx.Config.Brew.Description, Desc: ctx.Config.Brew.Description,
Homepage: ctx.Config.Brew.Homepage, Homepage: ctx.Config.Brew.Homepage,
Repo: ctx.Config.Release.GitHub, Repo: ctx.Config.Release.GitHub,
Tag: ctx.Git.CurrentTag, Tag: ctx.Git.CurrentTag,
Version: ctx.Version, Version: ctx.Version,
Binary: ctx.Config.Build.Binary,
Caveats: ctx.Config.Brew.Caveats, Caveats: ctx.Config.Brew.Caveats,
File: file, File: file,
SHA256: sum, SHA256: sum,

View File

@ -29,7 +29,6 @@ func TestSimpleName(t *testing.T) {
} }
var defaultTemplateData = templateData{ var defaultTemplateData = templateData{
Binary: "test",
Desc: "Some desc", Desc: "Some desc",
Homepage: "https://google.com", Homepage: "https://google.com",
Name: "Test", Name: "Test",
@ -104,7 +103,7 @@ func TestRunPipe(t *testing.T) {
}, },
}, },
}, },
Archives: map[string]string{ Folders: map[string]string{
"darwinamd64": "bin", "darwinamd64": "bin",
}, },
Publish: true, Publish: true,
@ -139,7 +138,7 @@ func TestRunPipeFormatOverride(t *testing.T) {
}, },
}, },
}, },
Archives: map[string]string{ Folders: map[string]string{
"darwinamd64": "bin", "darwinamd64": "bin",
}, },
Publish: true, Publish: true,
@ -167,7 +166,7 @@ func TestRunPipeArchiveDoesntExist(t *testing.T) {
}, },
}, },
}, },
Archives: map[string]string{ Folders: map[string]string{
"darwinamd64": "bin", "darwinamd64": "bin",
}, },
Publish: true, Publish: true,

View File

@ -10,8 +10,10 @@ import (
"strings" "strings"
"github.com/apex/log" "github.com/apex/log"
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context" "github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/ext" "github.com/goreleaser/goreleaser/internal/ext"
"github.com/goreleaser/goreleaser/internal/name"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
@ -25,31 +27,36 @@ func (Pipe) Description() string {
// Run the pipe // Run the pipe
func (Pipe) Run(ctx *context.Context) error { 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 return err
} }
sem := make(chan bool, 4) sem := make(chan bool, 4)
var g errgroup.Group var g errgroup.Group
for _, target := range buildTargets(ctx) { for _, target := range buildTargets(build) {
name, err := nameFor(ctx, target)
if err != nil {
return err
}
ctx.Archives[target.String()] = name
sem <- true sem <- true
target := target target := target
build := build
g.Go(func() error { g.Go(func() error {
defer func() { defer func() {
<-sem <-sem
}() }()
return build(ctx, name, target) return doBuild(ctx, build, target)
}) })
} }
if err := g.Wait(); err != nil { if err := g.Wait(); err != nil {
return err 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 { func runHook(env []string, hook string) error {
@ -61,23 +68,28 @@ func runHook(env []string, hook string) error {
return run(runtimeTarget, cmd, env) return run(runtimeTarget, cmd, env)
} }
func build(ctx *context.Context, name string, target buildTarget) error { func doBuild(ctx *context.Context, build config.Build, target buildTarget) error {
output := filepath.Join( folder, err := name.For(ctx, target.goos, target.goarch, target.goarm)
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)
if err != nil { if err != nil {
return err return err
} }
cmd = append(cmd, "-ldflags="+flags, "-o", output, ctx.Config.Build.Main) ctx.AddFolder(target.String(), folder)
return run(target, cmd, ctx.Config.Build.Env) 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 { func run(target buildTarget, command, env []string) error {

View File

@ -29,16 +29,19 @@ func TestRunInvalidCommand(t *testing.T) {
func TestBuild(t *testing.T) { func TestBuild(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
var config = config.Project{ var config = config.Project{
Build: config.Build{ Builds: []config.Build{
{
Binary: "testing", Binary: "testing",
Flags: "-n", Flags: "-n",
Env: []string{"BLAH=1"}, Env: []string{"BLAH=1"},
}, },
},
} }
var ctx = &context.Context{ 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) { func TestRunFullPipe(t *testing.T) {
@ -50,7 +53,8 @@ func TestRunFullPipe(t *testing.T) {
var post = filepath.Join(folder, "post") var post = filepath.Join(folder, "post")
var config = config.Project{ var config = config.Project{
Dist: folder, Dist: folder,
Build: config.Build{ Builds: []config.Build{
{
Binary: "testing", Binary: "testing",
Flags: "-v", Flags: "-v",
Ldflags: "-X main.test=testing", Ldflags: "-X main.test=testing",
@ -65,10 +69,11 @@ func TestRunFullPipe(t *testing.T) {
runtime.GOARCH, runtime.GOARCH,
}, },
}, },
},
} }
var ctx = &context.Context{ var ctx = &context.Context{
Config: config, Config: config,
Archives: map[string]string{}, Folders: map[string]string{},
} }
assert.NoError(Pipe{}.Run(ctx)) assert.NoError(Pipe{}.Run(ctx))
assert.True(exists(binary), binary) assert.True(exists(binary), binary)
@ -83,7 +88,8 @@ func TestRunPipeArmBuilds(t *testing.T) {
var binary = filepath.Join(folder, "armtesting") var binary = filepath.Join(folder, "armtesting")
var config = config.Project{ var config = config.Project{
Dist: folder, Dist: folder,
Build: config.Build{ Builds: []config.Build{
{
Binary: "armtesting", Binary: "armtesting",
Flags: "-v", Flags: "-v",
Ldflags: "-X main.test=armtesting", Ldflags: "-X main.test=armtesting",
@ -98,10 +104,11 @@ func TestRunPipeArmBuilds(t *testing.T) {
"6", "6",
}, },
}, },
},
} }
var ctx = &context.Context{ var ctx = &context.Context{
Config: config, Config: config,
Archives: map[string]string{}, Folders: map[string]string{},
} }
assert.NoError(Pipe{}.Run(ctx)) assert.NoError(Pipe{}.Run(ctx))
assert.True(exists(binary), binary) assert.True(exists(binary), binary)
@ -110,7 +117,8 @@ func TestRunPipeArmBuilds(t *testing.T) {
func TestBuildFailed(t *testing.T) { func TestBuildFailed(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
var config = config.Project{ var config = config.Project{
Build: config.Build{ Builds: []config.Build{
{
Flags: "-flag-that-dont-exists-to-force-failure", Flags: "-flag-that-dont-exists-to-force-failure",
Goos: []string{ Goos: []string{
runtime.GOOS, runtime.GOOS,
@ -119,10 +127,11 @@ func TestBuildFailed(t *testing.T) {
runtime.GOARCH, runtime.GOARCH,
}, },
}, },
},
} }
var ctx = &context.Context{ var ctx = &context.Context{
Config: config, Config: config,
Archives: map[string]string{}, Folders: map[string]string{},
} }
assert.Error(Pipe{}.Run(ctx)) assert.Error(Pipe{}.Run(ctx))
} }
@ -130,7 +139,8 @@ func TestBuildFailed(t *testing.T) {
func TestRunPipeWithInvalidOS(t *testing.T) { func TestRunPipeWithInvalidOS(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
var config = config.Project{ var config = config.Project{
Build: config.Build{ Builds: []config.Build{
{
Flags: "-v", Flags: "-v",
Goos: []string{ Goos: []string{
"windows", "windows",
@ -139,10 +149,11 @@ func TestRunPipeWithInvalidOS(t *testing.T) {
"arm", "arm",
}, },
}, },
},
} }
var ctx = &context.Context{ var ctx = &context.Context{
Config: config, Config: config,
Archives: map[string]string{}, Folders: map[string]string{},
} }
assert.NoError(Pipe{}.Run(ctx)) assert.NoError(Pipe{}.Run(ctx))
} }
@ -151,7 +162,8 @@ func TestRunInvalidNametemplate(t *testing.T) {
var assert = assert.New(t) var assert = assert.New(t)
var ctx = &context.Context{ var ctx = &context.Context{
Config: config.Project{ Config: config.Project{
Build: config.Build{ Builds: []config.Build{
{
Binary: "nametest", Binary: "nametest",
Flags: "-v", Flags: "-v",
Goos: []string{ Goos: []string{
@ -161,6 +173,7 @@ func TestRunInvalidNametemplate(t *testing.T) {
runtime.GOARCH, runtime.GOARCH,
}, },
}, },
},
Archive: config.Archive{ Archive: config.Archive{
NameTemplate: "{{.Binary}_{{.Os}}_{{.Arch}}_{{.Version}}", NameTemplate: "{{.Binary}_{{.Os}}_{{.Arch}}_{{.Version}}",
}, },
@ -172,9 +185,10 @@ func TestRunInvalidNametemplate(t *testing.T) {
func TestRunInvalidLdflags(t *testing.T) { func TestRunInvalidLdflags(t *testing.T) {
var assert = assert.New(t) var assert = assert.New(t)
var ctx = &context.Context{ var ctx = &context.Context{
Archives: map[string]string{}, Folders: map[string]string{},
Config: config.Project{ Config: config.Project{
Build: config.Build{ Builds: []config.Build{
{
Binary: "nametest", Binary: "nametest",
Flags: "-v", Flags: "-v",
Ldflags: "-s -w -X main.version={{.Version}", Ldflags: "-s -w -X main.version={{.Version}",
@ -186,6 +200,7 @@ func TestRunInvalidLdflags(t *testing.T) {
}, },
}, },
}, },
},
} }
assert.Error(Pipe{}.Run(ctx)) assert.Error(Pipe{}.Run(ctx))
} }
@ -193,7 +208,8 @@ func TestRunInvalidLdflags(t *testing.T) {
func TestRunPipeFailingHooks(t *testing.T) { func TestRunPipeFailingHooks(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
var config = config.Project{ var config = config.Project{
Build: config.Build{ Builds: []config.Build{
{
Hooks: config.Hooks{}, Hooks: config.Hooks{},
Goos: []string{ Goos: []string{
runtime.GOOS, runtime.GOOS,
@ -202,17 +218,18 @@ func TestRunPipeFailingHooks(t *testing.T) {
runtime.GOARCH, runtime.GOARCH,
}, },
}, },
},
} }
var ctx = &context.Context{ var ctx = &context.Context{
Config: config, Config: config,
Archives: map[string]string{}, Folders: map[string]string{},
} }
t.Run("pre-hook", func(t *testing.T) { 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)) assert.Error(Pipe{}.Run(ctx))
}) })
t.Run("post-hook", func(t *testing.T) { 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)) assert.Error(Pipe{}.Run(ctx))
}) })
} }

View File

@ -5,6 +5,7 @@ import (
"text/template" "text/template"
"time" "time"
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context" "github.com/goreleaser/goreleaser/context"
) )
@ -15,7 +16,7 @@ type ldflagsData struct {
Version string Version string
} }
func ldflags(ctx *context.Context) (string, error) { func ldflags(ctx *context.Context, build config.Build) (string, error) {
var data = ldflagsData{ var data = ldflagsData{
Commit: ctx.Git.Commit, Commit: ctx.Git.Commit,
Tag: ctx.Git.CurrentTag, Tag: ctx.Git.CurrentTag,
@ -23,7 +24,7 @@ func ldflags(ctx *context.Context) (string, error) {
Date: time.Now().UTC().Format(time.RFC3339), Date: time.Now().UTC().Format(time.RFC3339),
} }
var out bytes.Buffer 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 { if err != nil {
return "", err return "", err
} }

View File

@ -11,9 +11,11 @@ import (
func TestLdFlagsFullTemplate(t *testing.T) { func TestLdFlagsFullTemplate(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
var config = config.Project{ var config = config.Project{
Build: config.Build{ Builds: []config.Build{
{
Ldflags: "-s -w -X main.version={{.Version}} -X main.tag={{.Tag}} -X main.date={{.Date}} -X main.commit={{.Commit}}", Ldflags: "-s -w -X main.version={{.Version}} -X main.tag={{.Tag}} -X main.date={{.Date}} -X main.commit={{.Commit}}",
}, },
},
} }
var ctx = &context.Context{ var ctx = &context.Context{
Git: context.GitInfo{ Git: context.GitInfo{
@ -23,7 +25,7 @@ func TestLdFlagsFullTemplate(t *testing.T) {
Version: "1.2.3", Version: "1.2.3",
Config: config, Config: config,
} }
flags, err := ldflags(ctx) flags, err := ldflags(ctx, ctx.Config.Builds[0])
assert.NoError(err) assert.NoError(err)
assert.Contains(flags, "-s -w") assert.Contains(flags, "-s -w")
assert.Contains(flags, "-X main.version=1.2.3") assert.Contains(flags, "-X main.version=1.2.3")
@ -35,14 +37,14 @@ func TestLdFlagsFullTemplate(t *testing.T) {
func TestInvalidTemplate(t *testing.T) { func TestInvalidTemplate(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
var config = config.Project{ var config = config.Project{
Build: config.Build{ Builds: []config.Build{
Ldflags: "{invalid{.Template}}}{{}}}", {Ldflags: "{invalid{.Template}}}{{}}}"},
}, },
} }
var ctx = &context.Context{ var ctx = &context.Context{
Config: config, Config: config,
} }
flags, err := ldflags(ctx) flags, err := ldflags(ctx, ctx.Config.Builds[0])
assert.Error(err) assert.Error(err)
assert.Equal(flags, "") assert.Equal(flags, "")
} }

View File

@ -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
}

View File

@ -5,7 +5,7 @@ import (
"runtime" "runtime"
"github.com/apex/log" "github.com/apex/log"
"github.com/goreleaser/goreleaser/context" "github.com/goreleaser/goreleaser/config"
) )
var runtimeTarget = buildTarget{runtime.GOOS, runtime.GOARCH, ""} 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) return fmt.Sprintf("%v/%v%v", t.goos, t.goarch, t.goarm)
} }
func buildTargets(ctx *context.Context) (targets []buildTarget) { func buildTargets(build config.Build) (targets []buildTarget) {
for _, target := range allBuildTargets(ctx) { for _, target := range allBuildTargets(build) {
if !valid(target) { if !valid(target) {
log.WithField("target", target.PrettyString()). log.WithField("target", target.PrettyString()).
Warn("skipped invalid build") Warn("skipped invalid build")
continue continue
} }
if ignored(ctx, target) { if ignored(build, target) {
log.WithField("target", target.PrettyString()). log.WithField("target", target.PrettyString()).
Warn("skipped ignored build") Warn("skipped ignored build")
continue continue
@ -40,11 +40,11 @@ func buildTargets(ctx *context.Context) (targets []buildTarget) {
return return
} }
func allBuildTargets(ctx *context.Context) (targets []buildTarget) { func allBuildTargets(build config.Build) (targets []buildTarget) {
for _, goos := range ctx.Config.Build.Goos { for _, goos := range build.Goos {
for _, goarch := range ctx.Config.Build.Goarch { for _, goarch := range build.Goarch {
if goarch == "arm" { if goarch == "arm" {
for _, goarm := range ctx.Config.Build.Goarm { for _, goarm := range build.Goarm {
targets = append(targets, buildTarget{goos, goarch, goarm}) targets = append(targets, buildTarget{goos, goarch, goarm})
} }
continue continue
@ -55,8 +55,8 @@ func allBuildTargets(ctx *context.Context) (targets []buildTarget) {
return return
} }
func ignored(ctx *context.Context, target buildTarget) bool { func ignored(build config.Build, target buildTarget) bool {
for _, ig := range ctx.Config.Build.Ignore { for _, ig := range build.Ignore {
var ignored = buildTarget{ig.Goos, ig.Goarch, ig.Goarm} var ignored = buildTarget{ig.Goos, ig.Goarch, ig.Goarm}
if ignored == target { if ignored == target {
return true return true

View File

@ -5,15 +5,12 @@ import (
"testing" "testing"
"github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestAllBuildTargets(t *testing.T) { func TestAllBuildTargets(t *testing.T) {
var assert = assert.New(t) var assert = assert.New(t)
var ctx = &context.Context{ var build = config.Build{
Config: config.Project{
Build: config.Build{
Goos: []string{ Goos: []string{
"linux", "linux",
"darwin", "darwin",
@ -39,8 +36,6 @@ func TestAllBuildTargets(t *testing.T) {
Goarm: "7", Goarm: "7",
}, },
}, },
},
},
} }
assert.Equal([]buildTarget{ assert.Equal([]buildTarget{
{"linux", "386", ""}, {"linux", "386", ""},
@ -52,7 +47,7 @@ func TestAllBuildTargets(t *testing.T) {
{"freebsd", "amd64", ""}, {"freebsd", "amd64", ""},
{"freebsd", "arm", "6"}, {"freebsd", "arm", "6"},
{"freebsd", "arm", "7"}, {"freebsd", "arm", "7"},
}, buildTargets(ctx)) }, buildTargets(build))
} }
func TestValidGoosGoarchCombos(t *testing.T) { func TestValidGoosGoarchCombos(t *testing.T) {

View File

@ -26,7 +26,7 @@ func (Pipe) Run(ctx *context.Context) (err error) {
file, err := os.OpenFile( file, err := os.OpenFile(
filepath.Join( filepath.Join(
ctx.Config.Dist, 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, os.O_APPEND|os.O_WRONLY|os.O_CREATE|os.O_TRUNC,
0644, 0644,

View File

@ -25,9 +25,7 @@ func TestPipe(t *testing.T) {
var ctx = &context.Context{ var ctx = &context.Context{
Config: config.Project{ Config: config.Project{
Dist: folder, Dist: folder,
Build: config.Build{ ProjectName: binary,
Binary: binary,
},
}, },
} }
ctx.AddArtifact(file) ctx.AddArtifact(file)

View File

@ -4,7 +4,10 @@ package defaults
import ( import (
"fmt" "fmt"
"strings"
"github.com/apex/log"
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context" "github.com/goreleaser/goreleaser/context"
) )
@ -31,14 +34,44 @@ func (Pipe) Run(ctx *context.Context) error {
if err := setReleaseDefaults(ctx); err != nil { if err := setReleaseDefaults(ctx); err != nil {
return err return err
} }
if ctx.Config.ProjectName == "" {
ctx.Config.ProjectName = ctx.Config.Release.GitHub.Name
}
setBuildDefaults(ctx) setBuildDefaults(ctx)
if ctx.Config.Brew.Install == "" { if ctx.Config.Brew.Install == "" {
ctx.Config.Brew.Install = fmt.Sprintf( var installs []string
`bin.install "%s"`, for _, build := range ctx.Config.Builds {
ctx.Config.Build.Binary, if !isBrewBuild(build) {
continue
}
installs = append(
installs,
fmt.Sprintf(`bin.install "%s"`, build.Binary),
) )
} }
return setArchiveDefaults(ctx) ctx.Config.Brew.Install = strings.Join(installs, "\n")
}
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 { func setReleaseDefaults(ctx *context.Context) error {
@ -54,24 +87,36 @@ func setReleaseDefaults(ctx *context.Context) error {
} }
func setBuildDefaults(ctx *context.Context) { func setBuildDefaults(ctx *context.Context) {
if ctx.Config.Build.Binary == "" { for i, build := range ctx.Config.Builds {
ctx.Config.Build.Binary = ctx.Config.Release.GitHub.Name ctx.Config.Builds[i] = buildWithDefaults(ctx, build)
} }
if ctx.Config.Build.Main == "" { if len(ctx.Config.Builds) == 0 {
ctx.Config.Build.Main = "." ctx.Config.Builds = []config.Build{
buildWithDefaults(ctx, ctx.Config.SingleBuild),
} }
if len(ctx.Config.Build.Goos) == 0 {
ctx.Config.Build.Goos = []string{"linux", "darwin"}
} }
if len(ctx.Config.Build.Goarch) == 0 { }
ctx.Config.Build.Goarch = []string{"amd64", "386"}
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.Goarm) == 0 { if build.Main == "" {
ctx.Config.Build.Goarm = []string{"6"} build.Main = "."
} }
if ctx.Config.Build.Ldflags == "" { if len(build.Goos) == 0 {
ctx.Config.Build.Ldflags = "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}" build.Goos = []string{"linux", "darwin"}
} }
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 { func setArchiveDefaults(ctx *context.Context) error {

View File

@ -15,33 +15,32 @@ func TestDescription(t *testing.T) {
} }
func TestFillBasicData(t *testing.T) { func TestFillBasicData(t *testing.T) {
assert := assert.New(t) var assert = assert.New(t)
var ctx = &context.Context{ var ctx = &context.Context{
Config: config.Project{}, Config: config.Project{},
} }
assert.NoError(Pipe{}.Run(ctx)) assert.NoError(Pipe{}.Run(ctx))
assert.Equal("goreleaser", ctx.Config.Release.GitHub.Owner) assert.Equal("goreleaser", ctx.Config.Release.GitHub.Owner)
assert.Equal("goreleaser", ctx.Config.Release.GitHub.Name) assert.Equal("goreleaser", ctx.Config.Release.GitHub.Name)
assert.Equal("goreleaser", ctx.Config.Build.Binary) assert.NotEmpty(ctx.Config.Builds)
assert.Equal(".", ctx.Config.Build.Main) 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.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.Contains(ctx.Config.Brew.Install, "bin.install \"goreleaser\"")
assert.NotEmpty( assert.NotEmpty(
ctx.Config.Archive.NameTemplate, ctx.Config.Archive.NameTemplate,
ctx.Config.Build.Ldflags, ctx.Config.Builds[0].Ldflags,
ctx.Config.Archive.Files, ctx.Config.Archive.Files,
) )
} }
func TestFillPartial(t *testing.T) { func TestFillPartial(t *testing.T) {
assert := assert.New(t) var assert = assert.New(t)
var ctx = &context.Context{ var ctx = &context.Context{
Config: config.Project{ Config: config.Project{
@ -56,10 +55,36 @@ func TestFillPartial(t *testing.T) {
"glob/*", "glob/*",
}, },
}, },
Builds: []config.Build{
{Binary: "testreleaser"},
{Goos: []string{"linux"}},
{
Binary: "another",
Ignore: []config.IgnoredBuild{
{Goos: "darwin", Goarch: "amd64"},
},
},
},
}, },
} }
assert.NoError(Pipe{}.Run(ctx)) assert.NoError(Pipe{}.Run(ctx))
assert.Len(ctx.Config.Archive.Files, 1) 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) { func TestNotAGitRepo(t *testing.T) {

View File

@ -3,19 +3,17 @@ package fpm
import ( import (
"errors" "errors"
"fmt"
"io/ioutil"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings"
"github.com/apex/log" "github.com/apex/log"
"github.com/goreleaser/goreleaser/context" "github.com/goreleaser/goreleaser/context"
"golang.org/x/sync/errgroup" "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 // ErrNoFPM is shown when fpm cannot be found in $PATH
var ErrNoFPM = errors.New("fpm not present 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 var g errgroup.Group
for _, format := range ctx.Config.FPM.Formats { for _, format := range ctx.Config.FPM.Formats {
format := format for key, folder := range ctx.Folders {
for _, goarch := range ctx.Config.Build.Goarch { if !strings.Contains(key, "linux") {
if ctx.Archives["linux"+goarch] == "" { log.WithField("key", key).Debug("skipped non-linux builds for fpm")
continue continue
} }
archive := ctx.Archives["linux"+goarch] folder := folder
arch := goarchToUnix[goarch] format := format
arch := archFor(key)
g.Go(func() error { g.Go(func() error {
return create(ctx, format, archive, arch) return create(ctx, format, folder, arch)
}) })
} }
} }
return g.Wait() return g.Wait()
} }
func create(ctx *context.Context, format, archive, arch string) error { func archFor(key string) string {
var path = filepath.Join(ctx.Config.Dist, archive) 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 file = path + "." + format
var name = ctx.Config.Build.Binary log.WithField("file", file).Info("creating fpm archive")
log.WithField("file", file).Info("Creating")
var options = []string{ var options = []string{
"--input-type", "dir", "--input-type", "dir",
"--output-type", format, "--output-type", format,
"--name", name, "--name", ctx.Config.ProjectName,
"--version", ctx.Version, "--version", ctx.Version,
"--architecture", arch, "--architecture", arch,
"--chdir", path, "--chdir", path,
@ -94,9 +99,21 @@ func create(ctx *context.Context, format, archive, arch string) error {
options = append(options, "--conflicts", conflict) options = append(options, "--conflicts", conflict)
} }
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 // This basically tells fpm to put the binary in the /usr/local/bin
// binary=/usr/local/bin/binary // binary=/usr/local/bin/binary
options = append(options, name+"="+filepath.Join("/usr/local/bin", name)) 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 { if out, err := exec.Command("fpm", options...).CombinedOutput(); err != nil {
return errors.New(string(out)) return errors.New(string(out))

View File

@ -33,18 +33,14 @@ func TestRunPipe(t *testing.T) {
_, err = os.Create(filepath.Join(dist, "mybin", "mybin")) _, err = os.Create(filepath.Join(dist, "mybin", "mybin"))
assert.NoError(err) assert.NoError(err)
var ctx = &context.Context{ var ctx = &context.Context{
Archives: map[string]string{ Folders: map[string]string{
"linuxamd64": "mybin", "linuxamd64": "mybin",
"linux386": "mybin",
"darwinamd64": "anotherbin",
}, },
Config: config.Project{ Config: config.Project{
ProjectName: "mybin",
Dist: dist, Dist: dist,
Build: config.Build{
Goarch: []string{
"amd64",
"i386",
},
Binary: "mybin",
},
FPM: config.FPM{ FPM: config.FPM{
Formats: []string{"deb"}, Formats: []string{"deb"},
Dependencies: []string{"make"}, Dependencies: []string{"make"},
@ -85,18 +81,11 @@ func TestCreateFileDoesntExist(t *testing.T) {
assert.NoError(os.Mkdir(dist, 0755)) assert.NoError(os.Mkdir(dist, 0755))
assert.NoError(os.Mkdir(filepath.Join(dist, "mybin"), 0755)) assert.NoError(os.Mkdir(filepath.Join(dist, "mybin"), 0755))
var ctx = &context.Context{ var ctx = &context.Context{
Archives: map[string]string{ Folders: map[string]string{
"linuxamd64": "mybin", "linuxamd64": "mybin",
}, },
Config: config.Project{ Config: config.Project{
Dist: dist, Dist: dist,
Build: config.Build{
Goarch: []string{
"amd64",
"i386",
},
Binary: "mybin",
},
FPM: config.FPM{ FPM: config.FPM{
Formats: []string{"deb"}, Formats: []string{"deb"},
}, },
@ -104,3 +93,12 @@ func TestCreateFileDoesntExist(t *testing.T) {
} }
assert.Error(Pipe{}.Run(ctx)) 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",
)
}