github.com/goreleaser/goreleaser@v1.25.1/internal/pipe/build/build_test.go (about)

     1  package build
     2  
     3  import (
     4  	"errors"
     5  	"os"
     6  	"os/exec"
     7  	"path/filepath"
     8  	"testing"
     9  
    10  	"github.com/goreleaser/goreleaser/internal/artifact"
    11  	"github.com/goreleaser/goreleaser/internal/semerrgroup"
    12  	"github.com/goreleaser/goreleaser/internal/skips"
    13  	"github.com/goreleaser/goreleaser/internal/testctx"
    14  	"github.com/goreleaser/goreleaser/internal/testlib"
    15  	"github.com/goreleaser/goreleaser/internal/tmpl"
    16  	api "github.com/goreleaser/goreleaser/pkg/build"
    17  	"github.com/goreleaser/goreleaser/pkg/config"
    18  	"github.com/goreleaser/goreleaser/pkg/context"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  var (
    23  	errFailedBuild   = errors.New("fake builder failed")
    24  	errFailedDefault = errors.New("fake builder defaults failed")
    25  )
    26  
    27  type fakeBuilder struct {
    28  	fail        bool
    29  	failDefault bool
    30  }
    31  
    32  func (f *fakeBuilder) WithDefaults(build config.Build) (config.Build, error) {
    33  	if f.failDefault {
    34  		return build, errFailedDefault
    35  	}
    36  	return build, nil
    37  }
    38  
    39  func (f *fakeBuilder) Build(ctx *context.Context, _ config.Build, options api.Options) error {
    40  	if f.fail {
    41  		return errFailedBuild
    42  	}
    43  	if err := os.MkdirAll(filepath.Dir(options.Path), 0o755); err != nil {
    44  		return err
    45  	}
    46  	if err := os.WriteFile(options.Path, []byte("foo"), 0o755); err != nil {
    47  		return err
    48  	}
    49  	ctx.Artifacts.Add(&artifact.Artifact{
    50  		Name: options.Name,
    51  	})
    52  	return nil
    53  }
    54  
    55  func init() {
    56  	api.Register("fake", &fakeBuilder{})
    57  	api.Register("fakeFail", &fakeBuilder{
    58  		fail: true,
    59  	})
    60  	api.Register("fakeFailDefault", &fakeBuilder{
    61  		failDefault: true,
    62  	})
    63  }
    64  
    65  func TestPipeDescription(t *testing.T) {
    66  	require.NotEmpty(t, Pipe{}.String())
    67  }
    68  
    69  func TestBuild(t *testing.T) {
    70  	folder := testlib.Mktmp(t)
    71  	config := config.Project{
    72  		Dist: folder,
    73  		Builds: []config.Build{
    74  			{
    75  				Builder: "fake",
    76  				Binary:  "testing.v{{.Version}}",
    77  				BuildDetails: config.BuildDetails{
    78  					Flags: []string{"-n"},
    79  					Env:   []string{"BLAH=1"},
    80  				},
    81  			},
    82  		},
    83  	}
    84  
    85  	ctx := testctx.NewWithCfg(
    86  		config,
    87  		testctx.WithVersion("1.2.3"),
    88  		testctx.WithGitInfo(context.GitInfo{
    89  			CurrentTag: "v1.2.3",
    90  			Commit:     "123",
    91  		}),
    92  	)
    93  	opts, err := buildOptionsForTarget(ctx, ctx.Config.Builds[0], "darwin_amd64")
    94  	require.NoError(t, err)
    95  	require.NoError(t, doBuild(ctx, ctx.Config.Builds[0], *opts))
    96  }
    97  
    98  func TestRunPipe(t *testing.T) {
    99  	folder := testlib.Mktmp(t)
   100  	ctx := testctx.NewWithCfg(config.Project{
   101  		Dist: folder,
   102  		Builds: []config.Build{
   103  			{
   104  				Builder: "fake",
   105  				Binary:  "testing",
   106  				BuildDetails: config.BuildDetails{
   107  					Flags:   []string{"-v"},
   108  					Ldflags: []string{"-X main.test=testing"},
   109  				},
   110  				Targets: []string{"linux_amd64"},
   111  			},
   112  		},
   113  	}, testctx.WithCurrentTag("2.4.5"))
   114  	require.NoError(t, Pipe{}.Run(ctx))
   115  	require.Equal(t, []*artifact.Artifact{{
   116  		Name: "testing",
   117  	}}, ctx.Artifacts.List())
   118  }
   119  
   120  func TestRunFullPipe(t *testing.T) {
   121  	folder := testlib.Mktmp(t)
   122  	pre := filepath.Join(folder, "pre")
   123  	post := filepath.Join(folder, "post")
   124  	preOS := filepath.Join(folder, "pre_linux")
   125  	postOS := filepath.Join(folder, "post_linux")
   126  	config := config.Project{
   127  		Builds: []config.Build{
   128  			{
   129  				ID:      "build1",
   130  				Builder: "fake",
   131  				Binary:  "testing",
   132  				BuildDetails: config.BuildDetails{
   133  					Flags:   []string{"-v"},
   134  					Ldflags: []string{"-X main.test=testing"},
   135  					Env:     []string{"THE_OS={{ .Os }}"},
   136  				},
   137  				Hooks: config.BuildHookConfig{
   138  					Pre: []config.Hook{
   139  						{Cmd: "touch " + pre},
   140  						{Cmd: "touch pre_{{ .Env.THE_OS}}"},
   141  					},
   142  					Post: []config.Hook{
   143  						{Cmd: "touch " + post},
   144  						{Cmd: "touch post_{{ .Env.THE_OS}}"},
   145  					},
   146  				},
   147  				Targets: []string{"linux_amd64"},
   148  			},
   149  		},
   150  		Dist: folder,
   151  	}
   152  	ctx := testctx.NewWithCfg(config, testctx.WithCurrentTag("2.4.5"))
   153  	require.NoError(t, Pipe{}.Default(ctx))
   154  	require.NoError(t, Pipe{}.Run(ctx))
   155  	require.Equal(t, []*artifact.Artifact{{
   156  		Name: "testing",
   157  	}}, ctx.Artifacts.List())
   158  	require.FileExists(t, post)
   159  	require.FileExists(t, pre)
   160  	require.FileExists(t, postOS)
   161  	require.FileExists(t, preOS)
   162  	require.FileExists(t, filepath.Join(folder, "build1_linux_amd64", "testing"))
   163  }
   164  
   165  func TestRunFullPipeFail(t *testing.T) {
   166  	folder := testlib.Mktmp(t)
   167  	pre := filepath.Join(folder, "pre")
   168  	post := filepath.Join(folder, "post")
   169  	config := config.Project{
   170  		Dist: folder,
   171  		Builds: []config.Build{
   172  			{
   173  				Builder: "fakeFail",
   174  				Binary:  "testing",
   175  				BuildDetails: config.BuildDetails{
   176  					Flags:   []string{"-v"},
   177  					Ldflags: []string{"-X main.test=testing"},
   178  				},
   179  				Hooks: config.BuildHookConfig{
   180  					Pre: []config.Hook{
   181  						{Cmd: "touch " + pre},
   182  					},
   183  					Post: []config.Hook{
   184  						{Cmd: "touch " + post},
   185  					},
   186  				},
   187  				Targets: []string{"linux_amd64"},
   188  			},
   189  		},
   190  	}
   191  	ctx := testctx.NewWithCfg(config, testctx.WithCurrentTag("2.4.5"))
   192  	require.EqualError(t, Pipe{}.Run(ctx), errFailedBuild.Error())
   193  	require.Empty(t, ctx.Artifacts.List())
   194  	require.FileExists(t, pre)
   195  }
   196  
   197  func TestRunPipeFailingHooks(t *testing.T) {
   198  	folder := testlib.Mktmp(t)
   199  	cfg := config.Project{
   200  		Dist: folder,
   201  		Builds: []config.Build{
   202  			{
   203  				Builder: "fake",
   204  				Binary:  "hooks",
   205  				Hooks:   config.BuildHookConfig{},
   206  				Targets: []string{"linux_amd64"},
   207  			},
   208  		},
   209  	}
   210  	t.Run("pre-hook", func(t *testing.T) {
   211  		ctx := testctx.NewWithCfg(cfg, testctx.WithCurrentTag("2.4.5"))
   212  		ctx.Config.Builds[0].Hooks.Pre = []config.Hook{{Cmd: "exit 1"}}
   213  		ctx.Config.Builds[0].Hooks.Post = []config.Hook{{Cmd: "echo post"}}
   214  
   215  		err := Pipe{}.Run(ctx)
   216  		require.ErrorIs(t, err, exec.ErrNotFound)
   217  		require.ErrorContains(t, err, "pre hook failed")
   218  	})
   219  	t.Run("post-hook", func(t *testing.T) {
   220  		ctx := testctx.NewWithCfg(cfg, testctx.WithCurrentTag("2.4.5"))
   221  		ctx.Config.Builds[0].Hooks.Pre = []config.Hook{{Cmd: "echo pre"}}
   222  		ctx.Config.Builds[0].Hooks.Post = []config.Hook{{Cmd: "exit 1"}}
   223  		err := Pipe{}.Run(ctx)
   224  		require.ErrorIs(t, err, exec.ErrNotFound)
   225  		require.ErrorContains(t, err, "post hook failed")
   226  	})
   227  
   228  	t.Run("post-hook-skip", func(t *testing.T) {
   229  		ctx := testctx.NewWithCfg(
   230  			cfg,
   231  			testctx.WithCurrentTag("2.4.5"),
   232  			testctx.Skip(skips.PostBuildHooks),
   233  		)
   234  		ctx.Config.Builds[0].Hooks.Pre = []config.Hook{{Cmd: "echo pre"}}
   235  		ctx.Config.Builds[0].Hooks.Post = []config.Hook{{Cmd: "exit 1"}}
   236  		require.NoError(t, Pipe{}.Run(ctx))
   237  	})
   238  
   239  	t.Run("pre-hook-skip", func(t *testing.T) {
   240  		ctx := testctx.NewWithCfg(
   241  			cfg,
   242  			testctx.WithCurrentTag("2.4.5"),
   243  			testctx.Skip(skips.PreBuildHooks),
   244  		)
   245  		ctx.Config.Builds[0].Hooks.Post = []config.Hook{{Cmd: "echo pre"}}
   246  		ctx.Config.Builds[0].Hooks.Pre = []config.Hook{{Cmd: "exit 1"}}
   247  		require.NoError(t, Pipe{}.Run(ctx))
   248  	})
   249  }
   250  
   251  func TestDefaultNoBuilds(t *testing.T) {
   252  	ctx := testctx.New()
   253  	require.NoError(t, Pipe{}.Default(ctx))
   254  }
   255  
   256  func TestDefaultFail(t *testing.T) {
   257  	folder := testlib.Mktmp(t)
   258  	config := config.Project{
   259  		Dist: folder,
   260  		Builds: []config.Build{
   261  			{
   262  				Builder: "fakeFailDefault",
   263  			},
   264  		},
   265  	}
   266  	ctx := testctx.NewWithCfg(config)
   267  	require.EqualError(t, Pipe{}.Default(ctx), errFailedDefault.Error())
   268  	require.Empty(t, ctx.Artifacts.List())
   269  }
   270  
   271  func TestDefaultExpandEnv(t *testing.T) {
   272  	t.Setenv("XBAR", "FOOBAR")
   273  	ctx := testctx.NewWithCfg(config.Project{
   274  		Builds: []config.Build{
   275  			{
   276  				BuildDetails: config.BuildDetails{
   277  					Env: []string{
   278  						"XFOO=bar_$XBAR",
   279  					},
   280  				},
   281  			},
   282  		},
   283  	})
   284  	require.NoError(t, Pipe{}.Default(ctx))
   285  	env := ctx.Config.Builds[0].Env[0]
   286  	require.Equal(t, "XFOO=bar_FOOBAR", env)
   287  }
   288  
   289  func TestDefaultEmptyBuild(t *testing.T) {
   290  	ctx := testctx.NewWithCfg(config.Project{
   291  		ProjectName: "foo",
   292  		Builds: []config.Build{
   293  			{},
   294  		},
   295  	})
   296  	require.NoError(t, Pipe{}.Default(ctx))
   297  	build := ctx.Config.Builds[0]
   298  	require.Equal(t, ctx.Config.ProjectName, build.ID)
   299  	require.Equal(t, ctx.Config.ProjectName, build.Binary)
   300  	require.Equal(t, ".", build.Dir)
   301  	require.Equal(t, ".", build.Main)
   302  	require.Equal(t, []string{"linux", "darwin", "windows"}, build.Goos)
   303  	require.Equal(t, []string{"amd64", "arm64", "386"}, build.Goarch)
   304  	require.Equal(t, []string{"6"}, build.Goarm)
   305  	require.Equal(t, []string{"hardfloat"}, build.Gomips)
   306  	require.Equal(t, []string{"v1"}, build.Goamd64)
   307  	require.Len(t, build.Ldflags, 1)
   308  	require.Equal(t, "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser", build.Ldflags[0])
   309  }
   310  
   311  func TestDefaultBuildID(t *testing.T) {
   312  	ctx := testctx.NewWithCfg(config.Project{
   313  		ProjectName: "foo",
   314  		Builds: []config.Build{
   315  			{
   316  				Binary: "{{.Env.FOO}}",
   317  			},
   318  			{
   319  				Binary: "bar",
   320  			},
   321  		},
   322  	})
   323  	require.EqualError(t, Pipe{}.Default(ctx), "found 2 builds with the ID 'foo', please fix your config")
   324  	build1 := ctx.Config.Builds[0].ID
   325  	build2 := ctx.Config.Builds[1].ID
   326  	require.Equal(t, build1, build2)
   327  	require.Equal(t, "foo", build2)
   328  }
   329  
   330  func TestSeveralBuildsWithTheSameID(t *testing.T) {
   331  	ctx := testctx.NewWithCfg(config.Project{
   332  		Builds: []config.Build{
   333  			{
   334  				ID:     "a",
   335  				Binary: "bar",
   336  			},
   337  			{
   338  				ID:     "a",
   339  				Binary: "foo",
   340  			},
   341  		},
   342  	})
   343  	require.EqualError(t, Pipe{}.Default(ctx), "found 2 builds with the ID 'a', please fix your config")
   344  }
   345  
   346  func TestDefaultPartialBuilds(t *testing.T) {
   347  	ctx := testctx.NewWithCfg(config.Project{
   348  		Builds: []config.Build{
   349  			{
   350  				ID:     "build1",
   351  				Binary: "bar",
   352  				Goos:   []string{"linux"},
   353  				Main:   "./cmd/main.go",
   354  			},
   355  			{
   356  				ID:     "build2",
   357  				Binary: "foo",
   358  				Dir:    "baz",
   359  				BuildDetails: config.BuildDetails{
   360  					Ldflags: []string{"-s -w"},
   361  				},
   362  				Goarch: []string{"386"},
   363  			},
   364  		},
   365  	})
   366  	// Create any 'Dir' paths necessary for builds.
   367  	cwd, err := os.Getwd()
   368  	require.NoError(t, err)
   369  	t.Cleanup(func() { require.NoError(t, os.Chdir(cwd)) })
   370  	require.NoError(t, os.Chdir(t.TempDir()))
   371  	for _, b := range ctx.Config.Builds {
   372  		if b.Dir != "" {
   373  			require.NoError(t, os.Mkdir(b.Dir, 0o755))
   374  		}
   375  	}
   376  	require.NoError(t, Pipe{}.Default(ctx))
   377  
   378  	t.Run("build0", func(t *testing.T) {
   379  		build := ctx.Config.Builds[0]
   380  		require.Equal(t, "bar", build.Binary)
   381  		require.Equal(t, ".", build.Dir)
   382  		require.Equal(t, "./cmd/main.go", build.Main)
   383  		require.Equal(t, []string{"linux"}, build.Goos)
   384  		require.Equal(t, []string{"amd64", "arm64", "386"}, build.Goarch)
   385  		require.Equal(t, []string{"6"}, build.Goarm)
   386  		require.Len(t, build.Ldflags, 1)
   387  		require.Equal(t, "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser", build.Ldflags[0])
   388  	})
   389  	t.Run("build1", func(t *testing.T) {
   390  		build := ctx.Config.Builds[1]
   391  		require.Equal(t, "foo", build.Binary)
   392  		require.Equal(t, ".", build.Main)
   393  		require.Equal(t, "baz", build.Dir)
   394  		require.Equal(t, []string{"linux", "darwin", "windows"}, build.Goos)
   395  		require.Equal(t, []string{"386"}, build.Goarch)
   396  		require.Equal(t, []string{"6"}, build.Goarm)
   397  		require.Len(t, build.Ldflags, 1)
   398  		require.Equal(t, "-s -w", build.Ldflags[0])
   399  	})
   400  }
   401  
   402  func TestDefaultFillSingleBuild(t *testing.T) {
   403  	testlib.Mktmp(t)
   404  
   405  	ctx := testctx.NewWithCfg(config.Project{
   406  		ProjectName: "foo",
   407  		SingleBuild: config.Build{
   408  			Main: "testreleaser",
   409  		},
   410  	})
   411  	require.NoError(t, Pipe{}.Default(ctx))
   412  	require.Len(t, ctx.Config.Builds, 1)
   413  	require.Equal(t, "foo", ctx.Config.Builds[0].Binary)
   414  }
   415  
   416  func TestDefaultFailSingleBuild(t *testing.T) {
   417  	folder := testlib.Mktmp(t)
   418  	config := config.Project{
   419  		Dist: folder,
   420  		SingleBuild: config.Build{
   421  			Builder: "fakeFailDefault",
   422  		},
   423  	}
   424  	ctx := testctx.NewWithCfg(config)
   425  	require.EqualError(t, Pipe{}.Default(ctx), errFailedDefault.Error())
   426  	require.Empty(t, ctx.Artifacts.List())
   427  }
   428  
   429  func TestSkipBuild(t *testing.T) {
   430  	folder := testlib.Mktmp(t)
   431  	config := config.Project{
   432  		Dist: folder,
   433  		Builds: []config.Build{
   434  			{
   435  				Skip: true,
   436  			},
   437  		},
   438  	}
   439  	ctx := testctx.NewWithCfg(config, testctx.WithCurrentTag("2.4.5"))
   440  	require.NoError(t, Pipe{}.Run(ctx))
   441  	require.Empty(t, ctx.Artifacts.List())
   442  }
   443  
   444  func TestExtDarwin(t *testing.T) {
   445  	require.Equal(t, "", extFor("darwin_amd64", config.BuildDetails{}))
   446  	require.Equal(t, "", extFor("darwin_arm64", config.BuildDetails{}))
   447  	require.Equal(t, "", extFor("darwin_amd64", config.BuildDetails{}))
   448  	require.Equal(t, ".dylib", extFor("darwin_amd64", config.BuildDetails{Buildmode: "c-shared"}))
   449  	require.Equal(t, ".dylib", extFor("darwin_arm64", config.BuildDetails{Buildmode: "c-shared"}))
   450  	require.Equal(t, ".a", extFor("darwin_amd64", config.BuildDetails{Buildmode: "c-archive"}))
   451  	require.Equal(t, ".a", extFor("darwin_arm64", config.BuildDetails{Buildmode: "c-archive"}))
   452  }
   453  
   454  func TestExtLinux(t *testing.T) {
   455  	require.Equal(t, "", extFor("linux_amd64", config.BuildDetails{}))
   456  	require.Equal(t, "", extFor("linux_386", config.BuildDetails{}))
   457  	require.Equal(t, "", extFor("linux_amd64", config.BuildDetails{}))
   458  	require.Equal(t, ".so", extFor("linux_amd64", config.BuildDetails{Buildmode: "c-shared"}))
   459  	require.Equal(t, ".so", extFor("linux_386", config.BuildDetails{Buildmode: "c-shared"}))
   460  	require.Equal(t, ".a", extFor("linux_amd64", config.BuildDetails{Buildmode: "c-archive"}))
   461  	require.Equal(t, ".a", extFor("linux_386", config.BuildDetails{Buildmode: "c-archive"}))
   462  }
   463  
   464  func TestExtWindows(t *testing.T) {
   465  	require.Equal(t, ".exe", extFor("windows_amd64", config.BuildDetails{}))
   466  	require.Equal(t, ".exe", extFor("windows_386", config.BuildDetails{}))
   467  	require.Equal(t, ".exe", extFor("windows_amd64", config.BuildDetails{}))
   468  	require.Equal(t, ".dll", extFor("windows_amd64", config.BuildDetails{Buildmode: "c-shared"}))
   469  	require.Equal(t, ".dll", extFor("windows_386", config.BuildDetails{Buildmode: "c-shared"}))
   470  	require.Equal(t, ".lib", extFor("windows_amd64", config.BuildDetails{Buildmode: "c-archive"}))
   471  	require.Equal(t, ".lib", extFor("windows_386", config.BuildDetails{Buildmode: "c-archive"}))
   472  }
   473  
   474  func TestExtWasm(t *testing.T) {
   475  	require.Equal(t, ".wasm", extFor("js_wasm", config.BuildDetails{}))
   476  }
   477  
   478  func TestExtOthers(t *testing.T) {
   479  	require.Equal(t, "", extFor("linux_amd64", config.BuildDetails{}))
   480  	require.Equal(t, "", extFor("linuxwin_386", config.BuildDetails{}))
   481  	require.Equal(t, "", extFor("winasdasd_sad", config.BuildDetails{}))
   482  	require.Equal(t, ".so", extFor("aix_amd64", config.BuildDetails{Buildmode: "c-shared"}))
   483  	require.Equal(t, ".a", extFor("android_386", config.BuildDetails{Buildmode: "c-archive"}))
   484  	require.Equal(t, ".so", extFor("winasdasd_sad", config.BuildDetails{Buildmode: "c-shared"}))
   485  }
   486  
   487  func TestTemplate(t *testing.T) {
   488  	ctx := testctx.New(
   489  		testctx.WithEnv(map[string]string{"FOO": "123"}),
   490  		testctx.WithVersion("1.2.3"),
   491  		testctx.WithCurrentTag("v1.2.3"),
   492  		testctx.WithCommit("123"),
   493  	)
   494  	binary, err := tmpl.New(ctx).
   495  		Apply(`-s -w -X main.version={{.Version}} -X main.tag={{.Tag}} -X main.date={{.Date}} -X main.commit={{.Commit}} -X "main.foo={{.Env.FOO}}"`)
   496  	require.NoError(t, err)
   497  	require.Contains(t, binary, "-s -w")
   498  	require.Contains(t, binary, "-X main.version=1.2.3")
   499  	require.Contains(t, binary, "-X main.tag=v1.2.3")
   500  	require.Contains(t, binary, "-X main.commit=123")
   501  	require.Contains(t, binary, "-X main.date=")
   502  	require.Contains(t, binary, `-X "main.foo=123"`)
   503  }
   504  
   505  func TestBuild_hooksKnowGoosGoarch(t *testing.T) {
   506  	tmpDir := testlib.Mktmp(t)
   507  	build := config.Build{
   508  		Builder: "fake",
   509  		Goarch:  []string{"amd64"},
   510  		Goos:    []string{"linux"},
   511  		Binary:  "testing-goos-goarch.v{{.Version}}",
   512  		Targets: []string{
   513  			"linux_amd64",
   514  		},
   515  		Hooks: config.BuildHookConfig{
   516  			Pre: []config.Hook{
   517  				{Cmd: "touch pre-hook-{{.Arch}}-{{.Os}}", Dir: tmpDir},
   518  			},
   519  			Post: config.Hooks{
   520  				{Cmd: "touch post-hook-{{.Arch}}-{{.Os}}", Dir: tmpDir},
   521  			},
   522  		},
   523  	}
   524  
   525  	ctx := testctx.NewWithCfg(config.Project{
   526  		Builds: []config.Build{
   527  			build,
   528  		},
   529  	})
   530  	g := semerrgroup.New(ctx.Parallelism)
   531  	runPipeOnBuild(ctx, g, build)
   532  	require.NoError(t, g.Wait())
   533  	require.FileExists(t, filepath.Join(tmpDir, "pre-hook-amd64-linux"))
   534  	require.FileExists(t, filepath.Join(tmpDir, "post-hook-amd64-linux"))
   535  }
   536  
   537  func TestPipeOnBuild_hooksRunPerTarget(t *testing.T) {
   538  	tmpDir := testlib.Mktmp(t)
   539  
   540  	build := config.Build{
   541  		Builder: "fake",
   542  		Binary:  "testing.v{{.Version}}",
   543  		Targets: []string{
   544  			"linux_amd64",
   545  			"darwin_amd64",
   546  			"windows_amd64",
   547  		},
   548  		Hooks: config.BuildHookConfig{
   549  			Pre: []config.Hook{
   550  				{Cmd: "touch pre-hook-{{.Target}}", Dir: tmpDir},
   551  			},
   552  			Post: config.Hooks{
   553  				{Cmd: "touch post-hook-{{.Target}}", Dir: tmpDir},
   554  			},
   555  		},
   556  	}
   557  	ctx := testctx.NewWithCfg(config.Project{
   558  		Builds: []config.Build{
   559  			build,
   560  		},
   561  	})
   562  	g := semerrgroup.New(ctx.Parallelism)
   563  	runPipeOnBuild(ctx, g, build)
   564  	require.NoError(t, g.Wait())
   565  	require.FileExists(t, filepath.Join(tmpDir, "pre-hook-linux_amd64"))
   566  	require.FileExists(t, filepath.Join(tmpDir, "pre-hook-darwin_amd64"))
   567  	require.FileExists(t, filepath.Join(tmpDir, "pre-hook-windows_amd64"))
   568  	require.FileExists(t, filepath.Join(tmpDir, "post-hook-linux_amd64"))
   569  	require.FileExists(t, filepath.Join(tmpDir, "post-hook-darwin_amd64"))
   570  	require.FileExists(t, filepath.Join(tmpDir, "post-hook-windows_amd64"))
   571  }
   572  
   573  func TestPipeOnBuild_invalidBinaryTpl(t *testing.T) {
   574  	build := config.Build{
   575  		Builder: "fake",
   576  		Binary:  "testing.v{{.XYZ}}",
   577  		Targets: []string{
   578  			"linux_amd64",
   579  		},
   580  	}
   581  	ctx := testctx.NewWithCfg(config.Project{
   582  		Builds: []config.Build{
   583  			build,
   584  		},
   585  	})
   586  	g := semerrgroup.New(ctx.Parallelism)
   587  	runPipeOnBuild(ctx, g, build)
   588  	testlib.RequireTemplateError(t, g.Wait())
   589  }
   590  
   591  func TestBuildOptionsForTarget(t *testing.T) {
   592  	tmpDir := testlib.Mktmp(t)
   593  
   594  	testCases := []struct {
   595  		name         string
   596  		build        config.Build
   597  		expectedOpts *api.Options
   598  		expectedErr  string
   599  	}{
   600  		{
   601  			name: "simple options for target",
   602  			build: config.Build{
   603  				ID:     "testid",
   604  				Binary: "testbinary",
   605  				Targets: []string{
   606  					"linux_amd64",
   607  				},
   608  			},
   609  			expectedOpts: &api.Options{
   610  				Name:    "testbinary",
   611  				Path:    filepath.Join(tmpDir, "testid_linux_amd64_v1", "testbinary"),
   612  				Target:  "linux_amd64_v1",
   613  				Goos:    "linux",
   614  				Goarch:  "amd64",
   615  				Goamd64: "v1",
   616  			},
   617  		},
   618  		{
   619  			name: "binary name with Os and Arch template variables",
   620  			build: config.Build{
   621  				ID:     "testid",
   622  				Binary: "testbinary_{{.Os}}_{{.Arch}}",
   623  				Targets: []string{
   624  					"linux_amd64",
   625  				},
   626  			},
   627  			expectedOpts: &api.Options{
   628  				Name:    "testbinary_linux_amd64",
   629  				Path:    filepath.Join(tmpDir, "testid_linux_amd64_v1", "testbinary_linux_amd64"),
   630  				Target:  "linux_amd64_v1",
   631  				Goos:    "linux",
   632  				Goarch:  "amd64",
   633  				Goamd64: "v1",
   634  			},
   635  		},
   636  		{
   637  			name: "overriding dist path",
   638  			build: config.Build{
   639  				ID:     "testid",
   640  				Binary: "distpath/{{.Os}}/{{.Arch}}/testbinary_{{.Os}}_{{.Arch}}",
   641  				Targets: []string{
   642  					"linux_amd64",
   643  				},
   644  				NoUniqueDistDir: true,
   645  			},
   646  			expectedOpts: &api.Options{
   647  				Name:    "distpath/linux/amd64/testbinary_linux_amd64",
   648  				Path:    filepath.Join(tmpDir, "distpath", "linux", "amd64", "testbinary_linux_amd64"),
   649  				Target:  "linux_amd64_v1",
   650  				Goos:    "linux",
   651  				Goarch:  "amd64",
   652  				Goamd64: "v1",
   653  			},
   654  		},
   655  		{
   656  			name: "with goarm",
   657  			build: config.Build{
   658  				ID:     "testid",
   659  				Binary: "testbinary",
   660  				Targets: []string{
   661  					"linux_arm_6",
   662  				},
   663  			},
   664  			expectedOpts: &api.Options{
   665  				Name:   "testbinary",
   666  				Path:   filepath.Join(tmpDir, "testid_linux_arm_6", "testbinary"),
   667  				Target: "linux_arm_6",
   668  				Goos:   "linux",
   669  				Goarch: "arm",
   670  				Goarm:  "6",
   671  			},
   672  		},
   673  		{
   674  			name: "with gomips",
   675  			build: config.Build{
   676  				ID:     "testid",
   677  				Binary: "testbinary",
   678  				Targets: []string{
   679  					"linux_mips_softfloat",
   680  				},
   681  			},
   682  			expectedOpts: &api.Options{
   683  				Name:   "testbinary",
   684  				Path:   filepath.Join(tmpDir, "testid_linux_mips_softfloat", "testbinary"),
   685  				Target: "linux_mips_softfloat",
   686  				Goos:   "linux",
   687  				Goarch: "mips",
   688  				Gomips: "softfloat",
   689  			},
   690  		},
   691  		{
   692  			name: "with goamd64",
   693  			build: config.Build{
   694  				ID:     "testid",
   695  				Binary: "testbinary",
   696  				Targets: []string{
   697  					"linux_amd64_v3",
   698  				},
   699  			},
   700  			expectedOpts: &api.Options{
   701  				Name:    "testbinary",
   702  				Path:    filepath.Join(tmpDir, "testid_linux_amd64_v3", "testbinary"),
   703  				Target:  "linux_amd64_v3",
   704  				Goos:    "linux",
   705  				Goarch:  "amd64",
   706  				Goamd64: "v3",
   707  			},
   708  		},
   709  	}
   710  
   711  	for _, tc := range testCases {
   712  		t.Run(tc.name, func(t *testing.T) {
   713  			ctx := testctx.NewWithCfg(config.Project{
   714  				Dist:   tmpDir,
   715  				Builds: []config.Build{tc.build},
   716  			})
   717  			require.NoError(t, Pipe{}.Default(ctx))
   718  			opts, err := buildOptionsForTarget(ctx, ctx.Config.Builds[0], ctx.Config.Builds[0].Targets[0])
   719  			if tc.expectedErr == "" {
   720  				require.NoError(t, err)
   721  				require.Equal(t, tc.expectedOpts, opts)
   722  			} else {
   723  				require.EqualError(t, err, tc.expectedErr)
   724  			}
   725  		})
   726  	}
   727  }
   728  
   729  func TestRunHookFailWithLogs(t *testing.T) {
   730  	folder := testlib.Mktmp(t)
   731  	config := config.Project{
   732  		Dist: folder,
   733  		Builds: []config.Build{
   734  			{
   735  				Builder: "fakeFail",
   736  				Binary:  "testing",
   737  				BuildDetails: config.BuildDetails{
   738  					Flags: []string{"-v"},
   739  				},
   740  				Hooks: config.BuildHookConfig{
   741  					Pre: []config.Hook{
   742  						{Cmd: "sh -c 'echo foo; exit 1'"},
   743  					},
   744  				},
   745  				Targets: []string{"linux_amd64"},
   746  			},
   747  		},
   748  	}
   749  	ctx := testctx.NewWithCfg(config, testctx.WithCurrentTag("2.4.5"))
   750  	err := Pipe{}.Run(ctx)
   751  	require.ErrorContains(t, err, "pre hook failed")
   752  	require.Empty(t, ctx.Artifacts.List())
   753  }