diff --git a/README.md b/README.md index 59fab4227..c3e2a6b70 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`, `zip` and `binary`. # If format is `binary` no archives are created and the binaries are instead uploaded directly. 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/archiveformat/format.go b/internal/archiveformat/format.go new file mode 100644 index 000000000..ce87f215a --- /dev/null +++ b/internal/archiveformat/format.go @@ -0,0 +1,19 @@ +// Package archiveformat provides functions to get the format of given package +// based on the config +package archiveformat + +import ( + "strings" + + "github.com/goreleaser/goreleaser/context" +) + +// For return the archive format, considering overrides and all that +func For(ctx *context.Context, platform string) string { + for _, override := range ctx.Config.Archive.FormatOverrides { + if strings.HasPrefix(platform, override.Goos) { + return override.Format + } + } + return ctx.Config.Archive.Format +} diff --git a/internal/archiveformat/format_test.go b/internal/archiveformat/format_test.go new file mode 100644 index 000000000..76396648d --- /dev/null +++ b/internal/archiveformat/format_test.go @@ -0,0 +1,28 @@ +package archiveformat + +import ( + "testing" + + "github.com/goreleaser/goreleaser/config" + "github.com/goreleaser/goreleaser/context" + "github.com/stretchr/testify/assert" +) + +func TestFormatFor(t *testing.T) { + var assert = assert.New(t) + var ctx = &context.Context{ + Config: config.Project{ + Archive: config.Archive{ + Format: "tar.gz", + FormatOverrides: []config.FormatOverride{ + { + Goos: "windows", + Format: "zip", + }, + }, + }, + }, + } + assert.Equal("zip", For(ctx, "windowsamd64")) + assert.Equal("tar.gz", For(ctx, "linux386")) +} 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/ext/ext.go b/internal/ext/ext.go index f841e9c96..0e2d97310 100644 --- a/internal/ext/ext.go +++ b/internal/ext/ext.go @@ -2,6 +2,7 @@ package ext import "strings" +// For returns the binary extension for the given platform func For(platform string) (ext string) { if strings.HasPrefix(platform, "windows") { ext = ".exe" 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 b0da0eaa7..12445f650 100644 --- a/pipeline/archive/archive.go +++ b/pipeline/archive/archive.go @@ -4,13 +4,14 @@ package archive import ( + "io/ioutil" "os" "path/filepath" - "strings" "github.com/apex/log" "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,8 +28,8 @@ 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 { if ctx.Config.Archive.Format == "binary" { @@ -42,7 +43,7 @@ func (Pipe) Run(ctx *context.Context) error { func create(ctx *context.Context, platform, name string) error { var folder = filepath.Join(ctx.Config.Dist, name) - var format = formatFor(ctx, platform) + var format = archiveformat.For(ctx, platform) file, err := os.Create(folder + "." + format) if err != nil { return err @@ -61,10 +62,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 } @@ -87,12 +94,3 @@ func findFiles(ctx *context.Context) (result []string, err error) { } return } - -func formatFor(ctx *context.Context, platform string) string { - for _, override := range ctx.Config.Archive.FormatOverrides { - if strings.HasPrefix(platform, override.Goos) { - return override.Format - } - } - return ctx.Config.Archive.Format -} diff --git a/pipeline/archive/archive_test.go b/pipeline/archive/archive_test.go index 1cbb2eaf6..9877e0932 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.*", @@ -108,9 +106,8 @@ func TestRunPipeBinary(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", @@ -122,30 +119,11 @@ func TestRunPipeDistRemoved(t *testing.T) { assert.Error(Pipe{}.Run(ctx)) } -func TestFormatFor(t *testing.T) { - var assert = assert.New(t) - var ctx = &context.Context{ - Config: config.Project{ - Archive: config.Archive{ - Format: "tar.gz", - FormatOverrides: []config.FormatOverride{ - { - Goos: "windows", - Format: "zip", - }, - }, - }, - }, - } - assert.Equal("zip", formatFor(ctx, "windowsamd64")) - assert.Equal("tar.gz", formatFor(ctx, "linux386")) -} - 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", @@ -172,7 +150,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{ @@ -186,18 +164,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 5796e96b5..4e47df370 100644 --- a/pipeline/brew/brew.go +++ b/pipeline/brew/brew.go @@ -13,6 +13,7 @@ import ( "github.com/goreleaser/goreleaser/checksum" "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/archiveformat" "github.com/goreleaser/goreleaser/internal/client" ) @@ -20,10 +21,12 @@ 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 }}" - url "https://github.com/{{ .Repo.Owner }}/{{ .Repo.Name }}/releases/download/{{ .Tag }}/{{ .File }}.{{ .Format }}" + url "https://github.com/{{ .Repo.Owner }}/{{ .Repo.Name }}/releases/download/{{ .Tag }}/{{ .File }}" version "{{ .Version }}" sha256 "{{ .SHA256 }}" @@ -69,10 +72,8 @@ 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 - Format string SHA256 string Plist string Install []string @@ -110,7 +111,7 @@ func doRun(ctx *context.Context, client client.Client) error { log.Info("skipped because archive format is binary") 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") @@ -131,7 +132,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 } @@ -140,30 +141,24 @@ func doBuildFormula(data templateData) (bytes.Buffer, error) { } func dataFor(ctx *context.Context, client client.Client) (result templateData, err error) { - file := ctx.Archives["darwinamd64"] - if file == "" { + var folder = ctx.Folders[platform] + if folder == "" { return result, ErrNoDarwin64Build } - sum, err := checksum.SHA256( - filepath.Join( - ctx.Config.Dist, - file+"."+ctx.Config.Archive.Format, - ), - ) + 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, - Format: ctx.Config.Archive.Format, // TODO this can be broken by format_overrides SHA256: sum, Dependencies: ctx.Config.Brew.Dependencies, Conflicts: ctx.Config.Brew.Conflicts, diff --git a/pipeline/brew/brew_test.go b/pipeline/brew/brew_test.go index a0a4326fe..099ca527c 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", @@ -39,9 +38,8 @@ var defaultTemplateData = templateData{ }, Tag: "v0.1.3", Version: "0.1.3", - File: "test_Darwin_x86_64", + File: "test_Darwin_x86_64.tar.gz", SHA256: "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c68", - Format: "tar.gz", } func assertDefaultTemplateData(t *testing.T, formulae string) { @@ -105,7 +103,7 @@ func TestRunPipe(t *testing.T) { }, }, }, - Archives: map[string]string{ + Folders: map[string]string{ "darwinamd64": "bin", }, Publish: true, @@ -115,6 +113,69 @@ func TestRunPipe(t *testing.T) { assert.True(client.CreatedFile) } +func TestRunPipeFormatOverride(t *testing.T) { + assert := assert.New(t) + folder, err := ioutil.TempDir("", "goreleasertest") + assert.NoError(err) + _, err = os.Create(filepath.Join(folder, "bin.zip")) + assert.NoError(err) + var ctx = &context.Context{ + Config: config.Project{ + Dist: folder, + Archive: config.Archive{ + Format: "tar.gz", + FormatOverrides: []config.FormatOverride{ + { + Format: "zip", + Goos: "darwin", + }, + }, + }, + Brew: config.Homebrew{ + GitHub: config.Repo{ + Owner: "test", + Name: "test", + }, + }, + }, + Folders: map[string]string{ + "darwinamd64": "bin", + }, + Publish: true, + } + client := &DummyClient{} + assert.NoError(doRun(ctx, client)) + assert.True(client.CreatedFile) + assert.Contains(client.Content, "bin.zip") +} + +func TestRunPipeArchiveDoesntExist(t *testing.T) { + assert := assert.New(t) + folder, err := ioutil.TempDir("", "goreleasertest") + assert.NoError(err) + var ctx = &context.Context{ + Config: config.Project{ + Dist: folder, + Archive: config.Archive{ + Format: "tar.gz", + }, + Brew: config.Homebrew{ + GitHub: config.Repo{ + Owner: "test", + Name: "test", + }, + }, + }, + Folders: map[string]string{ + "darwinamd64": "bin", + }, + Publish: true, + } + client := &DummyClient{} + assert.Error(doRun(ctx, client)) + assert.False(client.CreatedFile) +} + func TestRunPipeNoDarwin64Build(t *testing.T) { assert := assert.New(t) var ctx = &context.Context{ @@ -202,6 +263,7 @@ func TestRunPipeFormatBinary(t *testing.T) { type DummyClient struct { CreatedFile bool + Content string } func (client *DummyClient) CreateRelease(ctx *context.Context, body string) (releaseID int, err error) { @@ -210,6 +272,8 @@ func (client *DummyClient) CreateRelease(ctx *context.Context, body string) (rel func (client *DummyClient) CreateFile(ctx *context.Context, content bytes.Buffer, path string) (err error) { client.CreatedFile = true + bts, _ := ioutil.ReadAll(&content) + client.Content = string(bts) return } diff --git a/pipeline/build/build.go b/pipeline/build/build.go index 0aa70e5a1..55b77e919 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,39 +68,45 @@ 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), - ) - if ctx.Config.Archive.Format == "binary" { - output = filepath.Join(ctx.Config.Dist, name+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), + ) + if ctx.Config.Archive.Format == "binary" { + binary = filepath.Join(ctx.Config.Dist, 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 { var cmd = exec.Command(command[0], command[1:]...) env = append(env, "GOOS="+target.goos, "GOARCH="+target.goarch, "GOARM="+target.goarm) + var log = log.WithField("target", target.PrettyString()). + WithField("env", env). + WithField("cmd", command) cmd.Env = append(cmd.Env, os.Environ()...) cmd.Env = append(cmd.Env, env...) - log.WithField("target", target.PrettyString()). - WithField("env", env). - WithField("args", cmd.Args). - Debug("running") + log.Debug("running") if out, err := cmd.CombinedOutput(); err != nil { - return fmt.Errorf("build failed: %s\n%v", target.PrettyString(), string(out)) + log.WithError(err).Debug("failed") + return fmt.Errorf("build failed for %s:\n%v", target.PrettyString(), string(out)) } return nil } diff --git a/pipeline/build/build_test.go b/pipeline/build/build_test.go index d86170fb9..d24169f50 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) @@ -112,25 +117,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) @@ -139,19 +146,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)) } @@ -159,19 +168,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)) } @@ -180,14 +191,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{ @@ -201,17 +214,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, + }, }, }, }, @@ -222,26 +237,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 c046a8efe..4a99bab9a 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") @@ -44,31 +42,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, @@ -98,9 +103,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 156d07104..2544710a0 100644 --- a/pipeline/fpm/fpm_test.go +++ b/pipeline/fpm/fpm_test.go @@ -45,18 +45,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"}, @@ -97,18 +93,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"}, }, @@ -116,3 +105,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", + ) +} diff --git a/pipeline/git/git.go b/pipeline/git/git.go index c96d9ce0f..c9e781b40 100644 --- a/pipeline/git/git.go +++ b/pipeline/git/git.go @@ -132,7 +132,7 @@ func getSnapshotName(ctx *context.Context, tag, commit string) (string, error) { } func validate(ctx *context.Context, commit, tag string) error { - out, err := git("status", "-s") + out, err := git("status", "--porcelain") if strings.TrimSpace(out) != "" || err != nil { return ErrDirty{out} } diff --git a/pipeline/release/body.go b/pipeline/release/body.go index 3bc79a207..9da98cf10 100644 --- a/pipeline/release/body.go +++ b/pipeline/release/body.go @@ -11,7 +11,7 @@ import ( const bodyTemplate = `{{ .ReleaseNotes }} --- -Automated with @goreleaser +Automated with [GoReleaser](https://github.com/goreleaser) Built with {{ .GoVersion }} ` diff --git a/pipeline/release/body_test.go b/pipeline/release/body_test.go index 0e5832ee5..328239ca2 100644 --- a/pipeline/release/body_test.go +++ b/pipeline/release/body_test.go @@ -17,7 +17,7 @@ func TestDescribeBody(t *testing.T) { out, err := describeBody(ctx) assert.NoError(err) assert.Contains(out.String(), changelog) - assert.Contains(out.String(), "Automated with @goreleaser") + assert.Contains(out.String(), "Automated with [GoReleaser]") assert.Contains(out.String(), "Built with go version go1.8") }