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

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