mirror of
https://github.com/goreleaser/goreleaser.git
synced 2024-12-31 01:53:50 +02:00
feat: improve --single-target (#4442)
closes #4437 closes #4426 --------- Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
This commit is contained in:
parent
6bce81c0be
commit
25a054c5e1
57
cmd/build.go
57
cmd/build.go
@ -2,7 +2,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
@ -22,7 +21,6 @@ import (
|
||||
"github.com/goreleaser/goreleaser/pkg/config"
|
||||
"github.com/goreleaser/goreleaser/pkg/context"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type buildCmd struct {
|
||||
@ -125,7 +123,7 @@ When using ` + "`--single-target`" + `, the ` + "`GOOS`" + ` and ` + "`GOARCH`"
|
||||
&root.opts.skips,
|
||||
"skip",
|
||||
nil,
|
||||
fmt.Sprintf("Skip the given options (valid options are %s)", skips.Build.String()),
|
||||
fmt.Sprintf("Skip the given options (valid options are: %s)", skips.Build.String()),
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc("skip", func(_ *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return skips.Build.Complete(toComplete), cobra.ShellCompDirectiveDefault
|
||||
@ -176,6 +174,7 @@ func setupBuildContext(ctx *context.Context, options buildOpts) error {
|
||||
}
|
||||
log.Debugf("parallelism: %v", ctx.Parallelism)
|
||||
ctx.Snapshot = options.snapshot
|
||||
|
||||
if err := skips.SetBuild(ctx, options.skips...); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -205,9 +204,7 @@ func setupBuildContext(ctx *context.Context, options buildOpts) error {
|
||||
ctx.Clean = options.clean || options.rmDist
|
||||
|
||||
if options.singleTarget {
|
||||
if err := setupBuildSingleTarget(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Partial = true
|
||||
}
|
||||
|
||||
if len(options.ids) > 0 {
|
||||
@ -226,54 +223,6 @@ func setupBuildContext(ctx *context.Context, options buildOpts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupBuildSingleTarget(ctx *context.Context) error {
|
||||
goos := os.Getenv("GOOS")
|
||||
if goos == "" {
|
||||
goos = runtime.GOOS
|
||||
}
|
||||
goarch := os.Getenv("GOARCH")
|
||||
if goarch == "" {
|
||||
goarch = runtime.GOARCH
|
||||
}
|
||||
log.WithField("reason", "single target is enabled").Warnf("building only for %s/%s", goos, goarch)
|
||||
if len(ctx.Config.Builds) == 0 {
|
||||
ctx.Config.Builds = append(ctx.Config.Builds, config.Build{})
|
||||
}
|
||||
var keep []config.Build
|
||||
for _, build := range ctx.Config.Builds {
|
||||
if !shouldBuild(build, goos, goarch) {
|
||||
continue
|
||||
}
|
||||
build.Goos = []string{goos}
|
||||
build.Goarch = []string{goarch}
|
||||
build.Goarm = nil
|
||||
build.Gomips = nil
|
||||
build.Goamd64 = nil
|
||||
build.Targets = nil
|
||||
keep = append(keep, build)
|
||||
}
|
||||
|
||||
ctx.Config.Builds = keep
|
||||
ctx.Config.UniversalBinaries = nil
|
||||
|
||||
if len(keep) == 0 {
|
||||
return fmt.Errorf("no builds matching --single-target %s/%s", goos, goarch)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func shouldBuild(build config.Build, goos, goarch string) bool {
|
||||
if len(build.Targets) > 0 {
|
||||
return slices.ContainsFunc(build.Targets, func(e string) bool {
|
||||
return strings.HasPrefix(e, fmt.Sprintf("%s_%s", goos, goarch))
|
||||
})
|
||||
}
|
||||
return (len(build.Goos) == 0 && len(build.Goarch) == 0) ||
|
||||
(slices.Contains(build.Goos, goos) &&
|
||||
slices.Contains(build.Goarch, goarch))
|
||||
}
|
||||
|
||||
func setupBuildID(ctx *context.Context, ids []string) error {
|
||||
if len(ctx.Config.Builds) < 2 {
|
||||
log.Warn("single build in config, '--id' ignored")
|
||||
|
@ -1,7 +1,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/goreleaser/goreleaser/internal/pipeline"
|
||||
@ -133,17 +132,7 @@ func TestSetupPipeline(t *testing.T) {
|
||||
|
||||
func TestBuildFlags(t *testing.T) {
|
||||
setup := func(opts buildOpts) *context.Context {
|
||||
ctx := testctx.NewWithCfg(config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Goos: []string{runtime.GOOS},
|
||||
Goarch: []string{runtime.GOARCH},
|
||||
},
|
||||
{
|
||||
Targets: []string{"linux_arm64"},
|
||||
},
|
||||
},
|
||||
})
|
||||
ctx := testctx.New()
|
||||
require.NoError(t, setupBuildContext(ctx, opts))
|
||||
return ctx
|
||||
}
|
||||
@ -189,49 +178,6 @@ func TestBuildFlags(t *testing.T) {
|
||||
}).Clean)
|
||||
})
|
||||
|
||||
t.Run("single-target", func(t *testing.T) {
|
||||
opts := buildOpts{
|
||||
singleTarget: true,
|
||||
}
|
||||
|
||||
t.Run("runtime", func(t *testing.T) {
|
||||
result := setup(opts)
|
||||
require.Len(t, result.Config.Builds, 1)
|
||||
require.Equal(t, []string{runtime.GOOS}, result.Config.Builds[0].Goos)
|
||||
require.Equal(t, []string{runtime.GOARCH}, result.Config.Builds[0].Goarch)
|
||||
})
|
||||
|
||||
t.Run("no matches", func(t *testing.T) {
|
||||
t.Setenv("GOOS", "windows")
|
||||
t.Setenv("GOARCH", "arm64")
|
||||
ctx := testctx.NewWithCfg(config.Project{
|
||||
Builds: []config.Build{{
|
||||
Goos: []string{"linux"},
|
||||
}},
|
||||
})
|
||||
require.EqualError(t, setupBuildContext(ctx, opts), "no builds matching --single-target windows/arm64")
|
||||
})
|
||||
|
||||
t.Run("default config", func(t *testing.T) {
|
||||
ctx := testctx.NewWithCfg(config.Project{
|
||||
Builds: []config.Build{{}},
|
||||
})
|
||||
require.NoError(t, setupBuildContext(ctx, opts))
|
||||
require.Len(t, ctx.Config.Builds, 1)
|
||||
require.Equal(t, []string{runtime.GOOS}, ctx.Config.Builds[0].Goos)
|
||||
require.Equal(t, []string{runtime.GOARCH}, ctx.Config.Builds[0].Goarch)
|
||||
})
|
||||
|
||||
t.Run("from env", func(t *testing.T) {
|
||||
t.Setenv("GOOS", "linux")
|
||||
t.Setenv("GOARCH", "arm64")
|
||||
result := setup(opts)
|
||||
require.Len(t, result.Config.Builds, 1)
|
||||
require.Equal(t, []string{"linux"}, result.Config.Builds[0].Goos)
|
||||
require.Equal(t, []string{"arm64"}, result.Config.Builds[0].Goarch)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("id", func(t *testing.T) {
|
||||
t.Run("match", func(t *testing.T) {
|
||||
ctx := testctx.NewWithCfg(config.Project{
|
||||
@ -318,74 +264,3 @@ func TestBuildFlags(t *testing.T) {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuildSingleTargetWithSpecificTargets(t *testing.T) {
|
||||
ctx := testctx.NewWithCfg(config.Project{
|
||||
ProjectName: "test",
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Targets: []string{
|
||||
"linux_amd64_v1",
|
||||
"darwin_arm64",
|
||||
"darwin_amd64_v1",
|
||||
},
|
||||
},
|
||||
},
|
||||
UniversalBinaries: []config.UniversalBinary{
|
||||
{Replace: true},
|
||||
},
|
||||
})
|
||||
|
||||
t.Setenv("GOOS", "darwin")
|
||||
t.Setenv("GOARCH", "amd64")
|
||||
setupBuildSingleTarget(ctx)
|
||||
require.Len(t, ctx.Config.Builds, 1)
|
||||
require.Equal(t, config.Build{
|
||||
Goos: []string{"darwin"},
|
||||
Goarch: []string{"amd64"},
|
||||
}, ctx.Config.Builds[0])
|
||||
require.Nil(t, ctx.Config.UniversalBinaries)
|
||||
}
|
||||
|
||||
func TestBuildSingleTargetNoMatch(t *testing.T) {
|
||||
ctx := testctx.NewWithCfg(config.Project{
|
||||
ProjectName: "test",
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Goos: []string{"linux", "darwin"},
|
||||
Goarch: []string{"amd64", "arm64"},
|
||||
Goamd64: []string{"v1", "v2"},
|
||||
Goarm: []string{"6"},
|
||||
Gomips: []string{"anything"},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
t.Setenv("GOOS", "windows")
|
||||
t.Setenv("GOARCH", "amd64")
|
||||
require.Error(t, setupBuildSingleTarget(ctx))
|
||||
require.Empty(t, ctx.Config.Builds)
|
||||
}
|
||||
|
||||
func TestBuildSingleTargetRemoveOtherOptions(t *testing.T) {
|
||||
ctx := testctx.NewWithCfg(config.Project{
|
||||
ProjectName: "test",
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Goos: []string{"linux", "darwin"},
|
||||
Goarch: []string{"amd64", "arm64"},
|
||||
Goamd64: []string{"v1", "v2"},
|
||||
Goarm: []string{"6"},
|
||||
Gomips: []string{"anything"},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
t.Setenv("GOOS", "linux")
|
||||
t.Setenv("GOARCH", "amd64")
|
||||
setupBuildSingleTarget(ctx)
|
||||
require.Equal(t, config.Build{
|
||||
Goos: []string{"linux"},
|
||||
Goarch: []string{"amd64"},
|
||||
}, ctx.Config.Builds[0])
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ func buildWithDefaults(ctx *context.Context, build config.Build) (config.Build,
|
||||
}
|
||||
|
||||
func runPipeOnBuild(ctx *context.Context, g semerrgroup.Group, build config.Build) {
|
||||
for _, target := range build.Targets {
|
||||
for _, target := range filter(ctx, build.Targets) {
|
||||
target := target
|
||||
build := build
|
||||
g.Go(func() error {
|
||||
|
28
internal/pipe/build/filter.go
Normal file
28
internal/pipe/build/filter.go
Normal file
@ -0,0 +1,28 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/caarlos0/log"
|
||||
"github.com/goreleaser/goreleaser/pkg/context"
|
||||
)
|
||||
|
||||
func filter(ctx *context.Context, targets []string) []string {
|
||||
if !ctx.Partial {
|
||||
return targets
|
||||
}
|
||||
|
||||
target := ctx.PartialTarget
|
||||
log.WithField("match", fmt.Sprintf("target=%s", target)).Infof("partial build")
|
||||
|
||||
var result []string
|
||||
for _, t := range targets {
|
||||
if !strings.HasPrefix(t, target) {
|
||||
continue
|
||||
}
|
||||
result = append(result, t)
|
||||
break
|
||||
}
|
||||
return result
|
||||
}
|
43
internal/pipe/build/filter_test.go
Normal file
43
internal/pipe/build/filter_test.go
Normal file
@ -0,0 +1,43 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/goreleaser/goreleaser/internal/testctx"
|
||||
"github.com/goreleaser/goreleaser/pkg/context"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var filterTestTargets = []string{
|
||||
"linux_amd64_v1",
|
||||
"linux_arm64",
|
||||
"linux_riscv64",
|
||||
"darwin_amd64_v1",
|
||||
"darwin_amd64_v2",
|
||||
"darwin_arm64",
|
||||
}
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
t.Run("none", func(t *testing.T) {
|
||||
ctx := testctx.New()
|
||||
require.Equal(t, filterTestTargets, filter(ctx, filterTestTargets))
|
||||
})
|
||||
|
||||
t.Run("target", func(t *testing.T) {
|
||||
ctx := testctx.New(func(ctx *context.Context) {
|
||||
ctx.Partial = true
|
||||
ctx.PartialTarget = "darwin_amd64"
|
||||
})
|
||||
require.Equal(t, []string{
|
||||
"darwin_amd64_v1",
|
||||
}, filter(ctx, filterTestTargets))
|
||||
})
|
||||
|
||||
t.Run("target no match", func(t *testing.T) {
|
||||
ctx := testctx.New(func(ctx *context.Context) {
|
||||
ctx.Partial = true
|
||||
ctx.PartialTarget = "linux_amd64_v1"
|
||||
})
|
||||
require.Empty(t, filter(ctx, []string{"darwin_amd64_v1"}))
|
||||
})
|
||||
}
|
25
internal/pipe/partial/partial.go
Normal file
25
internal/pipe/partial/partial.go
Normal file
@ -0,0 +1,25 @@
|
||||
package partial
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/charmbracelet/x/exp/ordered"
|
||||
"github.com/goreleaser/goreleaser/pkg/context"
|
||||
)
|
||||
|
||||
type Pipe struct{}
|
||||
|
||||
func (Pipe) String() string { return "partial" }
|
||||
func (Pipe) Skip(ctx *context.Context) bool { return !ctx.Partial }
|
||||
|
||||
func (Pipe) Run(ctx *context.Context) error {
|
||||
ctx.PartialTarget = getFilter()
|
||||
return nil
|
||||
}
|
||||
|
||||
func getFilter() string {
|
||||
goos := ordered.First(os.Getenv("GGOOS"), os.Getenv("GOOS"), runtime.GOOS)
|
||||
goarch := ordered.First(os.Getenv("GGOARCH"), os.Getenv("GOARCH"), runtime.GOARCH)
|
||||
return goos + "_" + goarch
|
||||
}
|
57
internal/pipe/partial/partial_test.go
Normal file
57
internal/pipe/partial/partial_test.go
Normal file
@ -0,0 +1,57 @@
|
||||
package partial
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/goreleaser/goreleaser/internal/testctx"
|
||||
"github.com/goreleaser/goreleaser/pkg/config"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var pipe = Pipe{}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
require.NotEmpty(t, pipe.String())
|
||||
}
|
||||
|
||||
func TestSkip(t *testing.T) {
|
||||
t.Run("partial", func(t *testing.T) {
|
||||
ctx := testctx.New(testctx.Partial)
|
||||
require.False(t, pipe.Skip(ctx))
|
||||
})
|
||||
|
||||
t.Run("full", func(t *testing.T) {
|
||||
require.True(t, pipe.Skip(testctx.New()))
|
||||
})
|
||||
}
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
t.Run("target", func(t *testing.T) {
|
||||
ctx := testctx.NewWithCfg(config.Project{
|
||||
Dist: "dist",
|
||||
}, testctx.Partial)
|
||||
t.Setenv("GOOS", "windows")
|
||||
t.Setenv("GOARCH", "arm64")
|
||||
require.NoError(t, pipe.Run(ctx))
|
||||
require.Equal(t, "windows_arm64", ctx.PartialTarget)
|
||||
})
|
||||
t.Run("using GGOOS and GGOARCH", func(t *testing.T) {
|
||||
ctx := testctx.NewWithCfg(config.Project{
|
||||
Dist: "dist",
|
||||
}, testctx.Partial)
|
||||
t.Setenv("GGOOS", "windows")
|
||||
t.Setenv("GGOARCH", "arm64")
|
||||
require.NoError(t, pipe.Run(ctx))
|
||||
require.Equal(t, "windows_arm64", ctx.PartialTarget)
|
||||
})
|
||||
t.Run("using runtime", func(t *testing.T) {
|
||||
ctx := testctx.NewWithCfg(config.Project{
|
||||
Dist: "dist",
|
||||
}, testctx.Partial)
|
||||
require.NoError(t, pipe.Run(ctx))
|
||||
target := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH)
|
||||
require.Equal(t, target, ctx.PartialTarget)
|
||||
})
|
||||
}
|
@ -24,6 +24,7 @@ import (
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/metadata"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/nfpm"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/nix"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/partial"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/prebuild"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/publish"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/reportsizes"
|
||||
@ -59,6 +60,8 @@ var BuildPipeline = []Piper{
|
||||
semver.Pipe{},
|
||||
// load default configs
|
||||
defaults.Pipe{},
|
||||
// setup things for partial builds/releases
|
||||
partial.Pipe{},
|
||||
// snapshot version handling
|
||||
snapshot.Pipe{},
|
||||
// run global hooks before build
|
||||
|
@ -116,6 +116,10 @@ func Snapshot(ctx *context.Context) {
|
||||
ctx.Snapshot = true
|
||||
}
|
||||
|
||||
func Partial(ctx *context.Context) {
|
||||
ctx.Partial = true
|
||||
}
|
||||
|
||||
func NewWithCfg(c config.Project, opts ...Opt) *context.Context {
|
||||
ctx := context.New(c)
|
||||
for _, opt := range opts {
|
||||
|
@ -89,8 +89,10 @@ type Context struct {
|
||||
ReleaseFooterTmpl string
|
||||
Version string
|
||||
ModulePath string
|
||||
PartialTarget string
|
||||
Snapshot bool
|
||||
FailFast bool
|
||||
Partial bool
|
||||
SkipTokenCheck bool
|
||||
Clean bool
|
||||
PreRelease bool
|
||||
|
@ -20,6 +20,7 @@ Other things you might need to run the tests:
|
||||
- [Podman](https://podman.io/)
|
||||
- [Snapcraft](https://snapcraft.io/)
|
||||
- [Syft](https://github.com/anchore/syft)
|
||||
- [upx](https://upx.github.io/)
|
||||
|
||||
Clone `goreleaser` anywhere:
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user