From d2a63c1093dc0f3f221651a4ea45a90aba54cd00 Mon Sep 17 00:00:00 2001 From: Engin Diri Date: Wed, 29 Sep 2021 01:16:39 +0200 Subject: [PATCH] feat: publish: GoFish integration (#2509) Signed-off-by: Engin Diri --- internal/artifact/artifact.go | 4 + internal/artifact/artifact_test.go | 1 + internal/golden/golden.go | 5 + internal/pipe/gofish/doc.go | 3 + internal/pipe/gofish/food_template.go | 49 + internal/pipe/gofish/gofish.go | 302 ++++++ internal/pipe/gofish/gofish_test.go | 898 ++++++++++++++++++ .../gofish/testdata/TestFullFood.lua.golden | 89 ++ .../testdata/TestFullFoodLinuxOnly.lua.golden | 51 + .../TestFullFoodWindowsOnly.lua.golden | 24 + .../testdata/TestFullPipe/default.lua.golden | 25 + .../TestFullPipe/default_gitlab.lua.golden | 25 + .../multiple_armv5.lua.golden | 51 + .../multiple_armv6.lua.golden | 51 + .../multiple_armv7.lua.golden | 51 + .../TestRunPipeNameTemplate.lua.golden | 25 + internal/pipe/publish/publish.go | 3 + internal/pipeline/pipeline.go | 2 + pkg/config/config.go | 16 + pkg/defaults/defaults.go | 2 + www/docs/customization/gofish.md | 120 +++ www/htmltest.yml | 1 + www/mkdocs.yml | 1 + 23 files changed, 1799 insertions(+) create mode 100644 internal/pipe/gofish/doc.go create mode 100644 internal/pipe/gofish/food_template.go create mode 100644 internal/pipe/gofish/gofish.go create mode 100644 internal/pipe/gofish/gofish_test.go create mode 100644 internal/pipe/gofish/testdata/TestFullFood.lua.golden create mode 100644 internal/pipe/gofish/testdata/TestFullFoodLinuxOnly.lua.golden create mode 100644 internal/pipe/gofish/testdata/TestFullFoodWindowsOnly.lua.golden create mode 100644 internal/pipe/gofish/testdata/TestFullPipe/default.lua.golden create mode 100644 internal/pipe/gofish/testdata/TestFullPipe/default_gitlab.lua.golden create mode 100644 internal/pipe/gofish/testdata/TestRunPipeForMultipleArmVersions/multiple_armv5.lua.golden create mode 100644 internal/pipe/gofish/testdata/TestRunPipeForMultipleArmVersions/multiple_armv6.lua.golden create mode 100644 internal/pipe/gofish/testdata/TestRunPipeForMultipleArmVersions/multiple_armv7.lua.golden create mode 100644 internal/pipe/gofish/testdata/TestRunPipeNameTemplate.lua.golden create mode 100644 www/docs/customization/gofish.md diff --git a/internal/artifact/artifact.go b/internal/artifact/artifact.go index ff9df69cc..3bbed3b28 100644 --- a/internal/artifact/artifact.go +++ b/internal/artifact/artifact.go @@ -50,6 +50,8 @@ const ( UploadableSourceArchive // BrewTap is an uploadable homebrew tap recipe file. BrewTap + // GoFishRig is an uploadable Rigs rig food file. + GoFishRig // ScoopManifest is an uploadable scoop manifest file. ScoopManifest ) @@ -78,6 +80,8 @@ func (t Type) String() string { return "Source" case BrewTap: return "Brew Tap" + case GoFishRig: + return "GoFish Rig" case ScoopManifest: return "Scoop Manifest" default: diff --git a/internal/artifact/artifact_test.go b/internal/artifact/artifact_test.go index 6d2262266..94b0c4c07 100644 --- a/internal/artifact/artifact_test.go +++ b/internal/artifact/artifact_test.go @@ -307,6 +307,7 @@ func TestTypeToString(t *testing.T) { Signature, UploadableSourceArchive, BrewTap, + GoFishRig, ScoopManifest, } { t.Run(a.String(), func(t *testing.T) { diff --git a/internal/golden/golden.go b/internal/golden/golden.go index 9598959f0..0c27dd76f 100644 --- a/internal/golden/golden.go +++ b/internal/golden/golden.go @@ -32,6 +32,11 @@ func RequireEqualRb(tb testing.TB, out []byte) { doRequireEqual(tb, out, ".rb") } +func RequireEqualLua(tb testing.TB, out []byte) { + tb.Helper() + doRequireEqual(tb, out, ".lua") +} + func RequireEqualYaml(tb testing.TB, out []byte) { tb.Helper() doRequireEqual(tb, out, ".yml") diff --git a/internal/pipe/gofish/doc.go b/internal/pipe/gofish/doc.go new file mode 100644 index 000000000..bddef2562 --- /dev/null +++ b/internal/pipe/gofish/doc.go @@ -0,0 +1,3 @@ +// Package gofish implements the Pipe, providing food generation and +// uploading it to a configured repo. +package gofish diff --git a/internal/pipe/gofish/food_template.go b/internal/pipe/gofish/food_template.go new file mode 100644 index 000000000..a6aca633a --- /dev/null +++ b/internal/pipe/gofish/food_template.go @@ -0,0 +1,49 @@ +package gofish + +type templateData struct { + Name string + Desc string + Homepage string + Version string + License string + ReleasePackages []releasePackage +} + +type releasePackage struct { + DownloadURL string + SHA256 string + OS string + Arch string +} + +const foodTemplate = `local name = "{{ .Name }}" +local version = "{{ .Version }}" + +food = { + name = name, + description = "{{ .Desc }}", + license = "{{ .License }}", + homepage = "{{ .Homepage }}", + version = version, + packages = { + {{- range $element := .ReleasePackages}} + {{- if ne $element.OS ""}} + { + os = "{{ $element.OS }}", + arch = "{{ $element.Arch }}", + url = "{{ $element.DownloadURL }}", + sha256 = "{{ $element.SHA256 }}", + resources = { + { + path = {{if ne $element.OS "windows"}}name{{else}}name .. ".exe"{{end}}, + installpath = {{if ne $element.OS "windows"}}"bin/" .. name,{{else}}"bin\\" .. name .. ".exe"{{end}} + {{- if ne $element.OS "windows"}} + executable = true + {{- end }} + } + } + }, + {{- end }} + {{- end}} + } +}` diff --git a/internal/pipe/gofish/gofish.go b/internal/pipe/gofish/gofish.go new file mode 100644 index 000000000..1995eeb75 --- /dev/null +++ b/internal/pipe/gofish/gofish.go @@ -0,0 +1,302 @@ +package gofish + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "os" + "path" + "path/filepath" + "strings" + "text/template" + + "github.com/apex/log" + "github.com/goreleaser/goreleaser/internal/artifact" + "github.com/goreleaser/goreleaser/internal/client" + "github.com/goreleaser/goreleaser/internal/pipe" + "github.com/goreleaser/goreleaser/internal/tmpl" + "github.com/goreleaser/goreleaser/pkg/config" + "github.com/goreleaser/goreleaser/pkg/context" +) + +const goFishConfigExtra = "GoFishConfig" + +const foodFolder = "Food" + +var ErrNoArchivesFound = errors.New("no linux/macos/windows archives found") + +var ErrMultipleArchivesSameOS = errors.New("one rig can handle only archive of an OS/Arch combination. Consider using ids in the gofish section") + +// ErrTokenTypeNotImplementedForGoFish indicates that a new token type was not implemented for this pipe. +type ErrTokenTypeNotImplementedForGoFish struct { + TokenType context.TokenType +} + +func (e ErrTokenTypeNotImplementedForGoFish) Error() string { + return fmt.Sprintf("token type %q not implemented for gofish pipe", e.TokenType) +} + +// Pipe for goFish deployment. +type Pipe struct{} + +func (Pipe) String() string { return "gofish fish food cookbook" } +func (Pipe) Skip(ctx *context.Context) bool { return len(ctx.Config.Rigs) == 0 } + +func (Pipe) Default(ctx *context.Context) error { + for i := range ctx.Config.Rigs { + goFish := &ctx.Config.Rigs[i] + + if goFish.CommitAuthor.Name == "" { + goFish.CommitAuthor.Name = "goreleaserbot" + } + if goFish.CommitAuthor.Email == "" { + goFish.CommitAuthor.Email = "goreleaser@carlosbecker.com" + } + if goFish.CommitMessageTemplate == "" { + goFish.CommitMessageTemplate = "GoFish fish food update for {{ .ProjectName }} version {{ .Tag }}" + } + if goFish.Name == "" { + goFish.Name = ctx.Config.ProjectName + } + if goFish.Goarm == "" { + goFish.Goarm = "6" + } + } + + return nil +} + +func (Pipe) Run(ctx *context.Context) error { + cli, err := client.New(ctx) + if err != nil { + return err + } + + return runAll(ctx, cli) +} + +func runAll(ctx *context.Context, cli client.Client) error { + for _, goFish := range ctx.Config.Rigs { + err := doRun(ctx, goFish, cli) + if err != nil { + return err + } + } + return nil +} + +func doRun(ctx *context.Context, goFish config.GoFish, cl client.Client) error { + if goFish.Rig.Name == "" { + return pipe.Skip("Rigs rig name is not set") + } + + filters := []artifact.Filter{ + artifact.Or( + artifact.ByGoos("darwin"), + artifact.ByGoos("linux"), + artifact.ByGoos("windows"), + ), + artifact.Or( + artifact.ByGoarch("amd64"), + artifact.ByGoarch("arm64"), + artifact.And( + artifact.ByGoarch("arm"), + artifact.ByGoarm(goFish.Goarm), + ), + ), + artifact.ByType(artifact.UploadableArchive), + } + if len(goFish.IDs) > 0 { + filters = append(filters, artifact.ByIDs(goFish.IDs...)) + } + + archives := ctx.Artifacts.Filter(artifact.And(filters...)).List() + if len(archives) == 0 { + return ErrNoArchivesFound + } + + name, err := tmpl.New(ctx).Apply(goFish.Name) + if err != nil { + return err + } + goFish.Name = name + + content, err := buildFood(ctx, goFish, cl, archives) + if err != nil { + return err + } + + filename := goFish.Name + ".lua" + path := filepath.Join(ctx.Config.Dist, filename) + log.WithField("food", path).Info("writing") + if err := os.WriteFile(path, []byte(content), 0o644); err != nil { //nolint: gosec + return fmt.Errorf("failed to write gofish food: %w", err) + } + + ctx.Artifacts.Add(&artifact.Artifact{ + Name: filename, + Path: path, + Type: artifact.GoFishRig, + Extra: map[string]interface{}{ + goFishConfigExtra: goFish, + }, + }) + + return nil +} + +func buildFood(ctx *context.Context, goFish config.GoFish, client client.Client, artifacts []*artifact.Artifact) (string, error) { + data, err := dataFor(ctx, goFish, client, artifacts) + if err != nil { + return "", err + } + return doBuildFood(ctx, data) +} + +func doBuildFood(ctx *context.Context, data templateData) (string, error) { + t, err := template. + New(data.Name). + Parse(foodTemplate) + if err != nil { + return "", err + } + var out bytes.Buffer + if err := t.Execute(&out, data); err != nil { + return "", err + } + + content, err := tmpl.New(ctx).Apply(out.String()) + if err != nil { + return "", err + } + out.Reset() + + // Sanitize the template output and get rid of trailing whitespace. + var ( + r = strings.NewReader(content) + s = bufio.NewScanner(r) + ) + for s.Scan() { + l := strings.TrimRight(s.Text(), " ") + _, _ = out.WriteString(l) + _ = out.WriteByte('\n') + } + if err := s.Err(); err != nil { + return "", err + } + + return out.String(), nil +} + +func dataFor(ctx *context.Context, cfg config.GoFish, cl client.Client, artifacts []*artifact.Artifact) (templateData, error) { + result := templateData{ + Name: cfg.Name, + Desc: cfg.Description, + Homepage: cfg.Homepage, + Version: ctx.Version, + License: cfg.License, + } + + for _, artifact := range artifacts { + sum, err := artifact.Checksum("sha256") + if err != nil { + return result, err + } + + if cfg.URLTemplate == "" { + url, err := cl.ReleaseURLTemplate(ctx) + if err != nil { + if client.IsNotImplementedErr(err) { + return result, ErrTokenTypeNotImplementedForGoFish{ctx.TokenType} + } + return result, err + } + cfg.URLTemplate = url + } + url, err := tmpl.New(ctx).WithArtifact(artifact, map[string]string{}).Apply(cfg.URLTemplate) + if err != nil { + return result, err + } + releasePackage := releasePackage{ + DownloadURL: url, + SHA256: sum, + OS: artifact.Goos, + Arch: artifact.Goarch, + } + for _, v := range result.ReleasePackages { + if v.OS == artifact.Goos && v.Arch == artifact.Goarch { + return result, ErrMultipleArchivesSameOS + } + } + result.ReleasePackages = append(result.ReleasePackages, releasePackage) + } + + return result, nil +} + +// Publish gofish rig. +func (Pipe) Publish(ctx *context.Context) error { + cli, err := client.New(ctx) + if err != nil { + return err + } + return publishAll(ctx, cli) +} + +func publishAll(ctx *context.Context, cli client.Client) error { + // even if one of them skips, we run them all, and then show return the skips all at once. + // this is needed so we actually create the `dist/foo.lua` file, which is useful for debugging. + skips := pipe.SkipMemento{} + for _, rig := range ctx.Artifacts.Filter(artifact.ByType(artifact.GoFishRig)).List() { + err := doPublish(ctx, rig, cli) + if err != nil && pipe.IsSkip(err) { + skips.Remember(err) + continue + } + if err != nil { + return err + } + } + return skips.Evaluate() +} + +func doPublish(ctx *context.Context, food *artifact.Artifact, cl client.Client) error { + rig := food.Extra[goFishConfigExtra].(config.GoFish) + var err error + cl, err = client.NewIfToken(ctx, cl, rig.Rig.Token) + if err != nil { + return err + } + + if strings.TrimSpace(rig.SkipUpload) == "true" { + return pipe.Skip("rig.skip_upload is set") + } + + if strings.TrimSpace(rig.SkipUpload) == "auto" && ctx.Semver.Prerelease != "" { + return pipe.Skip("prerelease detected with 'auto' upload, skipping gofish publish") + } + + repo := client.RepoFromRef(rig.Rig) + + gpath := buildFoodPath(foodFolder, food.Name) + log.WithField("food", gpath). + WithField("repo", repo.String()). + Info("pushing") + + msg, err := tmpl.New(ctx).Apply(rig.CommitMessageTemplate) + if err != nil { + return err + } + + content, err := os.ReadFile(food.Path) + if err != nil { + return err + } + + return cl.CreateFile(ctx, rig.CommitAuthor, repo, content, gpath, msg) +} + +func buildFoodPath(folder, filename string) string { + return path.Join(folder, filename) +} diff --git a/internal/pipe/gofish/gofish_test.go b/internal/pipe/gofish/gofish_test.go new file mode 100644 index 000000000..5daa11783 --- /dev/null +++ b/internal/pipe/gofish/gofish_test.go @@ -0,0 +1,898 @@ +package gofish + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/goreleaser/goreleaser/internal/artifact" + "github.com/goreleaser/goreleaser/internal/client" + "github.com/goreleaser/goreleaser/internal/golden" + "github.com/goreleaser/goreleaser/internal/testlib" + "github.com/goreleaser/goreleaser/pkg/config" + "github.com/goreleaser/goreleaser/pkg/context" + "github.com/stretchr/testify/require" +) + +func TestDescription(t *testing.T) { + require.NotEmpty(t, Pipe{}.String()) +} + +func createTemplateData() templateData { + return templateData{ + Desc: "Some desc", + Homepage: "https://google.com", + ReleasePackages: []releasePackage{ + { + Arch: "amd64", + OS: "darwin", + DownloadURL: "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Darwin_x86_64.tar.gz", + SHA256: "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c68", + }, + { + Arch: "arm64", + OS: "darwin", + DownloadURL: "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Darwin_arm64.tar.gz", + SHA256: "1633f61598ab0791e213135923624eb342196b349490sadasdsadsadasdasdsd", + }, + { + Arch: "amd64", + OS: "linux", + DownloadURL: "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Linux_x86_64.tar.gz", + SHA256: "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c67", + }, + { + Arch: "arm", + OS: "linux", + DownloadURL: "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Arm6.tar.gz", + SHA256: "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c67", + }, + { + Arch: "arm64", + OS: "linux", + DownloadURL: "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Arm64.tar.gz", + SHA256: "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c67", + }, + { + Arch: "amd64", + OS: "windows", + DownloadURL: "https://github.com/caarlos0/test/releases/download/v0.1.3/test_windows_amd64.zip", + SHA256: "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c67", + }, + }, + Name: "Test", + Version: "0.1.3", + } +} + +func assertDefaultTemplateData(t *testing.T, food string) { + t.Helper() + require.Contains(t, food, "food =") + require.Contains(t, food, `homepage = "https://google.com"`) + require.Contains(t, food, `url = "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Darwin_x86_64.tar.gz"`) + require.Contains(t, food, `sha256 = "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c68"`) + require.Contains(t, food, `local version = "0.1.3"`) +} + +func TestFullFood(t *testing.T) { + data := createTemplateData() + data.License = "MIT" + food, err := doBuildFood(context.New(config.Project{ + ProjectName: "foo", + }), data) + require.NoError(t, err) + + golden.RequireEqualLua(t, []byte(food)) +} + +func TestFullFoodLinuxOnly(t *testing.T) { + data := createTemplateData() + for i, v := range data.ReleasePackages { + if v.OS != "linux" { + data.ReleasePackages[i] = releasePackage{} + } + } + + formulae, err := doBuildFood(context.New(config.Project{ + ProjectName: "foo", + }), data) + require.NoError(t, err) + + golden.RequireEqualLua(t, []byte(formulae)) +} + +func TestFullFoodWindowsOnly(t *testing.T) { + data := createTemplateData() + for i, v := range data.ReleasePackages { + if v.OS != "windows" { + data.ReleasePackages[i] = releasePackage{} + } + } + formulae, err := doBuildFood(context.New(config.Project{ + ProjectName: "foo", + }), data) + require.NoError(t, err) + + golden.RequireEqualLua(t, []byte(formulae)) +} + +func TestFormulaeSimple(t *testing.T) { + formulae, err := doBuildFood(context.New(config.Project{}), createTemplateData()) + require.NoError(t, err) + assertDefaultTemplateData(t, formulae) + require.NotContains(t, formulae, "def caveats") + require.NotContains(t, formulae, "def plist;") +} + +func TestFullPipe(t *testing.T) { + type testcase struct { + prepare func(ctx *context.Context) + expectedPublishError string + } + for name, tt := range map[string]testcase{ + "default": { + prepare: func(ctx *context.Context) { + ctx.TokenType = context.TokenTypeGitHub + ctx.Config.Rigs[0].Rig.Owner = "test" + ctx.Config.Rigs[0].Rig.Name = "test" + ctx.Config.Rigs[0].Homepage = "https://github.com/goreleaser" + }, + }, + "default_gitlab": { + prepare: func(ctx *context.Context) { + ctx.TokenType = context.TokenTypeGitLab + ctx.Config.Rigs[0].Rig.Owner = "test" + ctx.Config.Rigs[0].Rig.Name = "test" + ctx.Config.Rigs[0].Homepage = "https://gitlab.com/goreleaser" + }, + }, + "invalid_commit_template": { + prepare: func(ctx *context.Context) { + ctx.Config.Rigs[0].Rig.Owner = "test" + ctx.Config.Rigs[0].Rig.Name = "test" + ctx.Config.Rigs[0].CommitMessageTemplate = "{{ .Asdsa }" + }, + expectedPublishError: `template: tmpl:1: unexpected "}" in operand`, + }, + } { + t.Run(name, func(t *testing.T) { + folder := t.TempDir() + ctx := &context.Context{ + Git: context.GitInfo{ + CurrentTag: "v1.0.1", + }, + Version: "1.0.1", + Artifacts: artifact.New(), + Env: map[string]string{ + "FOO": "foo_is_bar", + }, + Config: config.Project{ + Dist: folder, + ProjectName: name, + Rigs: []config.GoFish{ + { + Name: name, + IDs: []string{ + "foo", + }, + Description: "A run pipe test formula and FOO={{ .Env.FOO }}", + }, + }, + }, + } + tt.prepare(ctx) + ctx.Artifacts.Add(&artifact.Artifact{ + Name: "bar_bin.tar.gz", + Path: "doesnt matter", + Goos: "darwin", + Goarch: "amd64", + Type: artifact.UploadableArchive, + Extra: map[string]interface{}{ + "ID": "bar", + "Format": "tar.gz", + }, + }) + 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", + }, + }) + + f, err := os.Create(path) + require.NoError(t, err) + require.NoError(t, f.Close()) + client := &DummyClient{} + distFile := filepath.Join(folder, name+".lua") + + require.NoError(t, runAll(ctx, client)) + if tt.expectedPublishError != "" { + require.EqualError(t, publishAll(ctx, client), tt.expectedPublishError) + return + } + + require.NoError(t, publishAll(ctx, client)) + require.True(t, client.CreatedFile) + golden.RequireEqualLua(t, []byte(client.Content)) + + distBts, err := os.ReadFile(distFile) + require.NoError(t, err) + require.Equal(t, client.Content, string(distBts)) + }) + } +} + +func TestRunPipeNameTemplate(t *testing.T) { + folder := t.TempDir() + ctx := &context.Context{ + Git: context.GitInfo{ + CurrentTag: "v1.0.1", + }, + Version: "1.0.1", + Artifacts: artifact.New(), + Env: map[string]string{ + "FOO_BAR": "is_bar", + }, + Config: config.Project{ + Dist: folder, + ProjectName: "foo", + Rigs: []config.GoFish{ + { + Name: "foo_{{ .Env.FOO_BAR }}", + Rig: config.RepoRef{ + Owner: "foo", + Name: "bar", + }, + IDs: []string{ + "foo", + }, + }, + }, + }, + } + 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", + }, + }) + + f, err := os.Create(path) + require.NoError(t, err) + require.NoError(t, f.Close()) + client := &DummyClient{} + distFile := filepath.Join(folder, "foo_is_bar.lua") + + require.NoError(t, runAll(ctx, client)) + require.NoError(t, publishAll(ctx, client)) + require.True(t, client.CreatedFile) + golden.RequireEqualLua(t, []byte(client.Content)) + distBts, err := os.ReadFile(distFile) + require.NoError(t, err) + require.Equal(t, client.Content, string(distBts)) +} + +func TestRunPipeMultipleGoFishWithSkip(t *testing.T) { + folder := t.TempDir() + ctx := &context.Context{ + Git: context.GitInfo{ + CurrentTag: "v1.0.1", + }, + Version: "1.0.1", + Artifacts: artifact.New(), + Env: map[string]string{ + "FOO_BAR": "is_bar", + }, + Config: config.Project{ + Dist: folder, + ProjectName: "foo", + Rigs: []config.GoFish{ + { + Name: "foo", + Rig: config.RepoRef{ + Owner: "foo", + Name: "bar", + }, + IDs: []string{ + "foo", + }, + SkipUpload: "true", + }, + { + Name: "bar", + Rig: config.RepoRef{ + Owner: "foo", + Name: "bar", + }, + IDs: []string{ + "foo", + }, + }, + { + Name: "foobar", + Rig: config.RepoRef{ + Owner: "foo", + Name: "bar", + }, + IDs: []string{ + "foo", + }, + SkipUpload: "true", + }, + }, + }, + } + 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", + }, + }) + + f, err := os.Create(path) + require.NoError(t, err) + require.NoError(t, f.Close()) + + cli := &DummyClient{} + require.NoError(t, runAll(ctx, cli)) + require.EqualError(t, publishAll(ctx, cli), `rig.skip_upload is set`) + require.True(t, cli.CreatedFile) + + for _, food := range ctx.Config.Rigs { + distFile := filepath.Join(folder, food.Name+".lua") + _, err := os.Stat(distFile) + require.NoError(t, err, "file should exist: "+distFile) + } +} + +func TestRunPipeForMultipleArmVersions(t *testing.T) { + for name, fn := range map[string]func(ctx *context.Context){ + "multiple_armv5": func(ctx *context.Context) { + ctx.Config.Rigs[0].Goarm = "5" + }, + "multiple_armv6": func(ctx *context.Context) { + ctx.Config.Rigs[0].Goarm = "6" + }, + "multiple_armv7": func(ctx *context.Context) { + ctx.Config.Rigs[0].Goarm = "7" + }, + } { + t.Run(name, func(t *testing.T) { + folder := t.TempDir() + ctx := &context.Context{ + TokenType: context.TokenTypeGitHub, + Git: context.GitInfo{ + CurrentTag: "v1.0.1", + }, + Version: "1.0.1", + Artifacts: artifact.New(), + Env: map[string]string{ + "FOO": "foo_is_bar", + }, + Config: config.Project{ + Dist: folder, + ProjectName: name, + Rigs: []config.GoFish{ + { + Name: name, + Description: "A run pipe test formula and FOO={{ .Env.FOO }}", + Rig: config.RepoRef{ + Owner: "test", + Name: "test", + }, + Homepage: "https://github.com/goreleaser", + }, + }, + GitHubURLs: config.GitHubURLs{ + Download: "https://github.com", + }, + Release: config.Release{ + GitHub: config.Repo{ + Owner: "test", + Name: "test", + }, + }, + }, + } + fn(ctx) + for _, a := range []struct { + name string + goos string + goarch string + goarm string + }{ + { + name: "bin", + goos: "darwin", + goarch: "amd64", + }, + { + name: "arm64", + goos: "linux", + goarch: "arm64", + }, + { + name: "armv5", + goos: "linux", + goarch: "arm", + goarm: "5", + }, + { + name: "armv6", + goos: "linux", + goarch: "arm", + goarm: "6", + }, + { + name: "armv7", + goos: "linux", + goarch: "arm", + goarm: "7", + }, + } { + path := filepath.Join(folder, fmt.Sprintf("%s.tar.gz", a.name)) + ctx.Artifacts.Add(&artifact.Artifact{ + Name: fmt.Sprintf("%s.tar.gz", a.name), + Path: path, + Goos: a.goos, + Goarch: a.goarch, + Goarm: a.goarm, + Type: artifact.UploadableArchive, + Extra: map[string]interface{}{ + "ID": a.name, + "Format": "tar.gz", + }, + }) + f, err := os.Create(path) + require.NoError(t, err) + require.NoError(t, f.Close()) + } + + client := &DummyClient{} + distFile := filepath.Join(folder, name+".lua") + + require.NoError(t, runAll(ctx, client)) + require.NoError(t, publishAll(ctx, client)) + require.True(t, client.CreatedFile) + golden.RequireEqualLua(t, []byte(client.Content)) + + distBts, err := os.ReadFile(distFile) + require.NoError(t, err) + require.Equal(t, client.Content, string(distBts)) + }) + } +} + +func TestRunPipeNoBuilds(t *testing.T) { + ctx := &context.Context{ + TokenType: context.TokenTypeGitHub, + Config: config.Project{ + Rigs: []config.GoFish{ + { + Rig: config.RepoRef{ + Owner: "test", + Name: "test", + }, + }, + }, + }, + } + client := &DummyClient{} + require.Equal(t, ErrNoArchivesFound, runAll(ctx, client)) + require.False(t, client.CreatedFile) +} + +func TestRunPipeMultipleArchivesSameOsBuild(t *testing.T) { + ctx := context.New( + config.Project{ + Rigs: []config.GoFish{ + { + Rig: config.RepoRef{ + Owner: "test", + Name: "test", + }, + }, + }, + }, + ) + + ctx.TokenType = context.TokenTypeGitHub + f, err := ioutil.TempFile(t.TempDir(), "") + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, f.Close()) + }) + + tests := []struct { + expectedError error + osarchs []struct { + goos string + goarch string + goarm string + } + }{ + { + expectedError: ErrMultipleArchivesSameOS, + osarchs: []struct { + goos string + goarch string + goarm string + }{ + { + goos: "darwin", + goarch: "amd64", + }, + { + goos: "darwin", + goarch: "amd64", + }, + }, + }, + { + expectedError: ErrMultipleArchivesSameOS, + osarchs: []struct { + goos string + goarch string + goarm string + }{ + { + goos: "linux", + goarch: "amd64", + }, + { + goos: "linux", + goarch: "amd64", + }, + }, + }, + { + expectedError: ErrMultipleArchivesSameOS, + osarchs: []struct { + goos string + goarch string + goarm string + }{ + { + goos: "linux", + goarch: "arm64", + }, + { + goos: "linux", + goarch: "arm64", + }, + }, + }, + { + expectedError: ErrMultipleArchivesSameOS, + osarchs: []struct { + goos string + goarch string + goarm string + }{ + { + goos: "linux", + goarch: "arm", + goarm: "6", + }, + { + goos: "linux", + goarch: "arm", + goarm: "6", + }, + }, + }, + { + expectedError: ErrMultipleArchivesSameOS, + osarchs: []struct { + goos string + goarch string + goarm string + }{ + { + goos: "linux", + goarch: "arm", + goarm: "5", + }, + { + goos: "linux", + goarch: "arm", + goarm: "6", + }, + { + goos: "linux", + goarch: "arm", + goarm: "7", + }, + }, + }, + } + + for _, test := range tests { + for idx, ttt := range test.osarchs { + ctx.Artifacts.Add(&artifact.Artifact{ + Name: fmt.Sprintf("bin%d", idx), + Path: f.Name(), + Goos: ttt.goos, + Goarch: ttt.goarch, + Type: artifact.UploadableArchive, + Extra: map[string]interface{}{ + "ID": fmt.Sprintf("foo%d", idx), + "Format": "tar.gz", + }, + }) + } + client := &DummyClient{} + require.Equal(t, test.expectedError, runAll(ctx, client)) + require.False(t, client.CreatedFile) + // clean the artifacts for the next run + ctx.Artifacts = artifact.New() + } +} + +func TestRunPipeBinaryRelease(t *testing.T) { + ctx := context.New( + config.Project{ + Rigs: []config.GoFish{ + { + Rig: config.RepoRef{ + Owner: "test", + Name: "test", + }, + }, + }, + }, + ) + ctx.Artifacts.Add(&artifact.Artifact{ + Name: "bin", + Path: "doesnt mather", + Goos: "darwin", + Goarch: "amd64", + Type: artifact.Binary, + }) + client := &DummyClient{} + require.Equal(t, ErrNoArchivesFound, runAll(ctx, client)) + require.False(t, client.CreatedFile) +} + +func TestRunPipeNoUpload(t *testing.T) { + folder := t.TempDir() + ctx := context.New(config.Project{ + Dist: folder, + ProjectName: "foo", + Release: config.Release{}, + Rigs: []config.GoFish{ + { + Rig: config.RepoRef{ + Owner: "test", + Name: "test", + }, + }, + }, + }) + ctx.TokenType = context.TokenTypeGitHub + ctx.Git = context.GitInfo{CurrentTag: "v1.0.1"} + path := filepath.Join(folder, "whatever.tar.gz") + f, err := os.Create(path) + require.NoError(t, err) + require.NoError(t, f.Close()) + ctx.Artifacts.Add(&artifact.Artifact{ + Name: "bin", + Path: path, + Goos: "darwin", + Goarch: "amd64", + Type: artifact.UploadableArchive, + Extra: map[string]interface{}{ + "ID": "foo", + "Format": "tar.gz", + }, + }) + client := &DummyClient{} + + assertNoPublish := func(t *testing.T) { + t.Helper() + require.NoError(t, runAll(ctx, client)) + testlib.AssertSkipped(t, publishAll(ctx, client)) + require.False(t, client.CreatedFile) + } + t.Run("skip upload true", func(t *testing.T) { + ctx.Config.Rigs[0].SkipUpload = "true" + ctx.Semver.Prerelease = "" + assertNoPublish(t) + }) + t.Run("skip upload auto", func(t *testing.T) { + ctx.Config.Rigs[0].SkipUpload = "auto" + ctx.Semver.Prerelease = "beta1" + assertNoPublish(t) + }) +} + +func TestRunEmptyTokenType(t *testing.T) { + folder := t.TempDir() + ctx := context.New(config.Project{ + Dist: folder, + ProjectName: "foo", + Release: config.Release{}, + Rigs: []config.GoFish{ + { + Rig: config.RepoRef{ + Owner: "test", + Name: "test", + }, + }, + }, + }) + ctx.Git = context.GitInfo{CurrentTag: "v1.0.1"} + path := filepath.Join(folder, "whatever.tar.gz") + f, err := os.Create(path) + require.NoError(t, err) + require.NoError(t, f.Close()) + ctx.Artifacts.Add(&artifact.Artifact{ + Name: "bin", + Path: path, + Goos: "darwin", + Goarch: "amd64", + Type: artifact.UploadableArchive, + Extra: map[string]interface{}{ + "ID": "foo", + "Format": "tar.gz", + }, + }) + client := &DummyClient{} + require.NoError(t, runAll(ctx, client)) +} + +func TestRunTokenTypeNotImplementedForGoFish(t *testing.T) { + folder := t.TempDir() + ctx := context.New(config.Project{ + Dist: folder, + ProjectName: "foo", + Release: config.Release{}, + Rigs: []config.GoFish{ + { + Rig: config.RepoRef{ + Owner: "test", + Name: "test", + }, + }, + }, + }) + ctx.TokenType = context.TokenTypeGitea + ctx.Git = context.GitInfo{CurrentTag: "v1.0.1"} + path := filepath.Join(folder, "whatever.tar.gz") + f, err := os.Create(path) + require.NoError(t, err) + require.NoError(t, f.Close()) + ctx.Artifacts.Add(&artifact.Artifact{ + Name: "bin", + Path: path, + Goos: "darwin", + Goarch: "amd64", + Type: artifact.UploadableArchive, + Extra: map[string]interface{}{ + "ID": "foo", + "Format": "tar.gz", + }, + }) + client := &DummyClient{NotImplemented: true} + require.EqualError(t, runAll(ctx, client), `token type "gitea" not implemented for gofish pipe`) +} + +func TestDefault(t *testing.T) { + testlib.Mktmp(t) + + ctx := &context.Context{ + TokenType: context.TokenTypeGitHub, + Config: config.Project{ + ProjectName: "myproject", + Rigs: []config.GoFish{ + {}, + }, + Builds: []config.Build{ + { + Binary: "foo", + Goos: []string{"linux", "darwin"}, + Goarch: []string{"386", "amd64"}, + }, + { + Binary: "bar", + Goos: []string{"linux", "darwin"}, + Goarch: []string{"386", "amd64"}, + Ignore: []config.IgnoredBuild{ + {Goos: "darwin", Goarch: "amd64"}, + }, + }, + { + Binary: "foobar", + Goos: []string{"linux"}, + Goarch: []string{"amd64"}, + }, + }, + }, + } + require.NoError(t, Pipe{}.Default(ctx)) + require.Equal(t, ctx.Config.ProjectName, ctx.Config.Rigs[0].Name) + require.NotEmpty(t, ctx.Config.Rigs[0].CommitAuthor.Name) + require.NotEmpty(t, ctx.Config.Rigs[0].CommitAuthor.Email) + require.NotEmpty(t, ctx.Config.Rigs[0].CommitMessageTemplate) +} + +func TestGHFolder(t *testing.T) { + require.Equal(t, "bar.lua", buildFoodPath("", "bar.lua")) + require.Equal(t, "fooo/bar.lua", buildFoodPath("fooo", "bar.lua")) +} + +type DummyClient struct { + CreatedFile bool + Content string + NotImplemented bool +} + +func (dc *DummyClient) CloseMilestone(ctx *context.Context, repo client.Repo, title string) error { + return nil +} + +func (dc *DummyClient) CreateRelease(ctx *context.Context, body string) (releaseID string, err error) { + return +} + +func (dc *DummyClient) ReleaseURLTemplate(ctx *context.Context) (string, error) { + if dc.NotImplemented { + return "", client.NotImplementedError{} + } + + return "https://dummyhost/download/{{ .Tag }}/{{ .ArtifactName }}", nil +} + +func (dc *DummyClient) CreateFile(ctx *context.Context, commitAuthor config.CommitAuthor, repo client.Repo, content []byte, path, msg string) (err error) { + dc.CreatedFile = true + dc.Content = string(content) + return +} + +func (dc *DummyClient) Upload(ctx *context.Context, releaseID string, artifact *artifact.Artifact, file *os.File) (err error) { + return +} + +func TestSkip(t *testing.T) { + t.Run("skip", func(t *testing.T) { + require.True(t, Pipe{}.Skip(context.New(config.Project{}))) + }) + + t.Run("dont skip", func(t *testing.T) { + ctx := context.New(config.Project{ + Rigs: []config.GoFish{ + {}, + }, + }) + require.False(t, Pipe{}.Skip(ctx)) + }) +} + +func TestRunSkipNoName(t *testing.T) { + ctx := context.New(config.Project{ + Rigs: []config.GoFish{{}}, + }) + + client := &DummyClient{} + testlib.AssertSkipped(t, runAll(ctx, client)) +} diff --git a/internal/pipe/gofish/testdata/TestFullFood.lua.golden b/internal/pipe/gofish/testdata/TestFullFood.lua.golden new file mode 100644 index 000000000..e579630b6 --- /dev/null +++ b/internal/pipe/gofish/testdata/TestFullFood.lua.golden @@ -0,0 +1,89 @@ +local name = "Test" +local version = "0.1.3" + +food = { + name = name, + description = "Some desc", + license = "MIT", + homepage = "https://google.com", + version = version, + packages = { + { + os = "darwin", + arch = "amd64", + url = "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Darwin_x86_64.tar.gz", + sha256 = "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c68", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + { + os = "darwin", + arch = "arm64", + url = "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Darwin_arm64.tar.gz", + sha256 = "1633f61598ab0791e213135923624eb342196b349490sadasdsadsadasdasdsd", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + { + os = "linux", + arch = "amd64", + url = "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Linux_x86_64.tar.gz", + sha256 = "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c67", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + { + os = "linux", + arch = "arm", + url = "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Arm6.tar.gz", + sha256 = "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c67", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + { + os = "linux", + arch = "arm64", + url = "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Arm64.tar.gz", + sha256 = "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c67", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + { + os = "windows", + arch = "amd64", + url = "https://github.com/caarlos0/test/releases/download/v0.1.3/test_windows_amd64.zip", + sha256 = "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c67", + resources = { + { + path = name .. ".exe", + installpath = "bin\\" .. name .. ".exe" + } + } + }, + } +} diff --git a/internal/pipe/gofish/testdata/TestFullFoodLinuxOnly.lua.golden b/internal/pipe/gofish/testdata/TestFullFoodLinuxOnly.lua.golden new file mode 100644 index 000000000..c0629c189 --- /dev/null +++ b/internal/pipe/gofish/testdata/TestFullFoodLinuxOnly.lua.golden @@ -0,0 +1,51 @@ +local name = "Test" +local version = "0.1.3" + +food = { + name = name, + description = "Some desc", + license = "", + homepage = "https://google.com", + version = version, + packages = { + { + os = "linux", + arch = "amd64", + url = "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Linux_x86_64.tar.gz", + sha256 = "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c67", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + { + os = "linux", + arch = "arm", + url = "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Arm6.tar.gz", + sha256 = "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c67", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + { + os = "linux", + arch = "arm64", + url = "https://github.com/caarlos0/test/releases/download/v0.1.3/test_Arm64.tar.gz", + sha256 = "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c67", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + } +} diff --git a/internal/pipe/gofish/testdata/TestFullFoodWindowsOnly.lua.golden b/internal/pipe/gofish/testdata/TestFullFoodWindowsOnly.lua.golden new file mode 100644 index 000000000..2b98981d1 --- /dev/null +++ b/internal/pipe/gofish/testdata/TestFullFoodWindowsOnly.lua.golden @@ -0,0 +1,24 @@ +local name = "Test" +local version = "0.1.3" + +food = { + name = name, + description = "Some desc", + license = "", + homepage = "https://google.com", + version = version, + packages = { + { + os = "windows", + arch = "amd64", + url = "https://github.com/caarlos0/test/releases/download/v0.1.3/test_windows_amd64.zip", + sha256 = "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c67", + resources = { + { + path = name .. ".exe", + installpath = "bin\\" .. name .. ".exe" + } + } + }, + } +} diff --git a/internal/pipe/gofish/testdata/TestFullPipe/default.lua.golden b/internal/pipe/gofish/testdata/TestFullPipe/default.lua.golden new file mode 100644 index 000000000..df249e05c --- /dev/null +++ b/internal/pipe/gofish/testdata/TestFullPipe/default.lua.golden @@ -0,0 +1,25 @@ +local name = "default" +local version = "1.0.1" + +food = { + name = name, + description = "A run pipe test formula and FOO=foo_is_bar", + license = "", + homepage = "https://github.com/goreleaser", + version = version, + packages = { + { + os = "darwin", + arch = "amd64", + url = "https://dummyhost/download/v1.0.1/bin.tar.gz", + sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + } +} diff --git a/internal/pipe/gofish/testdata/TestFullPipe/default_gitlab.lua.golden b/internal/pipe/gofish/testdata/TestFullPipe/default_gitlab.lua.golden new file mode 100644 index 000000000..0b5745690 --- /dev/null +++ b/internal/pipe/gofish/testdata/TestFullPipe/default_gitlab.lua.golden @@ -0,0 +1,25 @@ +local name = "default_gitlab" +local version = "1.0.1" + +food = { + name = name, + description = "A run pipe test formula and FOO=foo_is_bar", + license = "", + homepage = "https://gitlab.com/goreleaser", + version = version, + packages = { + { + os = "darwin", + arch = "amd64", + url = "https://dummyhost/download/v1.0.1/bin.tar.gz", + sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + } +} diff --git a/internal/pipe/gofish/testdata/TestRunPipeForMultipleArmVersions/multiple_armv5.lua.golden b/internal/pipe/gofish/testdata/TestRunPipeForMultipleArmVersions/multiple_armv5.lua.golden new file mode 100644 index 000000000..0450d7f8b --- /dev/null +++ b/internal/pipe/gofish/testdata/TestRunPipeForMultipleArmVersions/multiple_armv5.lua.golden @@ -0,0 +1,51 @@ +local name = "multiple_armv5" +local version = "1.0.1" + +food = { + name = name, + description = "A run pipe test formula and FOO=foo_is_bar", + license = "", + homepage = "https://github.com/goreleaser", + version = version, + packages = { + { + os = "darwin", + arch = "amd64", + url = "https://dummyhost/download/v1.0.1/bin.tar.gz", + sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + { + os = "linux", + arch = "arm64", + url = "https://dummyhost/download/v1.0.1/arm64.tar.gz", + sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + { + os = "linux", + arch = "arm", + url = "https://dummyhost/download/v1.0.1/armv5.tar.gz", + sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + } +} diff --git a/internal/pipe/gofish/testdata/TestRunPipeForMultipleArmVersions/multiple_armv6.lua.golden b/internal/pipe/gofish/testdata/TestRunPipeForMultipleArmVersions/multiple_armv6.lua.golden new file mode 100644 index 000000000..317153459 --- /dev/null +++ b/internal/pipe/gofish/testdata/TestRunPipeForMultipleArmVersions/multiple_armv6.lua.golden @@ -0,0 +1,51 @@ +local name = "multiple_armv6" +local version = "1.0.1" + +food = { + name = name, + description = "A run pipe test formula and FOO=foo_is_bar", + license = "", + homepage = "https://github.com/goreleaser", + version = version, + packages = { + { + os = "darwin", + arch = "amd64", + url = "https://dummyhost/download/v1.0.1/bin.tar.gz", + sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + { + os = "linux", + arch = "arm64", + url = "https://dummyhost/download/v1.0.1/arm64.tar.gz", + sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + { + os = "linux", + arch = "arm", + url = "https://dummyhost/download/v1.0.1/armv6.tar.gz", + sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + } +} diff --git a/internal/pipe/gofish/testdata/TestRunPipeForMultipleArmVersions/multiple_armv7.lua.golden b/internal/pipe/gofish/testdata/TestRunPipeForMultipleArmVersions/multiple_armv7.lua.golden new file mode 100644 index 000000000..b13c1bf11 --- /dev/null +++ b/internal/pipe/gofish/testdata/TestRunPipeForMultipleArmVersions/multiple_armv7.lua.golden @@ -0,0 +1,51 @@ +local name = "multiple_armv7" +local version = "1.0.1" + +food = { + name = name, + description = "A run pipe test formula and FOO=foo_is_bar", + license = "", + homepage = "https://github.com/goreleaser", + version = version, + packages = { + { + os = "darwin", + arch = "amd64", + url = "https://dummyhost/download/v1.0.1/bin.tar.gz", + sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + { + os = "linux", + arch = "arm64", + url = "https://dummyhost/download/v1.0.1/arm64.tar.gz", + sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + { + os = "linux", + arch = "arm", + url = "https://dummyhost/download/v1.0.1/armv7.tar.gz", + sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + } +} diff --git a/internal/pipe/gofish/testdata/TestRunPipeNameTemplate.lua.golden b/internal/pipe/gofish/testdata/TestRunPipeNameTemplate.lua.golden new file mode 100644 index 000000000..e773c658f --- /dev/null +++ b/internal/pipe/gofish/testdata/TestRunPipeNameTemplate.lua.golden @@ -0,0 +1,25 @@ +local name = "foo_is_bar" +local version = "1.0.1" + +food = { + name = name, + description = "", + license = "", + homepage = "", + version = version, + packages = { + { + os = "darwin", + arch = "amd64", + url = "https://dummyhost/download/v1.0.1/bin.tar.gz", + sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + }, + } +} diff --git a/internal/pipe/publish/publish.go b/internal/pipe/publish/publish.go index 75b38a900..c71aed94b 100644 --- a/internal/pipe/publish/publish.go +++ b/internal/pipe/publish/publish.go @@ -4,6 +4,8 @@ package publish import ( "fmt" + "github.com/goreleaser/goreleaser/internal/pipe/gofish" + "github.com/goreleaser/goreleaser/internal/middleware/errhandler" "github.com/goreleaser/goreleaser/internal/middleware/logging" "github.com/goreleaser/goreleaser/internal/middleware/skip" @@ -43,6 +45,7 @@ var publishers = []Publisher{ release.Pipe{}, // brew and scoop use the release URL, so, they should be last brew.Pipe{}, + gofish.Pipe{}, scoop.Pipe{}, milestone.Pipe{}, } diff --git a/internal/pipeline/pipeline.go b/internal/pipeline/pipeline.go index 65c4d1f14..d998f7ef5 100644 --- a/internal/pipeline/pipeline.go +++ b/internal/pipeline/pipeline.go @@ -17,6 +17,7 @@ import ( "github.com/goreleaser/goreleaser/internal/pipe/effectiveconfig" "github.com/goreleaser/goreleaser/internal/pipe/env" "github.com/goreleaser/goreleaser/internal/pipe/git" + "github.com/goreleaser/goreleaser/internal/pipe/gofish" "github.com/goreleaser/goreleaser/internal/pipe/gomod" "github.com/goreleaser/goreleaser/internal/pipe/nfpm" "github.com/goreleaser/goreleaser/internal/pipe/publish" @@ -63,6 +64,7 @@ var Pipeline = append( nfpm.Pipe{}, // archive via fpm (deb, rpm) using "native" go impl snapcraft.Pipe{}, // archive via snapcraft (snap) brew.Pipe{}, // create brew tap + gofish.Pipe{}, // create gofish rig scoop.Pipe{}, // create scoop buckets checksums.Pipe{}, // checksums of the files sign.Pipe{}, // sign artifacts diff --git a/pkg/config/config.go b/pkg/config/config.go index f2e3be7d0..762933962 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -88,6 +88,21 @@ func (r Repo) String() string { return r.Owner + "/" + r.Name } +// GoFish contains the gofish section. +type GoFish struct { + Name string `yaml:",omitempty"` + Rig RepoRef `yaml:",omitempty"` + CommitAuthor CommitAuthor `yaml:"commit_author,omitempty"` + CommitMessageTemplate string `yaml:"commit_msg_template,omitempty"` + Description string `yaml:",omitempty"` + Homepage string `yaml:",omitempty"` + License string `yaml:",omitempty"` + SkipUpload string `yaml:"skip_upload,omitempty"` + URLTemplate string `yaml:"url_template,omitempty"` + IDs []string `yaml:"ids,omitempty"` + Goarm string `yaml:"goarm,omitempty"` +} + // Homebrew contains the brew section. type Homebrew struct { Name string `yaml:",omitempty"` @@ -648,6 +663,7 @@ type Project struct { Release Release `yaml:",omitempty"` Milestones []Milestone `yaml:",omitempty"` Brews []Homebrew `yaml:",omitempty"` + Rigs []GoFish `yaml:",omitempty"` Scoop Scoop `yaml:",omitempty"` Builds []Build `yaml:",omitempty"` Archives []Archive `yaml:",omitempty"` diff --git a/pkg/defaults/defaults.go b/pkg/defaults/defaults.go index ea7c9cd2f..6aa5dfc26 100644 --- a/pkg/defaults/defaults.go +++ b/pkg/defaults/defaults.go @@ -13,6 +13,7 @@ import ( "github.com/goreleaser/goreleaser/internal/pipe/checksums" "github.com/goreleaser/goreleaser/internal/pipe/discord" "github.com/goreleaser/goreleaser/internal/pipe/docker" + "github.com/goreleaser/goreleaser/internal/pipe/gofish" "github.com/goreleaser/goreleaser/internal/pipe/gomod" "github.com/goreleaser/goreleaser/internal/pipe/milestone" "github.com/goreleaser/goreleaser/internal/pipe/nfpm" @@ -60,6 +61,7 @@ var Defaulters = []Defaulter{ artifactory.Pipe{}, blob.Pipe{}, brew.Pipe{}, + gofish.Pipe{}, scoop.Pipe{}, discord.Pipe{}, reddit.Pipe{}, diff --git a/www/docs/customization/gofish.md b/www/docs/customization/gofish.md new file mode 100644 index 000000000..472ce607f --- /dev/null +++ b/www/docs/customization/gofish.md @@ -0,0 +1,120 @@ +--- +title: GoFish +--- + +After releasing to GitHub or GitLab, GoReleaser can generate and publish +a _Fish Food_ Cookbook into a repository that you have access to. + +The `rigs` section specifies how the fish food should be created. +You can check the +[GoFish documentation](https://gofi.sh/#intro) +and the +[Fish food cookbook](https://gofi.sh/#cookbook) +for more details. + +!!! warning + If you have multiple 32-bit arm versions in each `build` section, and + you do not specify any `ids` in the rigs section, it will default to all + artifacts and GoReleaser will fail. + +```yaml +# .goreleaser.yml +rigs: + - + # Name template of the recipe + # Default to project name + name: myproject + + # IDs of the archives to use. + # Defaults to all. + ids: + - foo + - bar + + # GOARM to specify which 32-bit arm version to use if there are multiple versions + # from the build section. GoFish fish food support atm only one 32-bit version. + # Default is 6 for all artifacts or each id if there a multiple versions. + goarm: 6 + + # NOTE: make sure the url_template, the token and given repo (github or gitlab) owner and name are from the + # same kind. We will probably unify this in the next major version like it is done with scoop. + + # GitHub/GitLab repository to push the fish food to + # Gitea is not supported yet, but the support coming + rig: + owner: repo-owner + name: gofish-rig + # Optionally a token can be provided, if it differs from the token provided to GoReleaser + token: "{{ .Env.GOFISH_RIG_GITHUB_TOKEN }}" + + # Template for the url which is determined by the given Token (github or gitlab) + # Default for github is "https://github.com///releases/download/{{ .Tag }}/{{ .ArtifactName }}" + # Default for gitlab is "https://gitlab.com///-/releases/{{ .Tag }}/downloads/{{ .ArtifactName }}" + # Default for gitea is "https://gitea.com///releases/download/{{ .Tag }}/{{ .ArtifactName }}" + url_template: "http://github.mycompany.com/foo/bar/releases/{{ .Tag }}/{{ .ArtifactName }}" + + # Git author used to commit to the repository. + # Defaults are shown. + commit_author: + name: goreleaserbot + email: goreleaser@carlosbecker.com + + # The project name and current git tag are used in the format string. + commit_msg_template: "GoFish fish food update for {{ .ProjectName }} version {{ .Tag }}" + + # Your app's homepage. + # Default is empty. + homepage: "https://example.com/" + + # Template of your app's description. + # Default is empty. + description: "Software to create fast and easy drum rolls." + + # SPDX identifier of your app's license. + # Default is empty. + license: "MIT" + + # Setting this will prevent goreleaser to actually try to commit the updated + # fish food - instead, the fish food 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 GoFish rig + # in case there is an indicator for prerelease in the tag e.g. v1.0.0-rc1 + # Default is false. + skip_upload: true +``` + +!!! tip + Learn more about the [name template engine](/customization/templates/). + +By defining the `rigs` section, GoReleaser will take care of publishing the +GoFish rig. +Assuming that the current tag is `v1.2.3`, the above configuration will generate a +`program.lua` fish food in the `Food` folder of `user/gofish-rig` repository: + +```lua +local name = "Program" +local version = "1.2.3" + +food = { + name = name, + description = "How to use this binary", + license = "MIT", + homepage = "https://github.com/user/repo", + version = version, + packages = { + { + os = "darwin", + arch = "amd64", + url = "https://github.com/user/repo/releases/download/v1.2.3/program_v1.2.3_macOs_64bit.zip", + sha256 = "9ee30fc358fae8d248a2d7538957089885da321dca3f09e3296fe2058e7fff74", + resources = { + { + path = name, + installpath = "bin/" .. name, + executable = true + } + } + } + } +} +``` diff --git a/www/htmltest.yml b/www/htmltest.yml index c99a3d088..6fbf81080 100644 --- a/www/htmltest.yml +++ b/www/htmltest.yml @@ -8,6 +8,7 @@ IgnoreURLs: - microsoft.com - https://github.com/goreleaser/goreleaser/edit/ - https://discord.com/ +- https://gofi.sh IgnoreDirs: - overrides IgnoreDirectoryMissingTrailingSlash: true diff --git a/www/mkdocs.yml b/www/mkdocs.yml index 7c41bb9b4..09291291c 100644 --- a/www/mkdocs.yml +++ b/www/mkdocs.yml @@ -94,6 +94,7 @@ nav: - customization/blob.md - customization/fury.md - customization/homebrew.md + - customization/gofish.md - customization/scoop.md - customization/release.md - customization/artifactory.md