From 1ef9906e040c914a306fca2e0e3a2a9dbd726615 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Mon, 10 Jun 2019 10:35:19 -0300 Subject: [PATCH] feat: multiple brew / linuxbrew (#1043) * feat: multiple brew / linuxbrew * feat: multiple brew / linuxbrew * test: added missing tests * docs: brews * docs: deprecate brew * test: fix tests * fix: fmt * test: fix tests --- internal/artifact/artifact.go | 12 ++ internal/artifact/artifact_test.go | 37 ++++ internal/pipe/archive/archive.go | 2 + internal/pipe/archive/archive_test.go | 2 + internal/pipe/brew/brew.go | 196 ++++++++++------- internal/pipe/brew/brew_test.go | 202 ++++++++++-------- internal/pipe/brew/template.go | 26 ++- .../brew/testdata/binary_overridden.rb.golden | 7 +- .../brew/testdata/build_from_source.rb.golden | 36 ---- .../pipe/brew/testdata/custom_block.rb.golden | 8 +- .../custom_download_strategy.rb.golden | 8 +- .../brew/testdata/custom_require.rb.golden | 8 +- internal/pipe/brew/testdata/default.rb.golden | 8 +- .../testdata/github_enterprise_url.rb.golden | 8 +- internal/pipe/brew/testdata/test.rb.golden | 10 +- internal/pipe/defaults/defaults_test.go | 4 +- pkg/config/config.go | 5 +- www/content/deprecations.md | 21 ++ www/content/homebrew.md | 159 +++++++------- 19 files changed, 452 insertions(+), 307 deletions(-) delete mode 100644 internal/pipe/brew/testdata/build_from_source.rb.golden diff --git a/internal/artifact/artifact.go b/internal/artifact/artifact.go index 32906934d..5b190f222 100644 --- a/internal/artifact/artifact.go +++ b/internal/artifact/artifact.go @@ -193,6 +193,18 @@ func ByType(t Type) Filter { } } +// ByFormats filters artifacts by a `Format` extra field. +func ByFormats(formats ...string) Filter { + var filters = make([]Filter, 0, len(formats)) + for _, format := range formats { + format := format + filters = append(filters, func(a Artifact) bool { + return a.ExtraOr("Format", "") == format + }) + } + return Or(filters...) +} + // ByIDs filter artifacts by an `ID` extra field. func ByIDs(ids ...string) Filter { var filters = make([]Filter, 0, len(ids)) diff --git a/internal/artifact/artifact_test.go b/internal/artifact/artifact_test.go index ab9ce304f..546d443f5 100644 --- a/internal/artifact/artifact_test.go +++ b/internal/artifact/artifact_test.go @@ -232,3 +232,40 @@ func TestByIDs(t *testing.T) { require.Len(t, artifacts.Filter(ByIDs("foo")).items, 2) require.Len(t, artifacts.Filter(ByIDs("foo", "bar")).items, 3) } + +func TestByFormats(t *testing.T) { + var data = []Artifact{ + { + Name: "foo", + Extra: map[string]interface{}{ + "Format": "zip", + }, + }, + { + Name: "bar", + Extra: map[string]interface{}{ + "Format": "tar.gz", + }, + }, + { + Name: "foobar", + Extra: map[string]interface{}{ + "Format": "zip", + }, + }, + { + Name: "bin", + Extra: map[string]interface{}{ + "Format": "binary", + }, + }, + } + var artifacts = New() + for _, a := range data { + artifacts.Add(a) + } + + require.Len(t, artifacts.Filter(ByFormats("binary")).items, 1) + require.Len(t, artifacts.Filter(ByFormats("zip")).items, 2) + require.Len(t, artifacts.Filter(ByFormats("zip", "tar.gz")).items, 3) +} diff --git a/internal/pipe/archive/archive.go b/internal/pipe/archive/archive.go index b15f5cd26..70be78ec8 100644 --- a/internal/pipe/archive/archive.go +++ b/internal/pipe/archive/archive.go @@ -169,6 +169,7 @@ func create(ctx *context.Context, archive config.Archive, binaries []artifact.Ar Extra: map[string]interface{}{ "Builds": binaries, "ID": archive.ID, + "Format": archive.Format, }, }) return nil @@ -204,6 +205,7 @@ func skip(ctx *context.Context, archive config.Archive, binaries []artifact.Arti Extra: map[string]interface{}{ "Builds": []artifact.Artifact{binary}, "ID": archive.ID, + "Format": archive.Format, }, }) } diff --git a/internal/pipe/archive/archive_test.go b/internal/pipe/archive/archive_test.go index 68db45758..9987caf39 100644 --- a/internal/pipe/archive/archive_test.go +++ b/internal/pipe/archive/archive_test.go @@ -561,10 +561,12 @@ func TestBinaryOverride(t *testing.T) { var archives = ctx.Artifacts.Filter(artifact.ByType(artifact.UploadableArchive)) darwin := archives.Filter(artifact.ByGoos("darwin")).List()[0] require.Equal(tt, "foobar_0.0.1_darwin_amd64."+format, darwin.Name) + require.Equal(tt, format, darwin.ExtraOr("Format", "")) archives = ctx.Artifacts.Filter(artifact.ByType(artifact.UploadableBinary)) windows := archives.Filter(artifact.ByGoos("windows")).List()[0] require.Equal(tt, "foobar_0.0.1_windows_amd64.exe", windows.Name) + require.Equal(tt, format, windows.ExtraOr("Format", "")) }) } } diff --git a/internal/pipe/brew/brew.go b/internal/pipe/brew/brew.go index 591276ab9..1e6e5731f 100644 --- a/internal/pipe/brew/brew.go +++ b/internal/pipe/brew/brew.go @@ -7,23 +7,28 @@ import ( "io/ioutil" "path" "path/filepath" + "reflect" "strings" "text/template" "github.com/apex/log" "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/internal/client" + "github.com/goreleaser/goreleaser/internal/deprecate" "github.com/goreleaser/goreleaser/internal/pipe" + "github.com/goreleaser/goreleaser/internal/semerrgroup" "github.com/goreleaser/goreleaser/internal/tmpl" "github.com/goreleaser/goreleaser/pkg/config" "github.com/goreleaser/goreleaser/pkg/context" ) -// ErrNoDarwin64Build when there is no build for darwin_amd64 -var ErrNoDarwin64Build = errors.New("brew tap requires one darwin amd64 build") +// ErrNoArchivesFound happens when 0 archives are found +var ErrNoArchivesFound = errors.New("brew tap: no archives found matching criteria") -// ErrTooManyDarwin64Builds when there are too many builds for darwin_amd64 -var ErrTooManyDarwin64Builds = errors.New("brew tap requires at most one darwin amd64 build") +// ErrMultipleArchivesSameOS happens when the config yields multiple archives +// for linux or windows. +// TODO: improve this confusing error message +var ErrMultipleArchivesSameOS = errors.New("brew tap: one tap can handle only 1 linux and 1 macos archive") // Pipe for brew deployment type Pipe struct{} @@ -38,34 +43,53 @@ func (Pipe) Publish(ctx *context.Context) error { if err != nil { return err } - return doRun(ctx, client) + var g = semerrgroup.New(ctx.Parallelism) + for _, brew := range ctx.Config.Brews { + brew := brew + g.Go(func() error { + return doRun(ctx, brew, client) + }) + } + return g.Wait() } // Default sets the pipe defaults func (Pipe) Default(ctx *context.Context) error { - if ctx.Config.Brew.Install == "" { - var installs []string - for _, build := range ctx.Config.Builds { - if !isBrewBuild(build) { - continue - } - installs = append( - installs, - fmt.Sprintf(`bin.install "%s"`, build.Binary), - ) + if len(ctx.Config.Brews) == 0 { + ctx.Config.Brews = append(ctx.Config.Brews, ctx.Config.Brew) + if !reflect.DeepEqual(ctx.Config.Brew, config.Homebrew{}) { + deprecate.Notice("brew") + } + } + for i := range ctx.Config.Brews { + var brew = &ctx.Config.Brews[i] + if brew.Install == "" { + // TODO: maybe replace this with a simplear also optimistic + // approach of just doing `bin.install "project_name"`? + var installs []string + for _, build := range ctx.Config.Builds { + if !isBrewBuild(build) { + continue + } + installs = append( + installs, + fmt.Sprintf(`bin.install "%s"`, build.Binary), + ) + } + brew.Install = strings.Join(installs, "\n") + log.Warnf("optimistically guessing `brew[%d].installs`, double check", i) + } + if brew.CommitAuthor.Name == "" { + brew.CommitAuthor.Name = "goreleaserbot" + } + if brew.CommitAuthor.Email == "" { + brew.CommitAuthor.Email = "goreleaser@carlosbecker.com" + } + if brew.Name == "" { + brew.Name = ctx.Config.ProjectName } - ctx.Config.Brew.Install = strings.Join(installs, "\n") } - if ctx.Config.Brew.CommitAuthor.Name == "" { - ctx.Config.Brew.CommitAuthor.Name = "goreleaserbot" - } - if ctx.Config.Brew.CommitAuthor.Email == "" { - ctx.Config.Brew.CommitAuthor.Email = "goreleaser@carlosbecker.com" - } - if ctx.Config.Brew.Name == "" { - ctx.Config.Brew.Name = ctx.Config.ProjectName - } return nil } @@ -87,42 +111,41 @@ func contains(ss []string, s string) bool { return false } -func doRun(ctx *context.Context, client client.Client) error { - if ctx.Config.Brew.GitHub.Name == "" { +func doRun(ctx *context.Context, brew config.Homebrew, client client.Client) error { + if brew.GitHub.Name == "" { return pipe.Skip("brew section is not configured") } - if getFormat(ctx) == "binary" { - return pipe.Skip("archive format is binary") - } - - var archives = ctx.Artifacts.Filter( - artifact.And( + var filters = []artifact.Filter{ + artifact.Or( artifact.ByGoos("darwin"), - artifact.ByGoarch("amd64"), - artifact.ByGoarm(""), - artifact.ByType(artifact.UploadableArchive), + artifact.ByGoos("linux"), ), - ).List() - if len(archives) == 0 { - return ErrNoDarwin64Build + artifact.ByFormats("zip", "tar.gz"), + artifact.ByGoarch("amd64"), + artifact.ByType(artifact.UploadableArchive), } - if len(archives) > 1 { - return ErrTooManyDarwin64Builds + if len(brew.IDs) > 0 { + filters = append(filters, artifact.ByIDs(brew.IDs...)) } - content, err := buildFormula(ctx, archives[0]) + var archives = ctx.Artifacts.Filter(artifact.And(filters...)).List() + if len(archives) == 0 { + return ErrNoArchivesFound + } + + content, err := buildFormula(ctx, brew, archives) if err != nil { return err } - var filename = ctx.Config.Brew.Name + ".rb" + var filename = brew.Name + ".rb" var path = filepath.Join(ctx.Config.Dist, filename) log.WithField("formula", path).Info("writing") if err := ioutil.WriteFile(path, content.Bytes(), 0644); err != nil { return err } - if strings.TrimSpace(ctx.Config.Brew.SkipUpload) == "true" { + if strings.TrimSpace(brew.SkipUpload) == "true" { return pipe.Skip("brew.skip_upload is set") } if ctx.SkipPublish { @@ -131,34 +154,25 @@ func doRun(ctx *context.Context, client client.Client) error { if ctx.Config.Release.Draft { return pipe.Skip("release is marked as draft") } - if strings.TrimSpace(ctx.Config.Brew.SkipUpload) == "auto" && ctx.Semver.Prerelease != "" { + if strings.TrimSpace(brew.SkipUpload) == "auto" && ctx.Semver.Prerelease != "" { return pipe.Skip("prerelease detected with 'auto' upload, skipping homebrew publish") } - var gpath = ghFormulaPath(ctx.Config.Brew.Folder, filename) + var gpath = ghFormulaPath(brew.Folder, filename) log.WithField("formula", gpath). - WithField("repo", ctx.Config.Brew.GitHub.String()). + WithField("repo", brew.GitHub.String()). Info("pushing") var msg = fmt.Sprintf("Brew formula update for %s version %s", ctx.Config.ProjectName, ctx.Git.CurrentTag) - return client.CreateFile(ctx, ctx.Config.Brew.CommitAuthor, ctx.Config.Brew.GitHub, content, gpath, msg) + return client.CreateFile(ctx, brew.CommitAuthor, brew.GitHub, content, gpath, msg) } func ghFormulaPath(folder, filename string) string { return path.Join(folder, filename) } -func getFormat(ctx *context.Context) string { - for _, override := range ctx.Config.Archive.FormatOverrides { - if strings.HasPrefix(override.Goos, "darwin") { - return override.Format - } - } - return ctx.Config.Archive.Format -} - -func buildFormula(ctx *context.Context, artifact artifact.Artifact) (bytes.Buffer, error) { - data, err := dataFor(ctx, artifact) +func buildFormula(ctx *context.Context, brew config.Homebrew, artifacts []artifact.Artifact) (bytes.Buffer, error) { + data, err := dataFor(ctx, brew, artifacts) if err != nil { return bytes.Buffer{}, err } @@ -174,32 +188,13 @@ func doBuildFormula(data templateData) (out bytes.Buffer, err error) { return } -func dataFor(ctx *context.Context, artifact artifact.Artifact) (result templateData, err error) { - sum, err := artifact.Checksum("sha256") - if err != nil { - return - } - var cfg = ctx.Config.Brew - - if ctx.Config.Brew.URLTemplate == "" { - ctx.Config.Brew.URLTemplate = fmt.Sprintf("%s/%s/%s/releases/download/{{ .Tag }}/{{ .ArtifactName }}", - ctx.Config.GitHubURLs.Download, - ctx.Config.Release.GitHub.Owner, - ctx.Config.Release.GitHub.Name) - } - url, err := tmpl.New(ctx).WithArtifact(artifact, map[string]string{}).Apply(ctx.Config.Brew.URLTemplate) - if err != nil { - return - } - - return templateData{ - Name: formulaNameFor(ctx.Config.Brew.Name), - DownloadURL: url, +func dataFor(ctx *context.Context, cfg config.Homebrew, artifacts []artifact.Artifact) (templateData, error) { + var result = templateData{ + Name: formulaNameFor(cfg.Name), Desc: cfg.Description, Homepage: cfg.Homepage, Version: ctx.Version, Caveats: split(cfg.Caveats), - SHA256: sum, Dependencies: cfg.Dependencies, Conflicts: cfg.Conflicts, Plist: cfg.Plist, @@ -208,7 +203,44 @@ func dataFor(ctx *context.Context, artifact artifact.Artifact) (result templateD DownloadStrategy: cfg.DownloadStrategy, CustomRequire: cfg.CustomRequire, CustomBlock: split(cfg.CustomBlock), - }, nil + } + + for _, artifact := range artifacts { + sum, err := artifact.Checksum("sha256") + if err != nil { + return result, err + } + + if cfg.URLTemplate == "" { + cfg.URLTemplate = fmt.Sprintf( + "%s/%s/%s/releases/download/{{ .Tag }}/{{ .ArtifactName }}", + ctx.Config.GitHubURLs.Download, + ctx.Config.Release.GitHub.Owner, + ctx.Config.Release.GitHub.Name, + ) + } + url, err := tmpl.New(ctx).WithArtifact(artifact, map[string]string{}).Apply(cfg.URLTemplate) + if err != nil { + return result, err + } + var down = downloadable{ + DownloadURL: url, + SHA256: sum, + } + if artifact.Goos == "darwin" { + if result.MacOS.DownloadURL != "" { + return result, ErrMultipleArchivesSameOS + } + result.MacOS = down + } else if artifact.Goos == "linux" { + if result.Linux.DownloadURL != "" { + return result, ErrMultipleArchivesSameOS + } + result.Linux = down + } + } + + return result, nil } func split(s string) []string { diff --git a/internal/pipe/brew/brew_test.go b/internal/pipe/brew/brew_test.go index eb0171ee4..340b6fc57 100644 --- a/internal/pipe/brew/brew_test.go +++ b/internal/pipe/brew/brew_test.go @@ -35,13 +35,19 @@ func TestSimpleName(t *testing.T) { } var defaultTemplateData = templateData{ - Desc: "Some desc", - Homepage: "https://google.com", - DownloadURL: "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Darwin_x86_64.tar.gz", - Name: "Test", - Version: "0.1.3", - Caveats: []string{}, - SHA256: "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c68", + Desc: "Some desc", + Homepage: "https://google.com", + MacOS: downloadable{ + DownloadURL: "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Darwin_x86_64.tar.gz", + SHA256: "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c68", + }, + Linux: downloadable{ + DownloadURL: "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Linux_x86_64.tar.gz", + SHA256: "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c67", + }, + Name: "Test", + Version: "0.1.3", + Caveats: []string{}, } func assertDefaultTemplateData(t *testing.T, formulae string) { @@ -101,23 +107,14 @@ func TestRunPipe(t *testing.T) { ctx.Config.GitHubURLs.Download = "http://github.example.org" }, "custom_download_strategy": func(ctx *context.Context) { - ctx.Config.Brew.DownloadStrategy = "GitHubPrivateRepositoryReleaseDownloadStrategy" + ctx.Config.Brews[0].DownloadStrategy = "GitHubPrivateRepositoryReleaseDownloadStrategy" }, "custom_require": func(ctx *context.Context) { - ctx.Config.Brew.DownloadStrategy = "CustomDownloadStrategy" - ctx.Config.Brew.CustomRequire = "custom_download_strategy" - }, - "binary_overridden": func(ctx *context.Context) { - ctx.Config.Archive.Format = "binary" - ctx.Config.Archive.FormatOverrides = []config.FormatOverride{ - { - Goos: "darwin", - Format: "zip", - }, - } + ctx.Config.Brews[0].DownloadStrategy = "CustomDownloadStrategy" + ctx.Config.Brews[0].CustomRequire = "custom_download_strategy" }, "custom_block": func(ctx *context.Context) { - ctx.Config.Brew.CustomBlock = `head "https://github.com/caarlos0/test.git"` + ctx.Config.Brews[0].CustomBlock = `head "https://github.com/caarlos0/test.git"` }, } { t.Run(name, func(t *testing.T) { @@ -135,41 +132,57 @@ func TestRunPipe(t *testing.T) { GitHubURLs: config.GitHubURLs{ Download: "https://github.com", }, - Archive: config.Archive{ - Format: "tar.gz", - }, Release: config.Release{ GitHub: config.Repo{ Owner: "test", Name: "test", }, }, - Brew: config.Homebrew{ - Name: name, - GitHub: config.Repo{ - Owner: "test", - Name: "test", + Brews: []config.Homebrew{ + { + Name: name, + GitHub: config.Repo{ + Owner: "test", + Name: "test", + }, + IDs: []string{ + "foo", + }, + Description: "A run pipe test formula", + Homepage: "https://github.com/goreleaser", + Caveats: "don't do this", + Test: "system \"true\"\nsystem \"#{bin}/foo -h\"", + Plist: `whatever`, + Dependencies: []string{"zsh", "bash"}, + Conflicts: []string{"gtk+", "qt"}, + Install: `bin.install "foo"`, }, - Description: "A run pipe test formula", - Homepage: "https://github.com/goreleaser", - Caveats: "don't do this", - Test: "system \"true\"\nsystem \"#{bin}/foo -h\"", - Plist: `whatever`, - Dependencies: []string{"zsh", "bash"}, - Conflicts: []string{"gtk+", "qt"}, - Install: `bin.install "foo"`, }, }, } fn(ctx) - var format = getFormat(ctx) - var path = filepath.Join(folder, "bin."+format) ctx.Artifacts.Add(artifact.Artifact{ - Name: "bin." + format, + Name: "bar_bin.tar.gz", + Path: "doesnt matter", + Goos: "darwin", + Goarch: "amd64", + Type: artifact.UploadableArchive, + Extra: map[string]interface{}{ + "ID": "bar", + "Format": "tar.gz", + }, + }) + var path = filepath.Join(folder, "bin.tar.gz") + ctx.Artifacts.Add(artifact.Artifact{ + Name: "bin.tar.gz", Path: path, Goos: "darwin", Goarch: "amd64", Type: artifact.UploadableArchive, + Extra: map[string]interface{}{ + "ID": "foo", + "Format": "tar.gz", + }, }) _, err = os.Create(path) @@ -177,7 +190,7 @@ func TestRunPipe(t *testing.T) { client := &DummyClient{} var distFile = filepath.Join(folder, name+".rb") - assert.NoError(t, doRun(ctx, client)) + assert.NoError(t, doRun(ctx, ctx.Config.Brews[0], client)) assert.True(t, client.CreatedFile) var golden = fmt.Sprintf("testdata/%s.rb.golden", name) if *update { @@ -197,52 +210,62 @@ func TestRunPipe(t *testing.T) { func TestRunPipeNoDarwin64Build(t *testing.T) { var ctx = &context.Context{ Config: config.Project{ - Archive: config.Archive{ - Format: "tar.gz", - }, - Brew: config.Homebrew{ - GitHub: config.Repo{ - Owner: "test", - Name: "test", + Brews: []config.Homebrew{ + { + GitHub: config.Repo{ + Owner: "test", + Name: "test", + }, }, }, }, } client := &DummyClient{} - assert.Equal(t, ErrNoDarwin64Build, doRun(ctx, client)) + assert.Equal(t, ErrNoArchivesFound, doRun(ctx, ctx.Config.Brews[0], client)) assert.False(t, client.CreatedFile) } func TestRunPipeMultipleDarwin64Build(t *testing.T) { var ctx = context.New( config.Project{ - Archive: config.Archive{ - Format: "tar.gz", - }, - Brew: config.Homebrew{ - GitHub: config.Repo{ - Owner: "test", - Name: "test", + Brews: []config.Homebrew{ + { + GitHub: config.Repo{ + Owner: "test", + Name: "test", + }, }, }, }, ) + + f, err := ioutil.TempFile("", "") + assert.NoError(t, err) + defer f.Close() ctx.Artifacts.Add(artifact.Artifact{ Name: "bin1", - Path: "doesnt mather", + Path: f.Name(), Goos: "darwin", Goarch: "amd64", Type: artifact.UploadableArchive, + Extra: map[string]interface{}{ + "ID": "foo", + "Format": "tar.gz", + }, }) ctx.Artifacts.Add(artifact.Artifact{ Name: "bin2", - Path: "doesnt mather", + Path: f.Name(), Goos: "darwin", Goarch: "amd64", Type: artifact.UploadableArchive, + Extra: map[string]interface{}{ + "ID": "bar", + "Format": "tar.gz", + }, }) client := &DummyClient{} - assert.Equal(t, ErrTooManyDarwin64Builds, doRun(ctx, client)) + assert.Equal(t, ErrMultipleArchivesSameOS, doRun(ctx, ctx.Config.Brews[0], client)) assert.False(t, client.CreatedFile) } @@ -251,20 +274,19 @@ func TestRunPipeBrewNotSetup(t *testing.T) { Config: config.Project{}, } client := &DummyClient{} - testlib.AssertSkipped(t, doRun(ctx, client)) + testlib.AssertSkipped(t, doRun(ctx, config.Homebrew{}, client)) assert.False(t, client.CreatedFile) } func TestRunPipeBinaryRelease(t *testing.T) { var ctx = context.New( config.Project{ - Archive: config.Archive{ - Format: "binary", - }, - Brew: config.Homebrew{ - GitHub: config.Repo{ - Owner: "test", - Name: "test", + Brews: []config.Homebrew{ + { + GitHub: config.Repo{ + Owner: "test", + Name: "test", + }, }, }, }, @@ -277,7 +299,7 @@ func TestRunPipeBinaryRelease(t *testing.T) { Type: artifact.Binary, }) client := &DummyClient{} - testlib.AssertSkipped(t, doRun(ctx, client)) + assert.Equal(t, ErrNoArchivesFound, doRun(ctx, ctx.Config.Brews[0], client)) assert.False(t, client.CreatedFile) } @@ -288,10 +310,12 @@ func TestRunPipeNoUpload(t *testing.T) { Dist: folder, ProjectName: "foo", Release: config.Release{}, - Brew: config.Homebrew{ - GitHub: config.Repo{ - Owner: "test", - Name: "test", + Brews: []config.Homebrew{ + { + GitHub: config.Repo{ + Owner: "test", + Name: "test", + }, }, }, }) @@ -305,34 +329,38 @@ func TestRunPipeNoUpload(t *testing.T) { Goos: "darwin", Goarch: "amd64", Type: artifact.UploadableArchive, + Extra: map[string]interface{}{ + "ID": "foo", + "Format": "tar.gz", + }, }) client := &DummyClient{} var assertNoPublish = func(t *testing.T) { - testlib.AssertSkipped(t, doRun(ctx, client)) + testlib.AssertSkipped(t, doRun(ctx, ctx.Config.Brews[0], client)) assert.False(t, client.CreatedFile) } t.Run("skip upload", func(tt *testing.T) { ctx.Config.Release.Draft = false - ctx.Config.Brew.SkipUpload = "true" + ctx.Config.Brews[0].SkipUpload = "true" ctx.SkipPublish = false assertNoPublish(tt) }) t.Run("skip publish", func(tt *testing.T) { ctx.Config.Release.Draft = false - ctx.Config.Brew.SkipUpload = "false" + ctx.Config.Brews[0].SkipUpload = "false" ctx.SkipPublish = true assertNoPublish(tt) }) t.Run("draft release", func(tt *testing.T) { ctx.Config.Release.Draft = true - ctx.Config.Brew.SkipUpload = "false" + ctx.Config.Brews[0].SkipUpload = "false" ctx.SkipPublish = false assertNoPublish(tt) }) t.Run("skip prerelease publish", func(tt *testing.T) { ctx.Config.Release.Draft = false - ctx.Config.Brew.SkipUpload = "auto" + ctx.Config.Brews[0].SkipUpload = "auto" ctx.SkipPublish = false ctx.Semver = context.Semver{ Major: 1, @@ -344,19 +372,6 @@ func TestRunPipeNoUpload(t *testing.T) { }) } -func TestRunPipeFormatBinary(t *testing.T) { - var ctx = &context.Context{ - Config: config.Project{ - Archive: config.Archive{ - Format: "binary", - }, - }, - } - client := &DummyClient{} - testlib.AssertSkipped(t, doRun(ctx, client)) - assert.False(t, client.CreatedFile) -} - func TestDefault(t *testing.T) { _, back := testlib.Mktmp(t) defer back() @@ -364,6 +379,7 @@ func TestDefault(t *testing.T) { var ctx = &context.Context{ Config: config.Project{ ProjectName: "myproject", + Brews: []config.Homebrew{}, Builds: []config.Build{ { Binary: "foo", @@ -387,10 +403,10 @@ func TestDefault(t *testing.T) { }, } assert.NoError(t, Pipe{}.Default(ctx)) - assert.Equal(t, ctx.Config.ProjectName, ctx.Config.Brew.Name) - assert.NotEmpty(t, ctx.Config.Brew.CommitAuthor.Name) - assert.NotEmpty(t, ctx.Config.Brew.CommitAuthor.Email) - assert.Equal(t, `bin.install "foo"`, ctx.Config.Brew.Install) + assert.Equal(t, ctx.Config.ProjectName, ctx.Config.Brews[0].Name) + assert.NotEmpty(t, ctx.Config.Brews[0].CommitAuthor.Name) + assert.NotEmpty(t, ctx.Config.Brews[0].CommitAuthor.Email) + assert.Equal(t, `bin.install "foo"`, ctx.Config.Brews[0].Install) } func TestGHFolder(t *testing.T) { diff --git a/internal/pipe/brew/template.go b/internal/pipe/brew/template.go index 133e26d21..463c502a5 100644 --- a/internal/pipe/brew/template.go +++ b/internal/pipe/brew/template.go @@ -4,10 +4,8 @@ type templateData struct { Name string Desc string Homepage string - DownloadURL string Version string Caveats []string - SHA256 string Plist string DownloadStrategy string Install []string @@ -16,6 +14,13 @@ type templateData struct { Tests []string CustomRequire string CustomBlock []string + MacOS downloadable + Linux downloadable +} + +type downloadable struct { + DownloadURL string + SHA256 string } const formulaTemplate = `# This file was generated by GoReleaser. DO NOT EDIT. @@ -25,10 +30,21 @@ require_relative "{{ .CustomRequire }}" class {{ .Name }} < Formula desc "{{ .Desc }}" homepage "{{ .Homepage }}" - url "{{ .DownloadURL }}" - {{- if .DownloadStrategy }}, :using => {{ .DownloadStrategy }}{{- end }} version "{{ .Version }}" - sha256 "{{ .SHA256 }}" + + if OS.mac? + {{- if .MacOS.DownloadURL }} + url "{{ .MacOS.DownloadURL }}" + sha256 "{{ .MacOS.SHA256 }}" + {{- end }} + elsif OS.linux? + {{- if .Linux.DownloadURL }} + url "{{ .Linux.DownloadURL }}" + sha256 "{{ .Linux.SHA256 }}" + {{- end }} + end + + {{- if .DownloadStrategy }}, :using => {{ .DownloadStrategy }}{{- end }} {{- with .CustomBlock }} {{ range $index, $element := . }} diff --git a/internal/pipe/brew/testdata/binary_overridden.rb.golden b/internal/pipe/brew/testdata/binary_overridden.rb.golden index a90fe9879..2704b242c 100644 --- a/internal/pipe/brew/testdata/binary_overridden.rb.golden +++ b/internal/pipe/brew/testdata/binary_overridden.rb.golden @@ -2,9 +2,12 @@ class BinaryOverridden < Formula desc "A run pipe test formula" homepage "https://github.com/goreleaser" - url "https://github.com/test/test/releases/download/v1.0.1/bin.zip" version "1.0.1" - sha256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + if OS.mac? + url "https://github.com/test/test/releases/download/v1.0.1/bin.todo" + sha256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + elsif OS.linux? + end depends_on "zsh" depends_on "bash" diff --git a/internal/pipe/brew/testdata/build_from_source.rb.golden b/internal/pipe/brew/testdata/build_from_source.rb.golden deleted file mode 100644 index d842cf77e..000000000 --- a/internal/pipe/brew/testdata/build_from_source.rb.golden +++ /dev/null @@ -1,36 +0,0 @@ -class BuildFromSource < Formula - desc "A run pipe test formula" - homepage "https://github.com/goreleaser" - url "https://github.com/test/test/archive/v1.0.1.tar.gz" - head "https://github.com/test/test.git" - version "1.0.1" - sha256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - - depends_on "zsh" - depends_on "bash" - depends_on "go" => :build - - conflicts_with "gtk+" - conflicts_with "qt" - - def install - bin.install "foo" - end - - def caveats; <<~EOS - don't do this - EOS - end - - plist_options :startup => false - - def plist; <<~EOS - whatever - EOS - end - - test do - system "true" - system "#{bin}/foo -h" - end -end diff --git a/internal/pipe/brew/testdata/custom_block.rb.golden b/internal/pipe/brew/testdata/custom_block.rb.golden index 9a5e35e5f..bc3d8fb7e 100644 --- a/internal/pipe/brew/testdata/custom_block.rb.golden +++ b/internal/pipe/brew/testdata/custom_block.rb.golden @@ -2,9 +2,13 @@ class CustomBlock < Formula desc "A run pipe test formula" homepage "https://github.com/goreleaser" - url "https://github.com/test/test/releases/download/v1.0.1/bin.tar.gz" version "1.0.1" - sha256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + + if OS.mac? + url "https://github.com/test/test/releases/download/v1.0.1/bin.tar.gz" + sha256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + elsif OS.linux? + end head "https://github.com/caarlos0/test.git" diff --git a/internal/pipe/brew/testdata/custom_download_strategy.rb.golden b/internal/pipe/brew/testdata/custom_download_strategy.rb.golden index bf1e9a1b7..754dae34f 100644 --- a/internal/pipe/brew/testdata/custom_download_strategy.rb.golden +++ b/internal/pipe/brew/testdata/custom_download_strategy.rb.golden @@ -2,9 +2,13 @@ class CustomDownloadStrategy < Formula desc "A run pipe test formula" homepage "https://github.com/goreleaser" - url "https://github.com/test/test/releases/download/v1.0.1/bin.tar.gz", :using => GitHubPrivateRepositoryReleaseDownloadStrategy version "1.0.1" - sha256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + + if OS.mac? + url "https://github.com/test/test/releases/download/v1.0.1/bin.tar.gz" + sha256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + elsif OS.linux? + end, :using => GitHubPrivateRepositoryReleaseDownloadStrategy depends_on "zsh" depends_on "bash" diff --git a/internal/pipe/brew/testdata/custom_require.rb.golden b/internal/pipe/brew/testdata/custom_require.rb.golden index 3bef6114d..1366d5aa5 100644 --- a/internal/pipe/brew/testdata/custom_require.rb.golden +++ b/internal/pipe/brew/testdata/custom_require.rb.golden @@ -3,9 +3,13 @@ require_relative "custom_download_strategy" class CustomRequire < Formula desc "A run pipe test formula" homepage "https://github.com/goreleaser" - url "https://github.com/test/test/releases/download/v1.0.1/bin.tar.gz", :using => CustomDownloadStrategy version "1.0.1" - sha256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + + if OS.mac? + url "https://github.com/test/test/releases/download/v1.0.1/bin.tar.gz" + sha256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + elsif OS.linux? + end, :using => CustomDownloadStrategy depends_on "zsh" depends_on "bash" diff --git a/internal/pipe/brew/testdata/default.rb.golden b/internal/pipe/brew/testdata/default.rb.golden index b936d6583..de7e8eb12 100644 --- a/internal/pipe/brew/testdata/default.rb.golden +++ b/internal/pipe/brew/testdata/default.rb.golden @@ -2,9 +2,13 @@ class Default < Formula desc "A run pipe test formula" homepage "https://github.com/goreleaser" - url "https://github.com/test/test/releases/download/v1.0.1/bin.tar.gz" version "1.0.1" - sha256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + + if OS.mac? + url "https://github.com/test/test/releases/download/v1.0.1/bin.tar.gz" + sha256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + elsif OS.linux? + end depends_on "zsh" depends_on "bash" diff --git a/internal/pipe/brew/testdata/github_enterprise_url.rb.golden b/internal/pipe/brew/testdata/github_enterprise_url.rb.golden index 3d2acf34d..b67e36b0a 100644 --- a/internal/pipe/brew/testdata/github_enterprise_url.rb.golden +++ b/internal/pipe/brew/testdata/github_enterprise_url.rb.golden @@ -2,9 +2,13 @@ class GithubEnterpriseUrl < Formula desc "A run pipe test formula" homepage "https://github.com/goreleaser" - url "http://github.example.org/test/test/releases/download/v1.0.1/bin.tar.gz" version "1.0.1" - sha256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + + if OS.mac? + url "http://github.example.org/test/test/releases/download/v1.0.1/bin.tar.gz" + sha256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + elsif OS.linux? + end depends_on "zsh" depends_on "bash" diff --git a/internal/pipe/brew/testdata/test.rb.golden b/internal/pipe/brew/testdata/test.rb.golden index f251343b2..1ff6f3987 100644 --- a/internal/pipe/brew/testdata/test.rb.golden +++ b/internal/pipe/brew/testdata/test.rb.golden @@ -2,9 +2,15 @@ class Test < Formula desc "Some desc" homepage "https://google.com" - url "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Darwin_x86_64.tar.gz" version "0.1.3" - sha256 "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c68" + + if OS.mac? + url "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Darwin_x86_64.tar.gz" + sha256 "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c68" + elsif OS.linux? + url "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Linux_x86_64.tar.gz" + sha256 "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c67" + end devel do url "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Darwin_x86_64.tar.gz" diff --git a/internal/pipe/defaults/defaults_test.go b/internal/pipe/defaults/defaults_test.go index 4484cde11..e78b5fe13 100644 --- a/internal/pipe/defaults/defaults_test.go +++ b/internal/pipe/defaults/defaults_test.go @@ -35,7 +35,7 @@ func TestFillBasicData(t *testing.T) { assert.Contains(t, ctx.Config.Builds[0].Goarch, "386") assert.Contains(t, ctx.Config.Builds[0].Goarch, "amd64") assert.Equal(t, "tar.gz", ctx.Config.Archives[0].Format) - assert.Contains(t, ctx.Config.Brew.Install, "bin.install \"goreleaser\"") + assert.Contains(t, ctx.Config.Brews[0].Install, "bin.install \"goreleaser\"") assert.Empty(t, ctx.Config.Dockers) assert.Equal(t, "https://github.com", ctx.Config.GitHubURLs.Download) assert.NotEmpty(t, ctx.Config.Archives[0].NameTemplate) @@ -86,7 +86,7 @@ func TestFillPartial(t *testing.T) { } assert.NoError(t, Pipe{}.Run(ctx)) assert.Len(t, ctx.Config.Archive.Files, 1) - assert.Equal(t, `bin.install "testreleaser"`, ctx.Config.Brew.Install) + assert.Equal(t, `bin.install "testreleaser"`, ctx.Config.Brews[0].Install) assert.NotEmpty(t, ctx.Config.Dockers[0].Binaries) assert.NotEmpty(t, ctx.Config.Dockers[0].Goos) assert.NotEmpty(t, ctx.Config.Dockers[0].Goarch) diff --git a/pkg/config/config.go b/pkg/config/config.go index 719608786..f1dc5f6d3 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -50,10 +50,10 @@ type Homebrew struct { Homepage string `yaml:",omitempty"` SkipUpload string `yaml:"skip_upload,omitempty"` DownloadStrategy string `yaml:"download_strategy,omitempty"` - SourceTarball string `yaml:"-"` URLTemplate string `yaml:"url_template,omitempty"` CustomRequire string `yaml:"custom_require,omitempty"` CustomBlock string `yaml:"custom_block,omitempty"` + IDs []string `yaml:"ids,omitempty"` } // Scoop contains the scoop.sh section @@ -326,7 +326,8 @@ type Project struct { ProjectName string `yaml:"project_name,omitempty"` Env []string `yaml:",omitempty"` Release Release `yaml:",omitempty"` - Brew Homebrew `yaml:",omitempty"` + Brew Homebrew `yaml:",omitempty"` // TODO: remove this + Brews []Homebrew `yaml:",omitempty"` Scoop Scoop `yaml:",omitempty"` Builds []Build `yaml:",omitempty"` Archive Archive `yaml:",omitempty"` // TODO: remove this diff --git a/www/content/deprecations.md b/www/content/deprecations.md index 5047e664c..f60e19a0b 100644 --- a/www/content/deprecations.md +++ b/www/content/deprecations.md @@ -33,6 +33,27 @@ to this: --> +### brew + +> since 2019-06-09 + +Brew was deprecated in favor of its plural form. + +Change this: + +```yaml +brew: + # etc +``` + +to this: + +```yaml +brews: + - + # etc +``` + ### s3 > since 2019-06-09 diff --git a/www/content/homebrew.md b/www/content/homebrew.md index 7f4f01ede..f3133c399 100644 --- a/www/content/homebrew.md +++ b/www/content/homebrew.md @@ -17,92 +17,99 @@ for more details. ```yml # .goreleaser.yml -brew: - # Name template of the recipe - # Default to project name - name: myproject +brews: + - + # Name template of the recipe + # Default to project name + name: myproject - # Repository to push the tap to. - github: - owner: user - name: homebrew-tap + # IDs of the archives to use. + # Defaults to all. + ids: + - foo + - bar - # Template for the url. - # Default is "https://github.com///releases/download/{{ .Tag }}/{{ .ArtifactName }}" - url_template: "http://github.mycompany.com/foo/bar/releases/{{ .Tag }}/{{ .ArtifactName }}" + # Repository to push the tap to. + github: + owner: user + name: homebrew-tap - # Allows you to set a custom download strategy. - # Default is empty. - download_strategy: GitHubPrivateRepositoryReleaseDownloadStrategy + # Template for the url. + # Default is "https://github.com///releases/download/{{ .Tag }}/{{ .ArtifactName }}" + url_template: "http://github.mycompany.com/foo/bar/releases/{{ .Tag }}/{{ .ArtifactName }}" - # Allows you to add a custom require_relative at the top of the formula template - # Default is empty - custom_require: custom_download_strategy + # Allows you to set a custom download strategy. + # Default is empty. + download_strategy: GitHubPrivateRepositoryReleaseDownloadStrategy - # Git author used to commit to the repository. - # Defaults are shown. - commit_author: - name: goreleaserbot - email: goreleaser@carlosbecker.com + # Allows you to add a custom require_relative at the top of the formula template + # Default is empty + custom_require: custom_download_strategy - # Folder inside the repository to put the formula. - # Default is the root folder. - folder: Formula + # Git author used to commit to the repository. + # Defaults are shown. + commit_author: + name: goreleaserbot + email: goreleaser@carlosbecker.com - # Caveats for the user of your binary. - # Default is empty. - caveats: "How to use this binary" + # Folder inside the repository to put the formula. + # Default is the root folder. + folder: Formula - # Your app's homepage. - # Default is empty. - homepage: "https://example.com/" + # Caveats for the user of your binary. + # Default is empty. + caveats: "How to use this binary" - # Your app's description. - # Default is empty. - description: "Software to create fast and easy drum rolls." + # Your app's homepage. + # Default is empty. + homepage: "https://example.com/" - # Setting this will prevent goreleaser to actually try to commit the updated - # formula - instead, the formula file will be stored on the dist folder only, - # leaving the responsibility of publishing it to the user. - # If set to auto, the release will not be uploaded to the homebrew tap - # in case there is an indicator for prerelease in the tag e.g. v1.0.0-rc1 - # Default is false. - skip_upload: true + # Your app's description. + # Default is empty. + description: "Software to create fast and easy drum rolls." - # Custom block for brew. - # Can be used to specify alternate downloads for devel or head releases. - # Default is empty. - custom_block: | - head "https://github.com/some/package.git" - ... + # Setting this will prevent goreleaser to actually try to commit the updated + # formula - instead, the formula file will be stored on the dist folder only, + # leaving the responsibility of publishing it to the user. + # If set to auto, the release will not be uploaded to the homebrew tap + # in case there is an indicator for prerelease in the tag e.g. v1.0.0-rc1 + # Default is false. + skip_upload: true - # Packages your package depends on. - dependencies: - - git - - zsh + # Custom block for brew. + # Can be used to specify alternate downloads for devel or head releases. + # Default is empty. + custom_block: | + head "https://github.com/some/package.git" + ... - # Packages that conflict with your package. - conflicts: - - svn - - bash + # Packages your package depends on. + dependencies: + - git + - zsh - # Specify for packages that run as a service. - # Default is empty. - plist: | - - ... + # Packages that conflict with your package. + conflicts: + - svn + - bash - # So you can `brew test` your formula. - # Default is empty. - test: | - system "#{bin}/program --version" - ... + # Specify for packages that run as a service. + # Default is empty. + plist: | + + ... - # Custom install script for brew. - # Default is 'bin.install "program"'. - install: | - bin.install "program" - ... + # So you can `brew test` your formula. + # Default is empty. + test: | + system "#{bin}/program --version" + ... + + # Custom install script for brew. + # Default is 'bin.install "program"'. + install: | + bin.install "program" + ... ``` > Learn more about the [name template engine](/templates). @@ -116,9 +123,15 @@ Assuming that the current tag is `v1.2.3`, the above configuration will generate class Program < Formula desc "How to use this binary" homepage "https://github.com/user/repo" - url "https://github.com/user/repo/releases/download/v1.2.3/program_v1.2.3_macOs_64bit.zip" version "v1.2.3" - sha256 "9ee30fc358fae8d248a2d7538957089885da321dca3f09e3296fe2058e7fff74" + + if os.Mac? + url "https://github.com/user/repo/releases/download/v1.2.3/program_v1.2.3_macOs_64bit.zip" + sha256 "9ee30fc358fae8d248a2d7538957089885da321dca3f09e3296fe2058e7fff74" + elsif os.Linux? + url "https://github.com/user/repo/releases/download/v1.2.3/program_v1.2.3_Linux_64bit.zip" + sha256 "b41bebd25fd7bb1a67dc2cd5ee12c9f67073094567fdf7b3871f05fd74a45fdd" + end depends_on "git" depends_on "zsh" @@ -129,7 +142,7 @@ class Program < Formula end ``` -**Important**: Note that GoReleaser does not yet generate a valid +**Important**: Note that GoReleaser does not generate a valid homebrew-core formula. The generated formulas are meant to be published as [homebrew taps](https://docs.brew.sh/Taps.html), and in their current form will not be accepted in any of the official homebrew repositories.