diff --git a/internal/builders/golang/build.go b/internal/builders/golang/build.go index 6de6b0fc870..3f6ab6cc2d5 100644 --- a/internal/builders/golang/build.go +++ b/internal/builders/golang/build.go @@ -34,7 +34,7 @@ func init() { type Builder struct{} // WithDefaults sets the defaults for a golang build and returns it. -func (*Builder) WithDefaults(build config.Build) config.Build { +func (*Builder) WithDefaults(build config.Build) (config.Build, error) { if build.Dir == "" { build.Dir = "." } @@ -54,12 +54,16 @@ func (*Builder) WithDefaults(build config.Build) config.Build { if len(build.Goarm) == 0 { build.Goarm = []string{"6"} } - build.Targets = matrix(build) + targets, err := matrix(build) + build.Targets = targets + if err != nil { + return build, err + } } if build.GoBinary == "" { build.GoBinary = "go" } - return build + return build, nil } // Build builds a golang build. diff --git a/internal/builders/golang/build_test.go b/internal/builders/golang/build_test.go index cb0ff68e72f..123dcbb57a7 100644 --- a/internal/builders/golang/build_test.go +++ b/internal/builders/golang/build_test.go @@ -94,13 +94,60 @@ func TestWithDefaults(t *testing.T) { } var ctx = context.New(config) ctx.Git.CurrentTag = "5.6.7" - var build = Default.WithDefaults(ctx.Config.Builds[0]) + build, err := Default.WithDefaults(ctx.Config.Builds[0]) + require.NoError(t, err) require.ElementsMatch(t, build.Targets, testcase.targets) require.EqualValues(t, testcase.goBinary, build.GoBinary) }) } } +func TestInvalidTargets(t *testing.T) { + type testcase struct { + build config.Build + expectedErr string + } + for s, tc := range map[string]testcase{ + "goos": { + build: config.Build{ + Goos: []string{"darwin", "darwim"}, + }, + expectedErr: "invalid goos: darwim", + }, + "goarch": { + build: config.Build{ + Goarch: []string{"amd64", "i386", "386"}, + }, + expectedErr: "invalid goarch: i386", + }, + "goarm": { + build: config.Build{ + Goarch: []string{"arm"}, + Goarm: []string{"6", "9", "8", "7"}, + }, + expectedErr: "invalid goarm: 9", + }, + "gomips": { + build: config.Build{ + Goarch: []string{"mips"}, + Gomips: []string{"softfloat", "mehfloat", "hardfloat"}, + }, + expectedErr: "invalid gomips: mehfloat", + }, + } { + t.Run(s, func(t *testing.T) { + var config = config.Project{ + Builds: []config.Build{ + tc.build, + }, + } + var ctx = context.New(config) + _, err := Default.WithDefaults(ctx.Config.Builds[0]) + require.EqualError(t, err, tc.expectedErr) + }) + } +} + func TestBuild(t *testing.T) { folder, back := testlib.Mktmp(t) defer back() diff --git a/internal/builders/golang/targets.go b/internal/builders/golang/targets.go index 14b776b4b29..271c89eb203 100644 --- a/internal/builders/golang/targets.go +++ b/internal/builders/golang/targets.go @@ -22,10 +22,24 @@ func (t target) String() string { return fmt.Sprintf("%s_%s", t.os, t.arch) } -func matrix(build config.Build) (result []string) { +func matrix(build config.Build) ([]string, error) { // nolint:prealloc var targets []target + // nolint:prealloc + var result []string for _, target := range allBuildTargets(build) { + if !contains(target.os, validGoos) { + return result, fmt.Errorf("invalid goos: %s", target.os) + } + if !contains(target.arch, validGoarch) { + return result, fmt.Errorf("invalid goarch: %s", target.arch) + } + if target.arm != "" && !contains(target.arm, validGoarm) { + return result, fmt.Errorf("invalid goarm: %s", target.arm) + } + if target.mips != "" && !contains(target.mips, validGomips) { + return result, fmt.Errorf("invalid gomips: %s", target.mips) + } if !valid(target) { log.WithField("target", target). Debug("skipped invalid build") @@ -41,7 +55,7 @@ func matrix(build config.Build) (result []string) { for _, target := range targets { result = append(result, target.String()) } - return + return result, nil } func allBuildTargets(build config.Build) (targets []target) { @@ -98,56 +112,95 @@ func ignored(build config.Build, target target) bool { } func valid(target target) bool { - var s = target.os + target.arch - for _, a := range validTargets { - if a == s { + return contains(target.os+target.arch, validTargets) +} + +func contains(s string, ss []string) bool { + for _, z := range ss { + if z == s { return true } } return false } -// list from https://golang.org/doc/install/source#environment +// lists from https://golang.org/doc/install/source#environment // nolint: gochecknoglobals -var validTargets = []string{ - "aixppc64", - "android386", - "androidamd64", - "androidarm", - "androidarm64", - // "darwin386", - deprecated on latest go 1.15+ - "darwinamd64", - // "darwinarm", - requires admin rights and other ios stuff - // "darwinarm64", - requires admin rights and other ios stuff - "dragonflyamd64", - "freebsd386", - "freebsdamd64", - "freebsdarm", - "freebsdarm64", // not on the official list for some reason, yet its supported on go 1.14+ - "illumosamd64", - "jswasm", - "linux386", - "linuxamd64", - "linuxarm", - "linuxarm64", - "linuxppc64", - "linuxppc64le", - "linuxmips", - "linuxmipsle", - "linuxmips64", - "linuxmips64le", - "linuxs390x", - "netbsd386", - "netbsdamd64", - "netbsdarm", - "openbsd386", - "openbsdamd64", - "openbsdarm", - "openbsdarm64", - "plan9386", - "plan9amd64", - "plan9arm", - "solarisamd64", - "windows386", - "windowsamd64", -} +var ( + validTargets = []string{ + "aixppc64", + "android386", + "androidamd64", + "androidarm", + "androidarm64", + // "darwin386", - deprecated on latest go 1.15+ + "darwinamd64", + // "darwinarm", - requires admin rights and other ios stuff + // "darwinarm64", - requires admin rights and other ios stuff + "dragonflyamd64", + "freebsd386", + "freebsdamd64", + "freebsdarm", + "freebsdarm64", // not on the official list for some reason, yet its supported on go 1.14+ + "illumosamd64", + "jswasm", + "linux386", + "linuxamd64", + "linuxarm", + "linuxarm64", + "linuxppc64", + "linuxppc64le", + "linuxmips", + "linuxmipsle", + "linuxmips64", + "linuxmips64le", + "linuxs390x", + "netbsd386", + "netbsdamd64", + "netbsdarm", + "openbsd386", + "openbsdamd64", + "openbsdarm", + "openbsdarm64", + "plan9386", + "plan9amd64", + "plan9arm", + "solarisamd64", + "windows386", + "windowsamd64", + } + + validGoos = []string{ + "aix", + "android", + "darwin", + "dragonfly", + "freebsd", + "illumos", + "js", + "linux", + "netbsd", + "openbsd", + "plan9", + "solaris", + "windows", + } + + validGoarch = []string{ + "386", + "amd64", + "arm", + "arm64", + "mips", + "mips64", + "mips64le", + "mipsle", + "ppc64", + "ppc64le", + "s390x", + "wasm", + } + + validGoarm = []string{"5", "6", "7"} + validGomips = []string{"hardfloat", "softfloat"} +) diff --git a/internal/builders/golang/targets_test.go b/internal/builders/golang/targets_test.go index cda0450bfc1..e59d1be2dbb 100644 --- a/internal/builders/golang/targets_test.go +++ b/internal/builders/golang/targets_test.go @@ -53,6 +53,8 @@ func TestAllBuildTargets(t *testing.T) { }, }, } + result, err := matrix(build) + require.NoError(t, err) require.Equal(t, []string{ "linux_386", "linux_amd64", @@ -74,7 +76,7 @@ func TestAllBuildTargets(t *testing.T) { "openbsd_amd64", "openbsd_arm64", "js_wasm", - }, matrix(build)) + }, result) } func TestGoosGoarchCombos(t *testing.T) { diff --git a/internal/pipe/build/build.go b/internal/pipe/build/build.go index 028e81807aa..775734af5e0 100644 --- a/internal/pipe/build/build.go +++ b/internal/pipe/build/build.go @@ -51,18 +51,24 @@ func (Pipe) Run(ctx *context.Context) error { func (Pipe) Default(ctx *context.Context) error { var ids = ids.New("builds") for i, build := range ctx.Config.Builds { - ctx.Config.Builds[i] = buildWithDefaults(ctx, build) + build, err := buildWithDefaults(ctx, build) + if err != nil { + return err + } + ctx.Config.Builds[i] = build ids.Inc(ctx.Config.Builds[i].ID) } if len(ctx.Config.Builds) == 0 { - ctx.Config.Builds = []config.Build{ - buildWithDefaults(ctx, ctx.Config.SingleBuild), + build, err := buildWithDefaults(ctx, ctx.Config.SingleBuild) + if err != nil { + return err } + ctx.Config.Builds = []config.Build{build} } return ids.Validate() } -func buildWithDefaults(ctx *context.Context, build config.Build) config.Build { +func buildWithDefaults(ctx *context.Context, build config.Build) (config.Build, error) { if build.Lang == "" { build.Lang = "go" } diff --git a/internal/pipe/build/build_test.go b/internal/pipe/build/build_test.go index 074a1fb67ac..caabc715ce1 100644 --- a/internal/pipe/build/build_test.go +++ b/internal/pipe/build/build_test.go @@ -17,16 +17,21 @@ import ( "github.com/stretchr/testify/require" ) +var errFailedBuild = errors.New("fake builder failed") +var errFailedDefault = errors.New("fake builder defaults failed") + type fakeBuilder struct { - fail bool + fail bool + failDefault bool } -func (*fakeBuilder) WithDefaults(build config.Build) config.Build { - return build +func (f *fakeBuilder) WithDefaults(build config.Build) (config.Build, error) { + if f.failDefault { + return build, errFailedDefault + } + return build, nil } -var errFailedBuild = errors.New("fake builder failed") - func (f *fakeBuilder) Build(ctx *context.Context, build config.Build, options api.Options) error { if f.fail { return errFailedBuild @@ -48,6 +53,9 @@ func init() { api.Register("fakeFail", &fakeBuilder{ fail: true, }) + api.Register("fakeFailDefault", &fakeBuilder{ + failDefault: true, + }) } func TestPipeDescription(t *testing.T) { @@ -212,6 +220,22 @@ func TestDefaultNoBuilds(t *testing.T) { require.NoError(t, Pipe{}.Default(ctx)) } +func TestDefaultFail(t *testing.T) { + folder, back := testlib.Mktmp(t) + defer back() + var config = config.Project{ + Dist: folder, + Builds: []config.Build{ + { + Lang: "fakeFailDefault", + }, + }, + } + var ctx = context.New(config) + require.EqualError(t, Pipe{}.Default(ctx), errFailedDefault.Error()) + require.Empty(t, ctx.Artifacts.List()) +} + func TestDefaultExpandEnv(t *testing.T) { require.NoError(t, os.Setenv("XBAR", "FOOBAR")) var ctx = &context.Context{ @@ -351,6 +375,20 @@ func TestDefaultFillSingleBuild(t *testing.T) { require.Equal(t, ctx.Config.Builds[0].Binary, "foo") } +func TestDefaultFailSingleBuild(t *testing.T) { + folder, back := testlib.Mktmp(t) + defer back() + var config = config.Project{ + Dist: folder, + SingleBuild: config.Build{ + Lang: "fakeFailDefault", + }, + } + var ctx = context.New(config) + require.EqualError(t, Pipe{}.Default(ctx), errFailedDefault.Error()) + require.Empty(t, ctx.Artifacts.List()) +} + func TestSkipBuild(t *testing.T) { folder, back := testlib.Mktmp(t) defer back() diff --git a/pkg/build/build.go b/pkg/build/build.go index 464ac714d93..6c7f5041ab3 100644 --- a/pkg/build/build.go +++ b/pkg/build/build.go @@ -33,6 +33,6 @@ type Options struct { // Builder defines a builder. type Builder interface { - WithDefaults(build config.Build) config.Build + WithDefaults(build config.Build) (config.Build, error) Build(ctx *context.Context, build config.Build, options Options) error } diff --git a/pkg/build/build_test.go b/pkg/build/build_test.go index a0d396d1ccc..33f8b20d3a4 100644 --- a/pkg/build/build_test.go +++ b/pkg/build/build_test.go @@ -10,8 +10,8 @@ import ( type dummy struct{} -func (*dummy) WithDefaults(build config.Build) config.Build { - return build +func (*dummy) WithDefaults(build config.Build) (config.Build, error) { + return build, nil } func (*dummy) Build(ctx *context.Context, build config.Build, options Options) error { return nil