From 1a69d44d6d37021530b1d0f49d5e0fb1aa1f7299 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Wed, 2 Nov 2022 14:54:16 -0300 Subject: [PATCH] fix: allow to template scoop/brew/krew repo ref (#3521) closes #3508 - adds template support for krew and scoop repo refs - template branch on reporef on brew as well Signed-off-by: Carlos A Becker --- internal/client/config.go | 23 +++ internal/client/config_test.go | 48 +++++ internal/pipe/brew/brew.go | 10 +- internal/pipe/krew/krew.go | 5 + internal/pipe/scoop/scoop.go | 6 + internal/pipe/scoop/scoop_test.go | 306 ++++++++++++++++++----------- www/docs/customization/homebrew.md | 8 +- www/docs/customization/krew.md | 8 +- www/docs/customization/scoop.md | 6 +- 9 files changed, 294 insertions(+), 126 deletions(-) diff --git a/internal/client/config.go b/internal/client/config.go index f36981a6b5a..9ca9dd06b7c 100644 --- a/internal/client/config.go +++ b/internal/client/config.go @@ -4,6 +4,7 @@ import ( "github.com/goreleaser/goreleaser/pkg/config" ) +// RepoFromRef converts a config.RepoRef into a Repo. func RepoFromRef(ref config.RepoRef) Repo { return Repo{ Owner: ref.Owner, @@ -11,3 +12,25 @@ func RepoFromRef(ref config.RepoRef) Repo { Branch: ref.Branch, } } + +// TemplateRef templates a config.RepoFromRef +func TemplateRef(apply func(s string) (string, error), ref config.RepoRef) (config.RepoRef, error) { + name, err := apply(ref.Name) + if err != nil { + return ref, err + } + owner, err := apply(ref.Owner) + if err != nil { + return ref, err + } + branch, err := apply(ref.Branch) + if err != nil { + return ref, err + } + return config.RepoRef{ + Owner: owner, + Name: name, + Token: ref.Token, + Branch: branch, + }, nil +} diff --git a/internal/client/config_test.go b/internal/client/config_test.go index b661b050051..3b26c3ee479 100644 --- a/internal/client/config_test.go +++ b/internal/client/config_test.go @@ -1,6 +1,7 @@ package client import ( + "fmt" "testing" "github.com/goreleaser/goreleaser/pkg/config" @@ -25,3 +26,50 @@ func TestRepoFromRef(t *testing.T) { require.Equal(t, name, repo.Name) require.Equal(t, branch, repo.Branch) } + +func TestTemplateRef(t *testing.T) { + expected := config.RepoRef{ + Owner: "owner", + Name: "name", + Branch: "branch", + Token: "token", + } + t.Run("success", func(t *testing.T) { + ref, err := TemplateRef(func(s string) (string, error) { + if s == "token" { + return "", fmt.Errorf("nope") + } + return s, nil + }, expected) + require.NoError(t, err) + require.Equal(t, expected, ref) + }) + + t.Run("fail owner", func(t *testing.T) { + _, err := TemplateRef(func(s string) (string, error) { + if s == "token" || s == "owner" { + return "", fmt.Errorf("nope") + } + return s, nil + }, expected) + require.Error(t, err) + }) + t.Run("fail name", func(t *testing.T) { + _, err := TemplateRef(func(s string) (string, error) { + if s == "token" || s == "name" { + return "", fmt.Errorf("nope") + } + return s, nil + }, expected) + require.Error(t, err) + }) + t.Run("fail branch", func(t *testing.T) { + _, err := TemplateRef(func(s string) (string, error) { + if s == "token" || s == "branch" { + return "", fmt.Errorf("nope") + } + return s, nil + }, expected) + require.Error(t, err) + }) +} diff --git a/internal/pipe/brew/brew.go b/internal/pipe/brew/brew.go index 6a483525d89..85b497a585f 100644 --- a/internal/pipe/brew/brew.go +++ b/internal/pipe/brew/brew.go @@ -196,17 +196,11 @@ func doRun(ctx *context.Context, brew config.Homebrew, cl client.Client) error { } brew.Name = name - tapOwner, err := tmpl.New(ctx).Apply(brew.Tap.Owner) + ref, err := client.TemplateRef(tmpl.New(ctx).Apply, brew.Tap) if err != nil { return err } - brew.Tap.Owner = tapOwner - - tapName, err := tmpl.New(ctx).Apply(brew.Tap.Name) - if err != nil { - return err - } - brew.Tap.Name = tapName + brew.Tap = ref skipUpload, err := tmpl.New(ctx).Apply(brew.SkipUpload) if err != nil { diff --git a/internal/pipe/krew/krew.go b/internal/pipe/krew/krew.go index 71f22f96db9..5bab918eb0b 100644 --- a/internal/pipe/krew/krew.go +++ b/internal/pipe/krew/krew.go @@ -301,6 +301,11 @@ func doPublish(ctx *context.Context, manifest *artifact.Artifact, cl client.Clie return pipe.Skip("prerelease detected with 'auto' upload, skipping krew publish") } + ref, err := client.TemplateRef(tmpl.New(ctx).Apply, cfg.Index) + if err != nil { + return err + } + cfg.Index = ref repo := client.RepoFromRef(cfg.Index) gpath := buildManifestPath(manifestsFolder, manifest.Name) diff --git a/internal/pipe/scoop/scoop.go b/internal/pipe/scoop/scoop.go index 284d19aa467..912f3dd8f90 100644 --- a/internal/pipe/scoop/scoop.go +++ b/internal/pipe/scoop/scoop.go @@ -159,6 +159,12 @@ func doPublish(ctx *context.Context, cl client.Client) error { return err } + ref, err := client.TemplateRef(tmpl.New(ctx).Apply, scoop.Bucket) + if err != nil { + return err + } + scoop.Bucket = ref + repo := client.RepoFromRef(scoop.Bucket) return cl.CreateFile( ctx, diff --git a/internal/pipe/scoop/scoop_test.go b/internal/pipe/scoop/scoop_test.go index 396bb9567e8..87d1eed95cd 100644 --- a/internal/pipe/scoop/scoop_test.go +++ b/internal/pipe/scoop/scoop_test.go @@ -39,25 +39,25 @@ func Test_doRun(t *testing.T) { require.NoError(t, os.WriteFile(file, []byte("lorem ipsum"), 0o644)) type args struct { - ctx *context.Context + ctx func() *context.Context client *client.Mock } - type asserter func(*testing.T, args) - type errChecker func(*testing.T, error) + type asserter func(testing.TB, args) + type errChecker func(testing.TB, error) shouldErr := func(msg string) errChecker { - return func(t *testing.T, err error) { - t.Helper() - require.Error(t, err) - require.EqualError(t, err, msg) + return func(tb testing.TB, err error) { + tb.Helper() + require.Error(tb, err) + require.EqualError(tb, err, msg) } } - noAssertions := func(t *testing.T, _ args) { - t.Helper() + noAssertions := func(tb testing.TB, _ args) { + tb.Helper() } - shouldNotErr := func(t *testing.T, err error) { - t.Helper() - require.NoError(t, err) + shouldNotErr := func(tb testing.TB, err error) { + tb.Helper() + require.NoError(tb, err) } tests := []struct { @@ -71,13 +71,8 @@ func Test_doRun(t *testing.T) { { "valid public github", args{ - &context.Context{ - TokenType: context.TokenTypeGitHub, - Git: context.GitInfo{ - CurrentTag: "v1.0.1", - }, - Version: "1.0.1", - Config: config.Project{ + func() *context.Context { + ctx := context.New(config.Project{ ProjectName: "run-pipe", Scoop: config.Scoop{ Bucket: config.RepoRef{ @@ -88,7 +83,13 @@ func Test_doRun(t *testing.T) { Description: "A run pipe test formula", Homepage: "https://github.com/goreleaser", }, - }, + }) + ctx.TokenType = context.TokenTypeGitHub + ctx.Git = context.GitInfo{ + CurrentTag: "v1.0.1", + } + ctx.Version = "1.0.1" + return ctx }, client.NewMock(), }, @@ -98,21 +99,16 @@ func Test_doRun(t *testing.T) { }, shouldNotErr, shouldNotErr, - func(t *testing.T, a args) { - t.Helper() - require.Equal(t, "scoops/run-pipe.json", a.client.Path) + func(tb testing.TB, a args) { + tb.Helper() + require.Equal(tb, "scoops/run-pipe.json", a.client.Path) }, }, { "wrap in directory", args{ - &context.Context{ - TokenType: context.TokenTypeGitHub, - Git: context.GitInfo{ - CurrentTag: "v1.0.1", - }, - Version: "1.0.1", - Config: config.Project{ + func() *context.Context { + ctx := context.New(config.Project{ ProjectName: "run-pipe", Scoop: config.Scoop{ Bucket: config.RepoRef{ @@ -122,7 +118,13 @@ func Test_doRun(t *testing.T) { Description: "A run pipe test formula", Homepage: "https://github.com/goreleaser", }, - }, + }) + ctx.TokenType = context.TokenTypeGitHub + ctx.Git = context.GitInfo{ + CurrentTag: "v1.0.1", + } + ctx.Version = "1.0.1" + return ctx }, client.NewMock(), }, @@ -154,13 +156,8 @@ func Test_doRun(t *testing.T) { { "valid enterprise github", args{ - &context.Context{ - TokenType: context.TokenTypeGitHub, - Git: context.GitInfo{ - CurrentTag: "v1.0.1", - }, - Version: "1.0.1", - Config: config.Project{ + func() *context.Context { + ctx := context.New(config.Project{ GitHubURLs: config.GitHubURLs{Download: "https://api.custom.github.enterprise.com"}, ProjectName: "run-pipe", Scoop: config.Scoop{ @@ -171,7 +168,13 @@ func Test_doRun(t *testing.T) { Description: "A run pipe test formula", Homepage: "https://github.com/goreleaser", }, - }, + }) + ctx.TokenType = context.TokenTypeGitHub + ctx.Git = context.GitInfo{ + CurrentTag: "v1.0.1", + } + ctx.Version = "1.0.1" + return ctx }, client.NewMock(), }, @@ -181,21 +184,16 @@ func Test_doRun(t *testing.T) { }, shouldNotErr, shouldNotErr, - func(t *testing.T, a args) { - t.Helper() - require.Equal(t, "run-pipe.json", a.client.Path) + func(tb testing.TB, a args) { + tb.Helper() + require.Equal(tb, "run-pipe.json", a.client.Path) }, }, { "valid public gitlab", args{ - &context.Context{ - TokenType: context.TokenTypeGitLab, - Git: context.GitInfo{ - CurrentTag: "v1.0.1", - }, - Version: "1.0.1", - Config: config.Project{ + func() *context.Context { + ctx := context.New(config.Project{ ProjectName: "run-pipe", Scoop: config.Scoop{ Bucket: config.RepoRef{ @@ -205,7 +203,13 @@ func Test_doRun(t *testing.T) { Description: "A run pipe test formula", Homepage: "https://gitlab.com/goreleaser", }, - }, + }) + ctx.TokenType = context.TokenTypeGitLab + ctx.Git = context.GitInfo{ + CurrentTag: "v1.0.1", + } + ctx.Version = "1.0.1" + return ctx }, client.NewMock(), }, @@ -231,13 +235,8 @@ func Test_doRun(t *testing.T) { { "valid enterprise gitlab", args{ - &context.Context{ - TokenType: context.TokenTypeGitLab, - Git: context.GitInfo{ - CurrentTag: "v1.0.1", - }, - Version: "1.0.1", - Config: config.Project{ + func() *context.Context { + ctx := context.New(config.Project{ GitHubURLs: config.GitHubURLs{Download: "https://api.custom.gitlab.enterprise.com"}, ProjectName: "run-pipe", Scoop: config.Scoop{ @@ -248,7 +247,13 @@ func Test_doRun(t *testing.T) { Description: "A run pipe test formula", Homepage: "https://gitlab.com/goreleaser", }, - }, + }) + ctx.TokenType = context.TokenTypeGitLab + ctx.Git = context.GitInfo{ + CurrentTag: "v1.0.1", + } + ctx.Version = "1.0.1" + return ctx }, client.NewMock(), }, @@ -274,13 +279,8 @@ func Test_doRun(t *testing.T) { { "no windows build", args{ - &context.Context{ - TokenType: context.TokenTypeGitHub, - Git: context.GitInfo{ - CurrentTag: "v1.0.1", - }, - Version: "1.0.1", - Config: config.Project{ + func() *context.Context { + ctx := context.New(config.Project{ ProjectName: "run-pipe", Scoop: config.Scoop{ Bucket: config.RepoRef{ @@ -290,7 +290,13 @@ func Test_doRun(t *testing.T) { Description: "A run pipe test formula", Homepage: "https://github.com/goreleaser", }, - }, + }) + ctx.TokenType = context.TokenTypeGitHub + ctx.Git = context.GitInfo{ + CurrentTag: "v1.0.1", + } + ctx.Version = "1.0.1" + return ctx }, client.NewMock(), }, @@ -302,13 +308,8 @@ func Test_doRun(t *testing.T) { { "is draft", args{ - &context.Context{ - TokenType: context.TokenTypeGitHub, - Git: context.GitInfo{ - CurrentTag: "v1.0.1", - }, - Version: "1.0.1", - Config: config.Project{ + func() *context.Context { + ctx := context.New(config.Project{ ProjectName: "run-pipe", Release: config.Release{ Draft: true, @@ -321,7 +322,13 @@ func Test_doRun(t *testing.T) { Description: "A run pipe test formula", Homepage: "https://github.com/goreleaser", }, - }, + }) + ctx.TokenType = context.TokenTypeGitHub + ctx.Git = context.GitInfo{ + CurrentTag: "v1.0.1", + } + ctx.Version = "1.0.1" + return ctx }, client.NewMock(), }, @@ -336,19 +343,8 @@ func Test_doRun(t *testing.T) { { "is prerelease and skip upload set to auto", args{ - &context.Context{ - TokenType: context.TokenTypeGitHub, - Git: context.GitInfo{ - CurrentTag: "v1.0.1-pre.1", - }, - Semver: context.Semver{ - Major: 1, - Minor: 0, - Patch: 1, - Prerelease: "-pre.1", - }, - Version: "1.0.1-pre.1", - Config: config.Project{ + func() *context.Context { + ctx := context.New(config.Project{ ProjectName: "run-pipe", Scoop: config.Scoop{ SkipUpload: "auto", @@ -359,7 +355,19 @@ func Test_doRun(t *testing.T) { Description: "A run pipe test formula", Homepage: "https://github.com/goreleaser", }, - }, + }) + ctx.TokenType = context.TokenTypeGitHub + ctx.Git = context.GitInfo{ + CurrentTag: "v1.0.1-pre.1", + } + ctx.Semver = context.Semver{ + Major: 1, + Minor: 0, + Patch: 1, + Prerelease: "-pre.1", + } + ctx.Version = "1.0.1-pre.1" + return ctx }, client.NewMock(), }, @@ -374,13 +382,8 @@ func Test_doRun(t *testing.T) { { "skip upload set to true", args{ - &context.Context{ - TokenType: context.TokenTypeGitHub, - Git: context.GitInfo{ - CurrentTag: "v1.0.1", - }, - Version: "1.0.1", - Config: config.Project{ + func() *context.Context { + ctx := context.New(config.Project{ ProjectName: "run-pipe", Scoop: config.Scoop{ SkipUpload: "true", @@ -391,7 +394,13 @@ func Test_doRun(t *testing.T) { Description: "A run pipe test formula", Homepage: "https://github.com/goreleaser", }, - }, + }) + ctx.TokenType = context.TokenTypeGitHub + ctx.Git = context.GitInfo{ + CurrentTag: "v1.0.1", + } + ctx.Version = "1.0.1" + return ctx }, client.NewMock(), }, @@ -406,13 +415,8 @@ func Test_doRun(t *testing.T) { { "release is disabled", args{ - &context.Context{ - TokenType: context.TokenTypeGitHub, - Git: context.GitInfo{ - CurrentTag: "v1.0.1", - }, - Version: "1.0.1", - Config: config.Project{ + func() *context.Context { + ctx := context.New(config.Project{ ProjectName: "run-pipe", Release: config.Release{ Disable: true, @@ -425,7 +429,13 @@ func Test_doRun(t *testing.T) { Description: "A run pipe test formula", Homepage: "https://github.com/goreleaser", }, - }, + }) + ctx.TokenType = context.TokenTypeGitHub + ctx.Git = context.GitInfo{ + CurrentTag: "v1.0.1", + } + ctx.Version = "1.0.1" + return ctx }, client.NewMock(), }, @@ -440,13 +450,8 @@ func Test_doRun(t *testing.T) { { "no archive", args{ - &context.Context{ - TokenType: context.TokenTypeGitHub, - Git: context.GitInfo{ - CurrentTag: "v1.0.1", - }, - Version: "1.0.1", - Config: config.Project{ + func() *context.Context { + ctx := context.New(config.Project{ ProjectName: "run-pipe", Scoop: config.Scoop{ Bucket: config.RepoRef{ @@ -456,7 +461,13 @@ func Test_doRun(t *testing.T) { Description: "A run pipe test formula", Homepage: "https://github.com/goreleaser", }, - }, + }) + ctx.TokenType = context.TokenTypeGitHub + ctx.Git = context.GitInfo{ + CurrentTag: "v1.0.1", + } + ctx.Version = "1.0.1" + return ctx }, client.NewMock(), }, @@ -465,10 +476,79 @@ func Test_doRun(t *testing.T) { shouldNotErr, noAssertions, }, + { + "invalid ref tmpl", + args{ + func() *context.Context { + ctx := context.New(config.Project{ + ProjectName: "run-pipe", + Scoop: config.Scoop{ + Bucket: config.RepoRef{ + Owner: "{{ .Env.aaaaaa }}", + Name: "test", + }, + Folder: "scoops", + Description: "A run pipe test formula", + Homepage: "https://github.com/goreleaser", + }, + }) + ctx.TokenType = context.TokenTypeGitHub + ctx.Git = context.GitInfo{ + CurrentTag: "v1.0.1", + } + ctx.Version = "1.0.1" + return ctx + }, + client.NewMock(), + }, + []artifact.Artifact{ + {Name: "foo_1.0.1-pre.1_windows_amd64.tar.gz", Goos: "windows", Goarch: "amd64", Goamd64: "v1", Path: file}, + }, + shouldNotErr, + testlib.RequireTemplateError, + noAssertions, + }, + { + "ref templ", + args{ + func() *context.Context { + ctx := context.New(config.Project{ + Env: []string{"FOO=test", "BRANCH=main"}, + ProjectName: "run-pipe", + Scoop: config.Scoop{ + Bucket: config.RepoRef{ + Owner: "{{ .Env.FOO }}", + Name: "{{ .Env.FOO }}", + Branch: "{{ .Env.BRANCH }}", + }, + Folder: "scoops", + Description: "A run pipe test formula", + Homepage: "https://github.com/goreleaser", + }, + }) + ctx.TokenType = context.TokenTypeGitHub + ctx.Git = context.GitInfo{ + CurrentTag: "v1.0.1", + } + ctx.Version = "1.0.1" + return ctx + }, + client.NewMock(), + }, + []artifact.Artifact{ + {Name: "foo_1.0.1_windows_amd64.tar.gz", Goos: "windows", Goarch: "amd64", Goamd64: "v1", Path: file}, + }, + shouldNotErr, + shouldNotErr, + func(tb testing.TB, a args) { + tb.Helper() + require.Equal(tb, "scoops/run-pipe.json", a.client.Path) + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx := tt.args.ctx + ctx := tt.args.ctx() ctx.Artifacts = artifact.New() for _, a := range tt.artifacts { a.Type = artifact.UploadableArchive diff --git a/www/docs/customization/homebrew.md b/www/docs/customization/homebrew.md index 388fc19e720..b25e44c1d91 100644 --- a/www/docs/customization/homebrew.md +++ b/www/docs/customization/homebrew.md @@ -42,10 +42,14 @@ brews: # GitHub/GitLab repository to push the formula to tap: - owner: repo-owner + # Repository owner template. (templateable) + owner: user + + # Repository name. (templateable) name: homebrew-tap - # Optionally a branch can be provided. + # Optionally a branch can be provided. (templateable) + # # Defaults to the default repository branch. branch: main diff --git a/www/docs/customization/krew.md b/www/docs/customization/krew.md index 9efe07ac76c..b982496dbbb 100644 --- a/www/docs/customization/krew.md +++ b/www/docs/customization/krew.md @@ -39,10 +39,14 @@ krews: # GitHub/GitLab repository to push the Krew plugin to # Gitea is not supported yet, but the support coming index: - owner: repo-owner + # Repository owner template. (templateable) + owner: user + + # Repository name. (templateable) name: krew-plugins - # Optionally a branch can be provided. + # Optionally a branch can be provided. (templateable) + # # Defaults to the default repository branch. branch: main diff --git a/www/docs/customization/scoop.md b/www/docs/customization/scoop.md index b84d3db2d8c..62a141c3349 100644 --- a/www/docs/customization/scoop.md +++ b/www/docs/customization/scoop.md @@ -17,10 +17,14 @@ scoop: # Repository to push the app manifest to. bucket: + # Repository owner template. (templateable) owner: user + + # Repository name. (templateable) name: scoop-bucket - # Optionally a branch can be provided. + # Optionally a branch can be provided. (templateable) + # # Defaults to the default repository branch. branch: main