From c03efa73aa58a482d0b1789f1d8044ff96b776a9 Mon Sep 17 00:00:00 2001 From: Carlos A Becker Date: Sun, 24 Apr 2022 22:24:27 -0300 Subject: [PATCH 1/2] feat: add builds.no_main_check and builds.command Added two more options to the builds section: - command: allow to override the command being run. E.g. instead of `go build`, one could run `go test -c` by setting gobinary, command and flags, respectively. - no_main_check: by default, goreleaser will fail if there's no main function. This option allows to disable that check. This PR effectively make GoReleaser able to release Go test binaries instead of just "regular" binaries. closes #3037 Signed-off-by: Carlos A Becker --- internal/builders/golang/build.go | 8 +++- internal/builders/golang/build_test.go | 61 ++++++++++++++++++++++---- pkg/config/config.go | 2 + www/docs/customization/build.md | 19 +++++++- 4 files changed, 78 insertions(+), 12 deletions(-) diff --git a/internal/builders/golang/build.go b/internal/builders/golang/build.go index bd6fd40d9c2..da5e04a8b8e 100644 --- a/internal/builders/golang/build.go +++ b/internal/builders/golang/build.go @@ -39,6 +39,9 @@ func (*Builder) WithDefaults(build config.Build) (config.Build, error) { if build.GoBinary == "" { build.GoBinary = "go" } + if build.Command == "" { + build.Command = "build" + } if build.Dir == "" { build.Dir = "." } @@ -177,7 +180,7 @@ func withOverrides(ctx *context.Context, build config.Build, options api.Options } func buildGoBuildLine(ctx *context.Context, build config.Build, options api.Options, artifact *artifact.Artifact, env []string) ([]string, error) { - cmd := []string{build.GoBinary, "build"} + cmd := []string{build.GoBinary, build.Command} details, err := withOverrides(ctx, build, options) if err != nil { @@ -255,6 +258,9 @@ func run(ctx *context.Context, command, env []string, dir string) error { } func checkMain(build config.Build) error { + if build.NoMainCheck { + return nil + } main := build.Main if build.UnproxiedMain != "" { main = build.UnproxiedMain diff --git a/internal/builders/golang/build_test.go b/internal/builders/golang/build_test.go index cf54b0d3239..e43d4ebf7ac 100644 --- a/internal/builders/golang/build_test.go +++ b/internal/builders/golang/build_test.go @@ -205,6 +205,21 @@ func TestWithDefaults(t *testing.T) { } } +func TestDefaults(t *testing.T) { + t.Run("command not set", func(t *testing.T) { + build, err := Default.WithDefaults(config.Build{}) + require.NoError(t, err) + require.Equal(t, "build", build.Command) + }) + t.Run("command set", func(t *testing.T) { + build, err := Default.WithDefaults(config.Build{ + Command: "test", + }) + require.NoError(t, err) + require.Equal(t, "test", build.Command) + }) +} + // createFakeGoBinaryWithVersion creates a temporary executable with the // given name, which will output a go version string with the given version. // The temporary directory created by this function will be placed in the PATH @@ -648,15 +663,7 @@ func TestRunPipeWithoutMainFunc(t *testing.T) { folder := testlib.Mktmp(t) writeMainWithoutMainFunc(t, folder) config := config.Project{ - Builds: []config.Build{ - { - Binary: "no-main", - Hooks: config.BuildHookConfig{}, - Targets: []string{ - runtimeTarget, - }, - }, - }, + Builds: []config.Build{{Binary: "no-main"}}, } ctx := context.New(config) ctx.Git.CurrentTag = "5.6.7" @@ -703,6 +710,28 @@ func TestRunPipeWithoutMainFunc(t *testing.T) { }) } +func TestBuildTests(t *testing.T) { + folder := testlib.Mktmp(t) + writeTest(t, folder) + config := config.Project{ + Builds: []config.Build{{ + Binary: "foo.test", + Command: "test", + BuildDetails: config.BuildDetails{ + Flags: []string{"-c"}, + }, + }}, + } + ctx := context.New(config) + ctx.Git.CurrentTag = "5.6.7" + ctx.Config.Builds[0].NoMainCheck = true + build, err := Default.WithDefaults(config.Builds[0]) + require.NoError(t, err) + require.NoError(t, Default.Build(ctx, build, api.Options{ + Target: runtimeTarget, + })) +} + func TestRunPipeWithProxiedRepo(t *testing.T) { folder := testlib.Mktmp(t) out, err := exec.Command("git", "clone", "https://github.com/goreleaser/goreleaser", "-b", "v0.161.1", "--depth=1", ".").CombinedOutput() @@ -1260,6 +1289,20 @@ func writeGoodMain(t *testing.T, folder string) { )) } +func writeTest(t *testing.T, folder string) { + t.Helper() + require.NoError(t, os.WriteFile( + filepath.Join(folder, "main_test.go"), + []byte("package main\nimport\"testing\"\nfunc TestFoo(t *testing.T) {t.Log(\"OK\")}"), + 0o644, + )) + require.NoError(t, os.WriteFile( + filepath.Join(folder, "go.mod"), + []byte("module foo\n"), + 0o666, + )) +} + func assertContainsError(t *testing.T, err error, s string) { t.Helper() require.Error(t, err) diff --git a/pkg/config/config.go b/pkg/config/config.go index f8e38f86d10..f3720dce37e 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -312,7 +312,9 @@ type Build struct { ModTimestamp string `yaml:"mod_timestamp,omitempty"` Skip bool `yaml:"skip,omitempty"` GoBinary string `yaml:"gobinary,omitempty"` + Command string `yaml:"command,omitempty"` NoUniqueDistDir bool `yaml:"no_unique_dist_dir,omitempty"` + NoMainCheck bool `yaml:"no_main_check,omitempty"` UnproxiedMain string `yaml:"-"` // used by gomod.proxy UnproxiedDir string `yaml:"-"` // used by gomod.proxy diff --git a/www/docs/customization/build.md b/www/docs/customization/build.md index 128334ceeb9..c2c8ca08dca 100644 --- a/www/docs/customization/build.md +++ b/www/docs/customization/build.md @@ -125,11 +125,20 @@ builds: - darwin_arm64 - linux_arm_6 - # Set a specific go binary to use when building. It is safe to ignore - # this option in most cases. + # Set a specific go binary to use when building. + # It is safe to ignore this option in most cases. + # # Default is "go" gobinary: "go1.13.4" + # Sets the command to run to build. + # Can be useful if you want to build tests, for example, + # in which case you can set this to "test". + # It is safe to ignore this option in most cases. + # + # Default is "build". + command: test + # Set the modified timestamp on the output binary, typically # you would do this to ensure a build was reproducible. Pass # empty string to skip modifying the output. @@ -158,6 +167,12 @@ builds: # Defaults to `false`. no_unique_dist_dir: true + # By default, GoReleaser will check if the main filepath has a main function. + # This can be used to skip that check, in case you're building tests, for example. + # + # Defaults to `false`. + no_main_check: true + # Builder allows you to use a different build implementation. # This is a GoReleaser Pro feature. # Valid options are: `go` and `prebuilt`. From a35125408bca92d6a20d2f25ebdae31a47b95af6 Mon Sep 17 00:00:00 2001 From: Carlos A Becker Date: Sun, 24 Apr 2022 22:49:02 -0300 Subject: [PATCH 2/2] test: fix broken tests Signed-off-by: Carlos A Becker --- internal/builders/golang/build_test.go | 31 +++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/internal/builders/golang/build_test.go b/internal/builders/golang/build_test.go index e43d4ebf7ac..dac01ac152e 100644 --- a/internal/builders/golang/build_test.go +++ b/internal/builders/golang/build_test.go @@ -316,6 +316,7 @@ func TestBuild(t *testing.T) { "linux_mips64le_softfloat", }, GoBinary: "go", + Command: "build", BuildDetails: config.BuildDetails{ Asmflags: []string{".=", "all="}, Gcflags: []string{"all="}, @@ -494,6 +495,7 @@ func TestBuildCodeInSubdir(t *testing.T) { runtimeTarget, }, GoBinary: "go", + Command: "build", }, }, } @@ -521,6 +523,7 @@ func TestBuildWithDotGoDir(t *testing.T) { Binary: "foo", Targets: []string{runtimeTarget}, GoBinary: "go", + Command: "build", }, }, } @@ -549,6 +552,7 @@ func TestBuildFailed(t *testing.T) { runtimeTarget, }, GoBinary: "go", + Command: "build", }, }, } @@ -773,6 +777,7 @@ import _ "github.com/goreleaser/goreleaser" runtimeTarget, }, GoBinary: "go", + Command: "build", }, }, } @@ -800,6 +805,7 @@ func TestRunPipeWithMainFuncNotInMainGoFile(t *testing.T) { runtimeTarget, }, GoBinary: "go", + Command: "build", }, }, } @@ -954,6 +960,7 @@ func TestBuildModTimestamp(t *testing.T) { }, ModTimestamp: fmt.Sprintf("%d", modTime.Unix()), GoBinary: "go", + Command: "build", }, }, } @@ -1006,7 +1013,7 @@ func TestBuildGoBuildLine(t *testing.T) { ctx.Git.Commit = "aaa" line, err := buildGoBuildLine(ctx, config.Builds[0], api.Options{ - Path: "foo", + Path: config.Builds[0].Binary, Goos: "linux", Goarch: "amd64", }, &artifact.Artifact{}, []string{}) @@ -1024,7 +1031,9 @@ func TestBuildGoBuildLine(t *testing.T) { Tags: []string{"tag1", "tag2"}, Ldflags: []string{"ldflag1", "ldflag2"}, }, + Binary: "foo", GoBinary: "go", + Command: "build", }, []string{ "go", "build", "-flag1", "-flag2", @@ -1060,6 +1069,8 @@ func TestBuildGoBuildLine(t *testing.T) { }, }, GoBinary: "go", + Binary: "foo", + Command: "build", }, []string{ "go", "build", "-flag3", @@ -1075,9 +1086,23 @@ func TestBuildGoBuildLine(t *testing.T) { requireEqualCmd(t, config.Build{ Main: ".", GoBinary: "go", + Command: "build", + Binary: "foo", }, strings.Fields("go build -o foo .")) }) + t.Run("test", func(t *testing.T) { + requireEqualCmd(t, config.Build{ + Main: ".", + GoBinary: "go", + Command: "test", + Binary: "foo.test", + BuildDetails: config.BuildDetails{ + Flags: []string{"-c"}, + }, + }, strings.Fields("go test -c -o foo.test .")) + }) + t.Run("ldflags1", func(t *testing.T) { requireEqualCmd(t, config.Build{ Main: ".", @@ -1085,6 +1110,8 @@ func TestBuildGoBuildLine(t *testing.T) { Ldflags: []string{"-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.builtBy=goreleaser"}, }, GoBinary: "go", + Command: "build", + Binary: "foo", }, []string{ "go", "build", "-ldflags=-s -w -X main.version=1.2.3 -X main.commit=aaa -X main.builtBy=goreleaser", @@ -1099,6 +1126,8 @@ func TestBuildGoBuildLine(t *testing.T) { Ldflags: []string{"-s -w", "-X main.version={{.Version}}"}, }, GoBinary: "go", + Binary: "foo", + Command: "build", }, []string{"go", "build", "-ldflags=-s -w -X main.version=1.2.3", "-o", "foo", "."}) }) }