1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-01-26 04:22:05 +02:00

feat: allow multiple scoops (#3963)

This brings the scoops feature a bit more closer to similar pipes, like
brew and krew.

- It now supports multiple scoops
- It improves some validations to prevent wrong manifests
- It uses extra.binaries instead of extra.builds, as brew does too
	- extra.builds is now unused, will be removed in a subsequent PR
- More tests were added as well

closes #3941

---------

Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
This commit is contained in:
Carlos Alexandro Becker 2023-04-30 21:29:36 -03:00 committed by GitHub
parent 01e9810ab2
commit eb823dee14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 371 additions and 119 deletions

View File

@ -212,14 +212,14 @@ aurs:
# man pages
install -Dm644 "./manpages/goreleaser.1.gz" "${pkgdir}/usr/share/man/man1/goreleaser.1.gz"
scoop:
bucket:
owner: goreleaser
name: scoop-bucket
folder: bucket
homepage: https://goreleaser.com
description: Deliver Go binaries as fast and easily as possible
license: MIT
scoops:
- bucket:
owner: goreleaser
name: scoop-bucket
folder: bucket
homepage: https://goreleaser.com
description: Deliver Go binaries as fast and easily as possible
license: MIT
nfpms:
- file_name_template: '{{ .ConventionalFileName }}'

View File

@ -131,7 +131,7 @@ const (
ExtraID = "ID"
ExtraBinary = "Binary"
ExtraExt = "Ext"
ExtraBuilds = "Builds"
ExtraBuilds = "Builds" // deprecated
ExtraFormat = "Format"
ExtraWrappedIn = "WrappedIn"
ExtraBinaries = "Binaries"

View File

@ -14,20 +14,43 @@ import (
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/goreleaser/goreleaser/internal/client"
"github.com/goreleaser/goreleaser/internal/commitauthor"
"github.com/goreleaser/goreleaser/internal/deprecate"
"github.com/goreleaser/goreleaser/internal/pipe"
"github.com/goreleaser/goreleaser/internal/tmpl"
"github.com/goreleaser/goreleaser/pkg/config"
"github.com/goreleaser/goreleaser/pkg/context"
)
// ErrNoWindows when there is no build for windows (goos doesn't contain
// windows) or archive.format is binary.
type ErrNoWindows struct {
goamd64 string
// ErrIncorrectArchiveCount happens when a given filter evaluates 0 or more
// than 1 archives.
type ErrIncorrectArchiveCount struct {
goamd64 string
ids []string
archives []*artifact.Artifact
}
func (e ErrNoWindows) Error() string {
return fmt.Sprintf("scoop requires a windows archive, but no archives matched goos=windows goarch=[386 amd64] goamd64=%s\nLearn more at https://goreleaser.com/errors/scoop-archive\n", e.goamd64) // nolint: revive
func (e ErrIncorrectArchiveCount) Error() string {
b := strings.Builder{}
_, _ = b.WriteString("scoop requires a single windows archive, ")
if len(e.archives) == 0 {
_, _ = b.WriteString("but no archives ")
} else {
_, _ = b.WriteString(fmt.Sprintf("but found %d archives ", len(e.archives)))
}
_, _ = b.WriteString(fmt.Sprintf("matching the given filters: goos=windows goarch=[386 amd64] goamd64=%s ids=%s", e.goamd64, e.ids))
if len(e.archives) > 0 {
names := make([]string, 0, len(e.archives))
for _, a := range e.archives {
names = append(names, a.Name)
}
_, _ = b.WriteString(fmt.Sprintf(": %s", names))
}
_, _ = b.WriteString("\nLearn more at https://goreleaser.com/errors/scoop-archive\n")
return b.String()
}
const scoopConfigExtra = "ScoopConfig"
@ -35,8 +58,10 @@ const scoopConfigExtra = "ScoopConfig"
// Pipe that builds and publishes scoop manifests.
type Pipe struct{}
func (Pipe) String() string { return "scoop manifests" }
func (Pipe) Skip(ctx *context.Context) bool { return ctx.Config.Scoop.Bucket.Name == "" }
func (Pipe) String() string { return "scoop manifests" }
func (Pipe) Skip(ctx *context.Context) bool {
return ctx.Config.Scoop.Bucket.Name == "" && len(ctx.Config.Scoops) == 0
}
// Run creates the scoop manifest locally.
func (Pipe) Run(ctx *context.Context) error {
@ -44,7 +69,7 @@ func (Pipe) Run(ctx *context.Context) error {
if err != nil {
return err
}
return doRun(ctx, client)
return runAll(ctx, client)
}
// Publish scoop manifest.
@ -53,47 +78,74 @@ func (Pipe) Publish(ctx *context.Context) error {
if err != nil {
return err
}
return doPublish(ctx, client)
return publishAll(ctx, client)
}
// Default sets the pipe defaults.
func (Pipe) Default(ctx *context.Context) error {
if ctx.Config.Scoop.Name == "" {
ctx.Config.Scoop.Name = ctx.Config.ProjectName
if ctx.Config.Scoop.Bucket.Name != "" {
deprecate.Notice(ctx, "scoop")
ctx.Config.Scoops = append(ctx.Config.Scoops, ctx.Config.Scoop)
}
ctx.Config.Scoop.CommitAuthor = commitauthor.Default(ctx.Config.Scoop.CommitAuthor)
if ctx.Config.Scoop.CommitMessageTemplate == "" {
ctx.Config.Scoop.CommitMessageTemplate = "Scoop update for {{ .ProjectName }} version {{ .Tag }}"
}
if ctx.Config.Scoop.Goamd64 == "" {
ctx.Config.Scoop.Goamd64 = "v1"
for i := range ctx.Config.Scoops {
scoop := &ctx.Config.Scoops[i]
if scoop.Name == "" {
scoop.Name = ctx.Config.ProjectName
}
scoop.CommitAuthor = commitauthor.Default(scoop.CommitAuthor)
if scoop.CommitMessageTemplate == "" {
scoop.CommitMessageTemplate = "Scoop update for {{ .ProjectName }} version {{ .Tag }}"
}
if scoop.Goamd64 == "" {
scoop.Goamd64 = "v1"
}
}
return nil
}
func doRun(ctx *context.Context, cl client.ReleaserURLTemplater) error {
scoop := ctx.Config.Scoop
func runAll(ctx *context.Context, cl client.ReleaserURLTemplater) error {
for _, scoop := range ctx.Config.Scoops {
err := doRun(ctx, scoop, cl)
if err != nil {
return err
}
}
return nil
}
archives := ctx.Artifacts.Filter(
artifact.And(
artifact.ByGoos("windows"),
artifact.ByType(artifact.UploadableArchive),
artifact.Or(
artifact.And(
artifact.ByGoarch("amd64"),
artifact.ByGoamd64(scoop.Goamd64),
),
artifact.ByGoarch("386"),
func doRun(ctx *context.Context, scoop config.Scoop, cl client.ReleaserURLTemplater) error {
filters := []artifact.Filter{
artifact.ByGoos("windows"),
artifact.ByType(artifact.UploadableArchive),
artifact.Or(
artifact.And(
artifact.ByGoarch("amd64"),
artifact.ByGoamd64(scoop.Goamd64),
),
artifact.ByGoarch("386"),
),
).List()
}
if len(scoop.IDs) > 0 {
filters = append(filters, artifact.ByIDs(scoop.IDs...))
}
filtered := ctx.Artifacts.Filter(artifact.And(filters...))
archives := filtered.List()
for _, platArchives := range filtered.GroupByPlatform() {
// there might be multiple archives, but only of for each platform
if len(platArchives) != 1 {
return ErrIncorrectArchiveCount{scoop.Goamd64, scoop.IDs, archives}
}
}
// handle no archives found whatsoever
if len(archives) == 0 {
return ErrNoWindows{scoop.Goamd64}
return ErrIncorrectArchiveCount{scoop.Goamd64, scoop.IDs, archives}
}
filename := scoop.Name + ".json"
data, err := dataFor(ctx, cl, archives)
data, err := dataFor(ctx, scoop, cl, archives)
if err != nil {
return err
}
@ -119,14 +171,24 @@ func doRun(ctx *context.Context, cl client.ReleaserURLTemplater) error {
return nil
}
func doPublish(ctx *context.Context, cl client.Client) error {
manifests := ctx.Artifacts.Filter(artifact.ByType(artifact.ScoopManifest)).List()
if len(manifests) == 0 { // should never happen
return nil
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.rb` file, which is useful for debugging.
skips := pipe.SkipMemento{}
for _, manifest := range ctx.Artifacts.Filter(artifact.ByType(artifact.ScoopManifest)).List() {
err := doPublish(ctx, manifest, cli)
if err != nil && pipe.IsSkip(err) {
skips.Remember(err)
continue
}
if err != nil {
return err
}
}
return skips.Evaluate()
}
manifest := manifests[0]
func doPublish(ctx *context.Context, manifest *artifact.Artifact, cl client.Client) error {
scoop, err := artifact.Extra[config.Scoop](*manifest, scoopConfigExtra)
if err != nil {
return err
@ -231,26 +293,26 @@ func doBuildManifest(manifest Manifest) (bytes.Buffer, error) {
return result, err
}
func dataFor(ctx *context.Context, cl client.ReleaserURLTemplater, artifacts []*artifact.Artifact) (Manifest, error) {
func dataFor(ctx *context.Context, scoop config.Scoop, cl client.ReleaserURLTemplater, artifacts []*artifact.Artifact) (Manifest, error) {
manifest := Manifest{
Version: ctx.Version,
Architecture: map[string]Resource{},
Homepage: ctx.Config.Scoop.Homepage,
License: ctx.Config.Scoop.License,
Description: ctx.Config.Scoop.Description,
Persist: ctx.Config.Scoop.Persist,
PreInstall: ctx.Config.Scoop.PreInstall,
PostInstall: ctx.Config.Scoop.PostInstall,
Depends: ctx.Config.Scoop.Depends,
Shortcuts: ctx.Config.Scoop.Shortcuts,
Homepage: scoop.Homepage,
License: scoop.License,
Description: scoop.Description,
Persist: scoop.Persist,
PreInstall: scoop.PreInstall,
PostInstall: scoop.PostInstall,
Depends: scoop.Depends,
Shortcuts: scoop.Shortcuts,
}
if ctx.Config.Scoop.URLTemplate == "" {
if scoop.URLTemplate == "" {
url, err := cl.ReleaseURLTemplate(ctx)
if err != nil {
return manifest, err
}
ctx.Config.Scoop.URLTemplate = url
scoop.URLTemplate = url
}
for _, artifact := range artifacts {
@ -268,7 +330,7 @@ func dataFor(ctx *context.Context, cl client.ReleaserURLTemplater, artifacts []*
continue
}
url, err := tmpl.New(ctx).WithArtifact(artifact).Apply(ctx.Config.Scoop.URLTemplate)
url, err := tmpl.New(ctx).WithArtifact(artifact).Apply(scoop.URLTemplate)
if err != nil {
return manifest, err
}
@ -280,7 +342,7 @@ func dataFor(ctx *context.Context, cl client.ReleaserURLTemplater, artifacts []*
log.WithFields(log.Fields{
"artifactExtras": artifact.Extra,
"fromURLTemplate": ctx.Config.Scoop.URLTemplate,
"fromURLTemplate": scoop.URLTemplate,
"templatedBrewURL": url,
"sum": sum,
}).Debug("scoop url templating")
@ -302,14 +364,14 @@ func dataFor(ctx *context.Context, cl client.ReleaserURLTemplater, artifacts []*
func binaries(a artifact.Artifact) ([]string, error) {
// nolint: prealloc
var bins []string
var result []string
wrap := artifact.ExtraOr(a, artifact.ExtraWrappedIn, "")
builds, err := artifact.Extra[[]artifact.Artifact](a, artifact.ExtraBuilds)
bins, err := artifact.Extra[[]string](a, artifact.ExtraBinaries)
if err != nil {
return nil, err
}
for _, b := range builds {
bins = append(bins, filepath.Join(wrap, b.Name))
for _, b := range bins {
result = append(result, filepath.Join(wrap, b))
}
return bins, nil
return result, nil
}

View File

@ -24,14 +24,46 @@ func TestDefault(t *testing.T) {
testlib.Mktmp(t)
ctx := testctx.NewWithCfg(
config.Project{ProjectName: "barr"},
config.Project{
ProjectName: "barr",
Scoops: []config.Scoop{
{
Bucket: config.RepoRef{
Name: "foo",
},
},
},
},
testctx.GitHubTokenType,
)
require.NoError(t, Pipe{}.Default(ctx))
require.Equal(t, ctx.Config.ProjectName, ctx.Config.Scoop.Name)
require.NotEmpty(t, ctx.Config.Scoop.CommitAuthor.Name)
require.NotEmpty(t, ctx.Config.Scoop.CommitAuthor.Email)
require.NotEmpty(t, ctx.Config.Scoop.CommitMessageTemplate)
require.Len(t, ctx.Config.Scoops, 1)
require.Equal(t, ctx.Config.ProjectName, ctx.Config.Scoops[0].Name)
require.NotEmpty(t, ctx.Config.Scoops[0].CommitAuthor.Name)
require.NotEmpty(t, ctx.Config.Scoops[0].CommitAuthor.Email)
require.NotEmpty(t, ctx.Config.Scoops[0].CommitMessageTemplate)
}
func TestDefaultDeprecated(t *testing.T) {
testlib.Mktmp(t)
ctx := testctx.NewWithCfg(
config.Project{
ProjectName: "barr",
Scoop: config.Scoop{
Bucket: config.RepoRef{
Name: "foo",
},
},
},
testctx.GitHubTokenType,
)
require.NoError(t, Pipe{}.Default(ctx))
require.Len(t, ctx.Config.Scoops, 1)
require.Equal(t, ctx.Config.ProjectName, ctx.Config.Scoops[0].Name)
require.NotEmpty(t, ctx.Config.Scoops[0].CommitAuthor.Name)
require.NotEmpty(t, ctx.Config.Scoops[0].CommitAuthor.Email)
require.NotEmpty(t, ctx.Config.Scoops[0].CommitMessageTemplate)
}
func Test_doRun(t *testing.T) {
@ -69,6 +101,88 @@ func Test_doRun(t *testing.T) {
assertPublishError errChecker
assert asserter
}{
{
"multiple_artifacts",
args{
testctx.NewWithCfg(
config.Project{
Dist: t.TempDir(),
ProjectName: "multi-arts",
Scoops: []config.Scoop{{
Bucket: config.RepoRef{
Owner: "test",
Name: "test",
},
Folder: "scoops",
Description: "A run pipe test formula",
Homepage: "https://github.com/goreleaser",
}},
},
testctx.GitHubTokenType,
testctx.WithCurrentTag("v1.0.1"),
testctx.WithVersion("1.0.1"),
),
client.NewMock(),
},
[]artifact.Artifact{
{Name: "foo_1.0.1_windows_amd64.tar.gz", Goos: "windows", Goarch: "amd64", Goamd64: "v1", Path: file},
{Name: "foos_1.0.1_windows_amd64.tar.gz", Goos: "windows", Goarch: "amd64", Goamd64: "v1", Path: file},
},
func(tb testing.TB, err error) {
tb.Helper()
require.EqualError(tb, err, ErrIncorrectArchiveCount{
goamd64: "v1",
archives: []*artifact.Artifact{
{Name: "foo_1.0.1_windows_amd64.tar.gz"},
{Name: "foos_1.0.1_windows_amd64.tar.gz"},
},
}.Error())
},
nil,
noAssertions,
},
{
"multiple_binaries",
args{
testctx.NewWithCfg(
config.Project{
Dist: t.TempDir(),
ProjectName: "multi-bins",
Scoops: []config.Scoop{{
Bucket: config.RepoRef{
Owner: "test",
Name: "test",
},
IDs: []string{"id2"},
Folder: "scoops",
Description: "A run pipe test formula",
Homepage: "https://github.com/goreleaser",
}},
},
testctx.GitHubTokenType,
testctx.WithCurrentTag("v1.0.1"),
testctx.WithVersion("1.0.1"),
),
client.NewMock(),
},
[]artifact.Artifact{
{Name: "foo_1.0.1_windows_amd64.tar.gz", Goos: "windows", Goarch: "amd64", Goamd64: "v1", Path: file, Extra: map[string]any{
artifact.ExtraID: "id1",
artifact.ExtraBinaries: []string{"bin1", "bin2"},
}},
{Name: "foos_1.0.1_windows_amd64.tar.gz", Goos: "windows", Goarch: "amd64", Goamd64: "v1", Path: file, Extra: map[string]any{
artifact.ExtraID: "id2",
artifact.ExtraBinaries: []string{"bin4", "bin3"},
}},
},
shouldNotErr,
shouldNotErr,
func(tb testing.TB, a args) {
tb.Helper()
require.Equal(tb, "scoops/multi-bins.json", a.client.Path)
golden.RequireEqualJSON(tb, []byte(a.client.Content))
},
},
{
"valid public github",
args{
@ -101,6 +215,7 @@ func Test_doRun(t *testing.T) {
func(tb testing.TB, a args) {
tb.Helper()
require.Equal(tb, "scoops/run-pipe.json", a.client.Path)
golden.RequireEqualJSON(tb, []byte(a.client.Content))
},
},
{
@ -328,7 +443,7 @@ func Test_doRun(t *testing.T) {
client.NewMock(),
},
[]artifact.Artifact{},
shouldErr(ErrNoWindows{"v1"}.Error()),
shouldErr(ErrIncorrectArchiveCount{"v1", nil, nil}.Error()),
shouldNotErr,
noAssertions,
},
@ -447,7 +562,7 @@ func Test_doRun(t *testing.T) {
client.NewMock(),
},
[]artifact.Artifact{},
shouldErr(ErrNoWindows{"v1"}.Error()),
shouldErr(ErrIncorrectArchiveCount{"v1", nil, nil}.Error()),
shouldNotErr,
noAssertions,
},
@ -487,7 +602,7 @@ func Test_doRun(t *testing.T) {
config.Project{
Env: []string{"FOO=test", "BRANCH=main"},
ProjectName: "run-pipe",
Scoop: config.Scoop{
Scoops: []config.Scoop{{
Bucket: config.RepoRef{
Owner: "{{ .Env.FOO }}",
Name: "{{ .Env.FOO }}",
@ -496,7 +611,7 @@ func Test_doRun(t *testing.T) {
Folder: "scoops",
Description: "A run pipe test formula",
Homepage: "https://github.com/goreleaser",
},
}},
},
testctx.GitHubTokenType,
testctx.WithCurrentTag("v1.0.1"),
@ -519,13 +634,15 @@ func Test_doRun(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
ctx := tt.args.ctx
for _, a := range tt.artifacts {
a := a
a.Type = artifact.UploadableArchive
ctx.Artifacts.Add(&a)
}
require.NoError(t, Pipe{}.Default(ctx))
tt.assertRunError(t, doRun(ctx, tt.args.client))
tt.assertPublishError(t, doPublish(ctx, tt.args.client))
tt.assertRunError(t, runAll(ctx, tt.args.client))
if tt.assertPublishError != nil {
tt.assertPublishError(t, publishAll(ctx, tt.args.client))
}
tt.assert(t, tt.args)
})
}
@ -537,7 +654,7 @@ func TestRunPipePullRequest(t *testing.T) {
config.Project{
Dist: folder,
ProjectName: "foo",
Scoop: config.Scoop{
Scoops: []config.Scoop{{
Name: "foo",
Homepage: "https://goreleaser.com",
Description: "Fake desc",
@ -549,7 +666,7 @@ func TestRunPipePullRequest(t *testing.T) {
Enabled: true,
},
},
},
}},
},
testctx.WithVersion("1.2.1"),
testctx.WithCurrentTag("v1.2.1"),
@ -574,8 +691,8 @@ func TestRunPipePullRequest(t *testing.T) {
require.NoError(t, f.Close())
client := client.NewMock()
require.NoError(t, doRun(ctx, client))
require.NoError(t, doPublish(ctx, client))
require.NoError(t, runAll(ctx, client))
require.NoError(t, publishAll(ctx, client))
require.True(t, client.CreatedFile)
require.True(t, client.OpenedPullRequest)
golden.RequireEqualJSON(t, []byte(client.Content))
@ -712,7 +829,7 @@ func Test_buildManifest(t *testing.T) {
require.NoError(t, err)
require.NoError(t, Pipe{}.Default(ctx))
mf, err := dataFor(ctx, cl, []*artifact.Artifact{
mf, err := dataFor(ctx, ctx.Config.Scoops[0], cl, []*artifact.Artifact{
{
Name: "foo_1.0.1_windows_amd64.tar.gz",
Goos: "windows",
@ -720,13 +837,9 @@ func Test_buildManifest(t *testing.T) {
Goamd64: "v1",
Path: file,
Extra: map[string]interface{}{
artifact.ExtraBuilds: []*artifact.Artifact{
{
Name: "foo.exe",
},
{
Name: "bar.exe",
},
artifact.ExtraBinaries: []string{
"foo.exe",
"bar.exe",
},
},
},
@ -736,13 +849,9 @@ func Test_buildManifest(t *testing.T) {
Goarch: "arm",
Path: file,
Extra: map[string]interface{}{
artifact.ExtraBuilds: []*artifact.Artifact{
{
Name: "foo.exe",
},
{
Name: "bar.exe",
},
artifact.ExtraBinaries: []string{
"foo.exe",
"bar.exe",
},
},
},
@ -752,13 +861,9 @@ func Test_buildManifest(t *testing.T) {
Goarch: "386",
Path: file,
Extra: map[string]interface{}{
artifact.ExtraBuilds: []*artifact.Artifact{
{
Name: "foo.exe",
},
{
Name: "bar.exe",
},
artifact.ExtraBinaries: []string{
"foo.exe",
"bar.exe",
},
},
},
@ -833,8 +938,8 @@ func TestRunPipeScoopWithSkipUpload(t *testing.T) {
cli := client.NewMock()
require.NoError(t, Pipe{}.Default(ctx))
require.NoError(t, doRun(ctx, cli))
require.EqualError(t, doPublish(ctx, cli), `scoop.skip_upload is true`)
require.NoError(t, runAll(ctx, cli))
require.EqualError(t, publishAll(ctx, cli), `scoop.skip_upload is true`)
distFile := filepath.Join(folder, ctx.Config.Scoop.Name+".json")
_, err = os.Stat(distFile)
@ -852,7 +957,7 @@ func TestWrapInDirectory(t *testing.T) {
Download: "https://gitlab.com",
},
ProjectName: "run-pipe",
Scoop: config.Scoop{
Scoops: []config.Scoop{{
Bucket: config.RepoRef{
Owner: "test",
Name: "test",
@ -862,7 +967,7 @@ func TestWrapInDirectory(t *testing.T) {
URLTemplate: "http://gitlab.mycompany.com/foo/bar/-/releases/{{ .Tag }}/downloads/{{ .ArtifactName }}",
CommitMessageTemplate: "chore(scoop): update {{ .ProjectName }} version {{ .Tag }}",
Persist: []string{"data.cfg", "etc"},
},
}},
},
testctx.GitHubTokenType,
testctx.WithCurrentTag("v1.0.1"),
@ -872,7 +977,7 @@ func TestWrapInDirectory(t *testing.T) {
require.NoError(t, Pipe{}.Default(ctx))
cl, err := client.New(ctx)
require.NoError(t, err)
mf, err := dataFor(ctx, cl, []*artifact.Artifact{
mf, err := dataFor(ctx, ctx.Config.Scoops[0], cl, []*artifact.Artifact{
{
Name: "foo_1.0.1_windows_amd64.tar.gz",
Goos: "windows",
@ -881,13 +986,9 @@ func TestWrapInDirectory(t *testing.T) {
Path: file,
Extra: map[string]interface{}{
artifact.ExtraWrappedIn: "foo_1.0.1_windows_amd64",
artifact.ExtraBuilds: []*artifact.Artifact{
{
Name: "foo.exe",
},
{
Name: "bar.exe",
},
artifact.ExtraBinaries: []string{
"foo.exe",
"bar.exe",
},
},
},

View File

@ -5,6 +5,11 @@
"url": "https://dummyhost/download/v1.0.1/foo_1.0.1_windows_386.tar.gz",
"bin": null,
"hash": "5e2bf57d3f40c4b6df69daf1936cb766f832374b4fc0259a7cbff06e2f70f269"
},
"64bit": {
"url": "https://dummyhost/download/v1.0.1/foo_1.0.1_windows_amd64.tar.gz",
"bin": null,
"hash": "5e2bf57d3f40c4b6df69daf1936cb766f832374b4fc0259a7cbff06e2f70f269"
}
},
"homepage": "https://github.com/goreleaser",

View File

@ -0,0 +1,15 @@
{
"version": "1.0.1",
"architecture": {
"64bit": {
"url": "https://dummyhost/download/v1.0.1/foos_1.0.1_windows_amd64.tar.gz",
"bin": [
"bin4",
"bin3"
],
"hash": "5e2bf57d3f40c4b6df69daf1936cb766f832374b4fc0259a7cbff06e2f70f269"
}
},
"homepage": "https://github.com/goreleaser",
"description": "A run pipe test formula"
}

View File

@ -0,0 +1,17 @@
{
"version": "1.0.1",
"architecture": {
"32bit": {
"url": "https://dummyhost/download/v1.0.1/foo_1.0.1_windows_386.tar.gz",
"bin": null,
"hash": "5e2bf57d3f40c4b6df69daf1936cb766f832374b4fc0259a7cbff06e2f70f269"
},
"64bit": {
"url": "https://dummyhost/download/v1.0.1/foo_1.0.1_windows_amd64.tar.gz",
"bin": null,
"hash": "5e2bf57d3f40c4b6df69daf1936cb766f832374b4fc0259a7cbff06e2f70f269"
}
},
"homepage": "https://github.com/goreleaser",
"description": "A run pipe test formula"
}

View File

@ -239,6 +239,7 @@ type Ko struct {
// Scoop contains the scoop.sh section.
type Scoop struct {
Name string `yaml:"name,omitempty" json:"name,omitempty"`
IDs []string `yaml:"ids,omitempty" json:"ids,omitempty"`
Bucket RepoRef `yaml:"bucket,omitempty" json:"bucket,omitempty"`
Folder string `yaml:"folder,omitempty" json:"folder,omitempty"`
CommitAuthor CommitAuthor `yaml:"commit_author,omitempty" json:"commit_author,omitempty"`
@ -957,7 +958,8 @@ type Project struct {
AURs []AUR `yaml:"aurs,omitempty" json:"aurs,omitempty"`
Krews []Krew `yaml:"krews,omitempty" json:"krews,omitempty"`
Kos []Ko `yaml:"kos,omitempty" json:"kos,omitempty"`
Scoop Scoop `yaml:"scoop,omitempty" json:"scoop,omitempty"`
Scoop Scoop `yaml:"scoop,omitempty" json:"scoop,omitempty"` // deprecated
Scoops []Scoop `yaml:"scoops,omitempty" json:"scoops,omitempty"`
Builds []Build `yaml:"builds,omitempty" json:"builds,omitempty"`
Archives []Archive `yaml:"archives,omitempty" json:"archives,omitempty"`
NFPMs []NFPM `yaml:"nfpms,omitempty" json:"nfpms,omitempty"`

View File

@ -8,7 +8,9 @@ commented example below:
```yaml
# .goreleaser.yaml
scoop:
# Since: v1.18
scoops:
-
# URL which is determined by the given Token (github or gitlab)
#
# Default:

View File

@ -37,6 +37,27 @@ Description.
-->
### scoop
> since 2023-04-30 (v1.18.0)
GoReleaser now allows many `scoop` configurations, so it should be pluralized
[accordingly](/customization/scoop).
=== "Before"
``` yaml
scoop:
# ...
```
=== "After"
``` yaml
scoops:
- # ...
```
### build
> since 2023-02-09 (v1.16.0)

View File

@ -1,7 +1,11 @@
# Scoop requires a windows archive
# Scoop requires single a windows archive
The Scoop pipe requires a Windows build and archive.
Usually, if you see this error, one of these 2 things probably happened:
## 1. Using binary archive format
The archive should not be in `binary` format.
For instance, this won't work:
@ -19,5 +23,28 @@ archives:
- format: zip
```
## 2. Multiple archives for the same GOOS/GOARCH
If you build multiple binaries and ship them in multiple archives, for example,
one for the _client_ and another one for the _server_ of a given project, you
will need to have multiple `scoops` in your configuration as well.
Scoops only allow to install a single archive per manifest, so we need to do
something like this:
```yaml
scoops:
- ids: [ client ]
name: foo
# ...
- ids: [ server ]
name: food
# ...
```
## Footnotes
Also notice the `goamd64` options, it must match the one from your build.
By default, only `GOAMD64` `v1` is built.
Please refer to the [documentation](/customization/scoop) for more details.