github.com/triarius/goreleaser@v1.12.5/internal/builders/golang/build_test.go (about)

     1  package golang
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/exec"
     7  	"path/filepath"
     8  	"runtime"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/triarius/goreleaser/internal/artifact"
    14  	"github.com/triarius/goreleaser/internal/testlib"
    15  	"github.com/triarius/goreleaser/internal/tmpl"
    16  	api "github.com/triarius/goreleaser/pkg/build"
    17  	"github.com/triarius/goreleaser/pkg/config"
    18  	"github.com/triarius/goreleaser/pkg/context"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  var runtimeTarget = runtime.GOOS + "_" + runtime.GOARCH
    23  
    24  func TestWithDefaults(t *testing.T) {
    25  	for name, testcase := range map[string]struct {
    26  		build    config.Build
    27  		targets  []string
    28  		goBinary string
    29  	}{
    30  		"full": {
    31  			build: config.Build{
    32  				ID:     "foo",
    33  				Binary: "foo",
    34  				Goos: []string{
    35  					"linux",
    36  					"windows",
    37  					"darwin",
    38  				},
    39  				Goarch: []string{
    40  					"amd64",
    41  					"arm",
    42  					"mips",
    43  				},
    44  				Goarm: []string{
    45  					"6",
    46  				},
    47  				Gomips: []string{
    48  					"softfloat",
    49  				},
    50  				Goamd64: []string{
    51  					"v2",
    52  					"v3",
    53  				},
    54  				GoBinary: "go1.2.3",
    55  			},
    56  			targets: []string{
    57  				"linux_amd64_v2",
    58  				"linux_amd64_v3",
    59  				"linux_mips_softfloat",
    60  				"darwin_amd64_v2",
    61  				"darwin_amd64_v3",
    62  				"windows_amd64_v3",
    63  				"windows_amd64_v2",
    64  				"windows_arm_6",
    65  				"linux_arm_6",
    66  			},
    67  			goBinary: "go1.2.3",
    68  		},
    69  		"empty": {
    70  			build: config.Build{
    71  				ID:     "foo2",
    72  				Binary: "foo",
    73  			},
    74  			targets: []string{
    75  				"linux_amd64_v1",
    76  				"linux_386",
    77  				"linux_arm64",
    78  				"darwin_amd64_v1",
    79  				"darwin_arm64",
    80  			},
    81  			goBinary: "go",
    82  		},
    83  		"custom targets": {
    84  			build: config.Build{
    85  				ID:     "foo3",
    86  				Binary: "foo",
    87  				Targets: []string{
    88  					"linux_386",
    89  					"darwin_amd64_v2",
    90  				},
    91  			},
    92  			targets: []string{
    93  				"linux_386",
    94  				"darwin_amd64_v2",
    95  			},
    96  			goBinary: "go",
    97  		},
    98  		"custom targets no amd64": {
    99  			build: config.Build{
   100  				ID:     "foo3",
   101  				Binary: "foo",
   102  				Targets: []string{
   103  					"linux_386",
   104  					"darwin_amd64",
   105  				},
   106  			},
   107  			targets: []string{
   108  				"linux_386",
   109  				"darwin_amd64_v1",
   110  			},
   111  			goBinary: "go",
   112  		},
   113  		"custom targets no arm": {
   114  			build: config.Build{
   115  				ID:      "foo3",
   116  				Binary:  "foo",
   117  				Targets: []string{"linux_arm"},
   118  			},
   119  			targets:  []string{"linux_arm_6"},
   120  			goBinary: "go",
   121  		},
   122  		"custom targets no mips": {
   123  			build: config.Build{
   124  				ID:      "foo3",
   125  				Binary:  "foo",
   126  				Targets: []string{"linux_mips"},
   127  			},
   128  			targets:  []string{"linux_mips_hardfloat"},
   129  			goBinary: "go",
   130  		},
   131  		"custom targets no mipsle": {
   132  			build: config.Build{
   133  				ID:      "foo3",
   134  				Binary:  "foo",
   135  				Targets: []string{"linux_mipsle"},
   136  			},
   137  			targets:  []string{"linux_mipsle_hardfloat"},
   138  			goBinary: "go",
   139  		},
   140  		"custom targets no mips64": {
   141  			build: config.Build{
   142  				ID:      "foo3",
   143  				Binary:  "foo",
   144  				Targets: []string{"linux_mips64"},
   145  			},
   146  			targets:  []string{"linux_mips64_hardfloat"},
   147  			goBinary: "go",
   148  		},
   149  		"custom targets no mips64le": {
   150  			build: config.Build{
   151  				ID:      "foo3",
   152  				Binary:  "foo",
   153  				Targets: []string{"linux_mips64le"},
   154  			},
   155  			targets:  []string{"linux_mips64le_hardfloat"},
   156  			goBinary: "go",
   157  		},
   158  		"empty with custom dir": {
   159  			build: config.Build{
   160  				ID:     "foo2",
   161  				Binary: "foo",
   162  				Dir:    "./testdata",
   163  			},
   164  			targets: []string{
   165  				"linux_amd64_v1",
   166  				"linux_386",
   167  				"linux_arm64",
   168  				"darwin_amd64_v1",
   169  				"darwin_arm64",
   170  			},
   171  			goBinary: "go",
   172  		},
   173  		"empty with custom dir that doest exist": {
   174  			build: config.Build{
   175  				ID:     "foo2",
   176  				Binary: "foo",
   177  				Dir:    "./nope",
   178  			},
   179  			targets: []string{
   180  				"linux_amd64_v1",
   181  				"linux_386",
   182  				"linux_arm64",
   183  				"darwin_amd64_v1",
   184  				"darwin_arm64",
   185  			},
   186  			goBinary: "go",
   187  		},
   188  		"go first class targets": {
   189  			build: config.Build{
   190  				ID:      "foo3",
   191  				Binary:  "foo",
   192  				Targets: []string{goStableFirstClassTargetsName},
   193  			},
   194  			targets:  go118FirstClassTargets,
   195  			goBinary: "go",
   196  		},
   197  		"go 1.18 first class targets": {
   198  			build: config.Build{
   199  				ID:      "foo3",
   200  				Binary:  "foo",
   201  				Targets: []string{go118FirstClassTargetsName},
   202  			},
   203  			targets:  go118FirstClassTargets,
   204  			goBinary: "go",
   205  		},
   206  		"go 1.18 first class targets plus custom": {
   207  			build: config.Build{
   208  				ID:      "foo3",
   209  				Binary:  "foo",
   210  				Targets: []string{"linux_amd64_v1", go118FirstClassTargetsName, "darwin_amd64_v2"},
   211  			},
   212  			targets:  append(go118FirstClassTargets, "darwin_amd64_v2"),
   213  			goBinary: "go",
   214  		},
   215  		"repeatin targets": {
   216  			build: config.Build{
   217  				ID:      "foo3",
   218  				Binary:  "foo",
   219  				Targets: []string{go118FirstClassTargetsName, go118FirstClassTargetsName, goStableFirstClassTargetsName},
   220  			},
   221  			targets:  go118FirstClassTargets,
   222  			goBinary: "go",
   223  		},
   224  	} {
   225  		t.Run(name, func(t *testing.T) {
   226  			if testcase.build.GoBinary != "" && testcase.build.GoBinary != "go" {
   227  				createFakeGoBinaryWithVersion(t, testcase.build.GoBinary, "go1.18")
   228  			}
   229  			config := config.Project{
   230  				Builds: []config.Build{
   231  					testcase.build,
   232  				},
   233  			}
   234  			ctx := context.New(config)
   235  			ctx.Git.CurrentTag = "5.6.7"
   236  			build, err := Default.WithDefaults(ctx.Config.Builds[0])
   237  			require.NoError(t, err)
   238  			require.ElementsMatch(t, build.Targets, testcase.targets)
   239  			require.EqualValues(t, testcase.goBinary, build.GoBinary)
   240  		})
   241  	}
   242  }
   243  
   244  func TestDefaults(t *testing.T) {
   245  	t.Run("command not set", func(t *testing.T) {
   246  		build, err := Default.WithDefaults(config.Build{})
   247  		require.NoError(t, err)
   248  		require.Equal(t, "build", build.Command)
   249  	})
   250  	t.Run("command set", func(t *testing.T) {
   251  		build, err := Default.WithDefaults(config.Build{
   252  			Command: "test",
   253  		})
   254  		require.NoError(t, err)
   255  		require.Equal(t, "test", build.Command)
   256  	})
   257  }
   258  
   259  // createFakeGoBinaryWithVersion creates a temporary executable with the
   260  // given name, which will output a go version string with the given version.
   261  //
   262  // The temporary directory created by this function will be placed in the
   263  // PATH variable for the duration of (and cleaned up at the end of) the
   264  // current test run.
   265  func createFakeGoBinaryWithVersion(tb testing.TB, name, version string) {
   266  	tb.Helper()
   267  	d := tb.TempDir()
   268  
   269  	require.NoError(tb, os.WriteFile(
   270  		filepath.Join(d, name),
   271  		[]byte(fmt.Sprintf("#!/bin/sh\necho %s", version)),
   272  		0o755,
   273  	))
   274  
   275  	currentPath := os.Getenv("PATH")
   276  	tb.Cleanup(func() {
   277  		require.NoError(tb, os.Setenv("PATH", currentPath))
   278  	})
   279  
   280  	path := fmt.Sprintf("%s%c%s", d, os.PathListSeparator, currentPath)
   281  	require.NoError(tb, os.Setenv("PATH", path))
   282  }
   283  
   284  func TestInvalidTargets(t *testing.T) {
   285  	type testcase struct {
   286  		build       config.Build
   287  		expectedErr string
   288  	}
   289  	for s, tc := range map[string]testcase{
   290  		"goos": {
   291  			build: config.Build{
   292  				Goos: []string{"darwin", "darwim"},
   293  			},
   294  			expectedErr: "invalid goos: darwim",
   295  		},
   296  		"goarch": {
   297  			build: config.Build{
   298  				Goarch: []string{"amd64", "i386", "386"},
   299  			},
   300  			expectedErr: "invalid goarch: i386",
   301  		},
   302  		"goarm": {
   303  			build: config.Build{
   304  				Goarch: []string{"arm"},
   305  				Goarm:  []string{"6", "9", "8", "7"},
   306  			},
   307  			expectedErr: "invalid goarm: 9",
   308  		},
   309  		"gomips": {
   310  			build: config.Build{
   311  				Goarch: []string{"mips"},
   312  				Gomips: []string{"softfloat", "mehfloat", "hardfloat"},
   313  			},
   314  			expectedErr: "invalid gomips: mehfloat",
   315  		},
   316  		"goamd64": {
   317  			build: config.Build{
   318  				Goarch:  []string{"amd64"},
   319  				Goamd64: []string{"v1", "v431"},
   320  			},
   321  			expectedErr: "invalid goamd64: v431",
   322  		},
   323  	} {
   324  		t.Run(s, func(t *testing.T) {
   325  			config := config.Project{
   326  				Builds: []config.Build{
   327  					tc.build,
   328  				},
   329  			}
   330  			ctx := context.New(config)
   331  			_, err := Default.WithDefaults(ctx.Config.Builds[0])
   332  			require.EqualError(t, err, tc.expectedErr)
   333  		})
   334  	}
   335  }
   336  
   337  func TestBuild(t *testing.T) {
   338  	folder := testlib.Mktmp(t)
   339  	writeGoodMain(t, folder)
   340  	config := config.Project{
   341  		Builds: []config.Build{
   342  			{
   343  				ID:     "foo",
   344  				Binary: "bin/foo-{{ .Version }}",
   345  				Targets: []string{
   346  					"linux_amd64",
   347  					"darwin_amd64",
   348  					"windows_amd64",
   349  					"linux_arm_6",
   350  					"js_wasm",
   351  					"linux_mips_softfloat",
   352  					"linux_mips64le_softfloat",
   353  				},
   354  				GoBinary: "go",
   355  				Command:  "build",
   356  				BuildDetails: config.BuildDetails{
   357  					Env:      []string{"GO111MODULE=off"},
   358  					Asmflags: []string{".=", "all="},
   359  					Gcflags:  []string{"all="},
   360  					Flags:    []string{"{{.Env.GO_FLAGS}}"},
   361  					Tags:     []string{"osusergo", "netgo", "static_build"},
   362  				},
   363  			},
   364  		},
   365  	}
   366  	ctx := context.New(config)
   367  	ctx.Env["GO_FLAGS"] = "-v"
   368  	ctx.Git.CurrentTag = "v5.6.7"
   369  	ctx.Version = ctx.Git.CurrentTag
   370  	build := ctx.Config.Builds[0]
   371  	for _, target := range build.Targets {
   372  		var ext string
   373  		if strings.HasPrefix(target, "windows") {
   374  			ext = ".exe"
   375  		} else if target == "js_wasm" {
   376  			ext = ".wasm"
   377  		}
   378  		bin, terr := tmpl.New(ctx).Apply(build.Binary)
   379  		require.NoError(t, terr)
   380  
   381  		// injecting some delay here to force inconsistent mod times on bins
   382  		time.Sleep(2 * time.Second)
   383  
   384  		parts := strings.Split(target, "_")
   385  		goos := parts[0]
   386  		goarch := parts[1]
   387  		goarm := ""
   388  		gomips := ""
   389  		if len(parts) > 2 {
   390  			if strings.Contains(goarch, "arm") {
   391  				goarm = parts[2]
   392  			}
   393  			if strings.Contains(goarch, "mips") {
   394  				gomips = parts[2]
   395  			}
   396  		}
   397  		err := Default.Build(ctx, build, api.Options{
   398  			Target: target,
   399  			Name:   bin + ext,
   400  			Path:   filepath.Join(folder, "dist", target, bin+ext),
   401  			Goos:   goos,
   402  			Goarch: goarch,
   403  			Goarm:  goarm,
   404  			Gomips: gomips,
   405  			Ext:    ext,
   406  		})
   407  		require.NoError(t, err)
   408  	}
   409  	require.ElementsMatch(t, ctx.Artifacts.List(), []*artifact.Artifact{
   410  		{
   411  			Name:   "bin/foo-v5.6.7",
   412  			Path:   filepath.Join(folder, "dist", "linux_amd64", "bin", "foo-v5.6.7"),
   413  			Goos:   "linux",
   414  			Goarch: "amd64",
   415  			Type:   artifact.Binary,
   416  			Extra: map[string]interface{}{
   417  				artifact.ExtraExt:    "",
   418  				artifact.ExtraBinary: "foo-v5.6.7",
   419  				artifact.ExtraID:     "foo",
   420  			},
   421  		},
   422  		{
   423  			Name:   "bin/foo-v5.6.7",
   424  			Path:   filepath.Join(folder, "dist", "linux_mips_softfloat", "bin", "foo-v5.6.7"),
   425  			Goos:   "linux",
   426  			Goarch: "mips",
   427  			Gomips: "softfloat",
   428  			Type:   artifact.Binary,
   429  			Extra: map[string]interface{}{
   430  				artifact.ExtraExt:    "",
   431  				artifact.ExtraBinary: "foo-v5.6.7",
   432  				artifact.ExtraID:     "foo",
   433  			},
   434  		},
   435  		{
   436  			Name:   "bin/foo-v5.6.7",
   437  			Path:   filepath.Join(folder, "dist", "linux_mips64le_softfloat", "bin", "foo-v5.6.7"),
   438  			Goos:   "linux",
   439  			Goarch: "mips64le",
   440  			Gomips: "softfloat",
   441  			Type:   artifact.Binary,
   442  			Extra: map[string]interface{}{
   443  				artifact.ExtraExt:    "",
   444  				artifact.ExtraBinary: "foo-v5.6.7",
   445  				artifact.ExtraID:     "foo",
   446  			},
   447  		},
   448  		{
   449  			Name:   "bin/foo-v5.6.7",
   450  			Path:   filepath.Join(folder, "dist", "darwin_amd64", "bin", "foo-v5.6.7"),
   451  			Goos:   "darwin",
   452  			Goarch: "amd64",
   453  			Type:   artifact.Binary,
   454  			Extra: map[string]interface{}{
   455  				artifact.ExtraExt:    "",
   456  				artifact.ExtraBinary: "foo-v5.6.7",
   457  				artifact.ExtraID:     "foo",
   458  			},
   459  		},
   460  		{
   461  			Name:   "bin/foo-v5.6.7",
   462  			Path:   filepath.Join(folder, "dist", "linux_arm_6", "bin", "foo-v5.6.7"),
   463  			Goos:   "linux",
   464  			Goarch: "arm",
   465  			Goarm:  "6",
   466  			Type:   artifact.Binary,
   467  			Extra: map[string]interface{}{
   468  				artifact.ExtraExt:    "",
   469  				artifact.ExtraBinary: "foo-v5.6.7",
   470  				artifact.ExtraID:     "foo",
   471  			},
   472  		},
   473  		{
   474  			Name:   "bin/foo-v5.6.7.exe",
   475  			Path:   filepath.Join(folder, "dist", "windows_amd64", "bin", "foo-v5.6.7.exe"),
   476  			Goos:   "windows",
   477  			Goarch: "amd64",
   478  			Type:   artifact.Binary,
   479  			Extra: map[string]interface{}{
   480  				artifact.ExtraExt:    ".exe",
   481  				artifact.ExtraBinary: "foo-v5.6.7",
   482  				artifact.ExtraID:     "foo",
   483  			},
   484  		},
   485  		{
   486  			Name:   "bin/foo-v5.6.7.wasm",
   487  			Path:   filepath.Join(folder, "dist", "js_wasm", "bin", "foo-v5.6.7.wasm"),
   488  			Goos:   "js",
   489  			Goarch: "wasm",
   490  			Type:   artifact.Binary,
   491  			Extra: map[string]interface{}{
   492  				artifact.ExtraExt:    ".wasm",
   493  				artifact.ExtraBinary: "foo-v5.6.7",
   494  				artifact.ExtraID:     "foo",
   495  			},
   496  		},
   497  	})
   498  
   499  	modTimes := map[int64]bool{}
   500  	for _, bin := range ctx.Artifacts.List() {
   501  		if bin.Type != artifact.Binary {
   502  			continue
   503  		}
   504  
   505  		fi, err := os.Stat(bin.Path)
   506  		require.NoError(t, err)
   507  
   508  		// make this a suitable map key, per docs: https://golang.org/pkg/time/#Time
   509  		modTime := fi.ModTime().UTC().Round(0).Unix()
   510  
   511  		if modTimes[modTime] {
   512  			t.Fatal("duplicate modified time found, times should be different by default")
   513  		}
   514  		modTimes[modTime] = true
   515  	}
   516  }
   517  
   518  func TestBuildCodeInSubdir(t *testing.T) {
   519  	folder := testlib.Mktmp(t)
   520  	subdir := filepath.Join(folder, "bar")
   521  	err := os.Mkdir(subdir, 0o755)
   522  	require.NoError(t, err)
   523  	writeGoodMain(t, subdir)
   524  	config := config.Project{
   525  		Builds: []config.Build{
   526  			{
   527  				ID:     "foo",
   528  				Dir:    "bar",
   529  				Binary: "foo",
   530  				Targets: []string{
   531  					runtimeTarget,
   532  				},
   533  				GoBinary: "go",
   534  				Command:  "build",
   535  				BuildDetails: config.BuildDetails{
   536  					Env: []string{"GO111MODULE=off"},
   537  				},
   538  			},
   539  		},
   540  	}
   541  	ctx := context.New(config)
   542  	ctx.Git.CurrentTag = "5.6.7"
   543  	build := ctx.Config.Builds[0]
   544  	err = Default.Build(ctx, build, api.Options{
   545  		Target: runtimeTarget,
   546  		Name:   build.Binary,
   547  		Path:   filepath.Join(folder, "dist", runtimeTarget, build.Binary),
   548  		Ext:    "",
   549  	})
   550  	require.NoError(t, err)
   551  }
   552  
   553  func TestBuildWithDotGoDir(t *testing.T) {
   554  	folder := testlib.Mktmp(t)
   555  	require.NoError(t, os.Mkdir(filepath.Join(folder, ".go"), 0o755))
   556  	writeGoodMain(t, folder)
   557  	config := config.Project{
   558  		Builds: []config.Build{
   559  			{
   560  				ID:       "foo",
   561  				Binary:   "foo",
   562  				Targets:  []string{runtimeTarget},
   563  				GoBinary: "go",
   564  				Command:  "build",
   565  				BuildDetails: config.BuildDetails{
   566  					Env: []string{"GO111MODULE=off"},
   567  				},
   568  			},
   569  		},
   570  	}
   571  	ctx := context.New(config)
   572  	ctx.Git.CurrentTag = "5.6.7"
   573  	build := ctx.Config.Builds[0]
   574  	require.NoError(t, Default.Build(ctx, build, api.Options{
   575  		Target: runtimeTarget,
   576  		Name:   build.Binary,
   577  		Path:   filepath.Join(folder, "dist", runtimeTarget, build.Binary),
   578  		Ext:    "",
   579  	}))
   580  }
   581  
   582  func TestBuildFailed(t *testing.T) {
   583  	folder := testlib.Mktmp(t)
   584  	writeGoodMain(t, folder)
   585  	config := config.Project{
   586  		Builds: []config.Build{
   587  			{
   588  				ID: "buildid",
   589  				BuildDetails: config.BuildDetails{
   590  					Flags: []string{"-flag-that-dont-exists-to-force-failure"},
   591  				},
   592  				Targets: []string{
   593  					runtimeTarget,
   594  				},
   595  				GoBinary: "go",
   596  				Command:  "build",
   597  			},
   598  		},
   599  	}
   600  	ctx := context.New(config)
   601  	ctx.Git.CurrentTag = "5.6.7"
   602  	err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   603  		Target: "darwin_amd64",
   604  	})
   605  	assertContainsError(t, err, `flag provided but not defined: -flag-that-dont-exists-to-force-failure`)
   606  	require.Empty(t, ctx.Artifacts.List())
   607  }
   608  
   609  func TestRunInvalidAsmflags(t *testing.T) {
   610  	folder := testlib.Mktmp(t)
   611  	writeGoodMain(t, folder)
   612  	config := config.Project{
   613  		Builds: []config.Build{
   614  			{
   615  				Binary: "nametest",
   616  				BuildDetails: config.BuildDetails{
   617  					Asmflags: []string{"{{.Version}"},
   618  				},
   619  				Targets: []string{
   620  					runtimeTarget,
   621  				},
   622  			},
   623  		},
   624  	}
   625  	ctx := context.New(config)
   626  	ctx.Git.CurrentTag = "5.6.7"
   627  	err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   628  		Target: runtimeTarget,
   629  	})
   630  	testlib.RequireTemplateError(t, err)
   631  }
   632  
   633  func TestRunInvalidGcflags(t *testing.T) {
   634  	folder := testlib.Mktmp(t)
   635  	writeGoodMain(t, folder)
   636  	config := config.Project{
   637  		Builds: []config.Build{
   638  			{
   639  				Binary: "nametest",
   640  				BuildDetails: config.BuildDetails{
   641  					Gcflags: []string{"{{.Version}"},
   642  				},
   643  				Targets: []string{
   644  					runtimeTarget,
   645  				},
   646  			},
   647  		},
   648  	}
   649  	ctx := context.New(config)
   650  	ctx.Git.CurrentTag = "5.6.7"
   651  	err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   652  		Target: runtimeTarget,
   653  	})
   654  	testlib.RequireTemplateError(t, err)
   655  }
   656  
   657  func TestRunInvalidLdflags(t *testing.T) {
   658  	folder := testlib.Mktmp(t)
   659  	writeGoodMain(t, folder)
   660  	config := config.Project{
   661  		Builds: []config.Build{
   662  			{
   663  				Binary: "nametest",
   664  				BuildDetails: config.BuildDetails{
   665  					Flags:   []string{"-v"},
   666  					Ldflags: []string{"-s -w -X main.version={{.Version}"},
   667  				},
   668  				Targets: []string{
   669  					runtimeTarget,
   670  				},
   671  			},
   672  		},
   673  	}
   674  	ctx := context.New(config)
   675  	ctx.Git.CurrentTag = "5.6.7"
   676  	err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   677  		Target: runtimeTarget,
   678  	})
   679  	testlib.RequireTemplateError(t, err)
   680  }
   681  
   682  func TestRunInvalidFlags(t *testing.T) {
   683  	folder := testlib.Mktmp(t)
   684  	writeGoodMain(t, folder)
   685  	config := config.Project{
   686  		Builds: []config.Build{
   687  			{
   688  				Binary: "nametest",
   689  				BuildDetails: config.BuildDetails{
   690  					Flags: []string{"{{.Env.GOOS}"},
   691  				},
   692  				Targets: []string{
   693  					runtimeTarget,
   694  				},
   695  			},
   696  		},
   697  	}
   698  	ctx := context.New(config)
   699  	err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   700  		Target: runtimeTarget,
   701  	})
   702  	testlib.RequireTemplateError(t, err)
   703  }
   704  
   705  func TestRunPipeWithoutMainFunc(t *testing.T) {
   706  	newCtx := func(t *testing.T) *context.Context {
   707  		t.Helper()
   708  		folder := testlib.Mktmp(t)
   709  		writeMainWithoutMainFunc(t, folder)
   710  		config := config.Project{
   711  			Builds: []config.Build{{Binary: "no-main"}},
   712  		}
   713  		ctx := context.New(config)
   714  		ctx.Git.CurrentTag = "5.6.7"
   715  		return ctx
   716  	}
   717  	t.Run("empty", func(t *testing.T) {
   718  		ctx := newCtx(t)
   719  		ctx.Config.Builds[0].Main = ""
   720  		require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   721  			Target: runtimeTarget,
   722  		}), errNoMain{"no-main"}.Error())
   723  	})
   724  	t.Run("not main.go", func(t *testing.T) {
   725  		ctx := newCtx(t)
   726  		ctx.Config.Builds[0].Main = "foo.go"
   727  		require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   728  			Target: runtimeTarget,
   729  		}), `couldn't find main file: stat foo.go: no such file or directory`)
   730  	})
   731  	t.Run("glob", func(t *testing.T) {
   732  		ctx := newCtx(t)
   733  		ctx.Config.Builds[0].Main = "."
   734  		require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   735  			Target: runtimeTarget,
   736  		}), errNoMain{"no-main"}.Error())
   737  	})
   738  	t.Run("fixed main.go", func(t *testing.T) {
   739  		ctx := newCtx(t)
   740  		ctx.Config.Builds[0].Main = "main.go"
   741  		require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   742  			Target: runtimeTarget,
   743  		}), errNoMain{"no-main"}.Error())
   744  	})
   745  	t.Run("using gomod.proxy", func(t *testing.T) {
   746  		ctx := newCtx(t)
   747  		ctx.Config.GoMod.Proxy = true
   748  		ctx.Config.Builds[0].Dir = "dist/proxy/test"
   749  		ctx.Config.Builds[0].Main = "github.com/caarlos0/test"
   750  		ctx.Config.Builds[0].UnproxiedDir = "."
   751  		ctx.Config.Builds[0].UnproxiedMain = "."
   752  		require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   753  			Target: runtimeTarget,
   754  		}), errNoMain{"no-main"}.Error())
   755  	})
   756  }
   757  
   758  func TestBuildTests(t *testing.T) {
   759  	folder := testlib.Mktmp(t)
   760  	writeTest(t, folder)
   761  	config := config.Project{
   762  		Builds: []config.Build{{
   763  			Binary:  "foo.test",
   764  			Command: "test",
   765  			BuildDetails: config.BuildDetails{
   766  				Flags: []string{"-c"},
   767  			},
   768  		}},
   769  	}
   770  	ctx := context.New(config)
   771  	ctx.Git.CurrentTag = "5.6.7"
   772  	ctx.Config.Builds[0].NoMainCheck = true
   773  	build, err := Default.WithDefaults(config.Builds[0])
   774  	require.NoError(t, err)
   775  	require.NoError(t, Default.Build(ctx, build, api.Options{
   776  		Target: runtimeTarget,
   777  	}))
   778  }
   779  
   780  func TestRunPipeWithProxiedRepo(t *testing.T) {
   781  	folder := testlib.Mktmp(t)
   782  	out, err := exec.Command("git", "clone", "https://github.com/triarius/goreleaser", "-b", "v0.161.1", "--depth=1", ".").CombinedOutput()
   783  	require.NoError(t, err, string(out))
   784  
   785  	proxied := filepath.Join(folder, "dist/proxy/default")
   786  	require.NoError(t, os.MkdirAll(proxied, 0o750))
   787  	require.NoError(t, os.WriteFile(
   788  		filepath.Join(proxied, "main.go"),
   789  		[]byte(`// +build main
   790  package main
   791  
   792  import _ "github.com/triarius/goreleaser"
   793  `),
   794  		0o666,
   795  	))
   796  	require.NoError(t, os.WriteFile(
   797  		filepath.Join(proxied, "go.mod"),
   798  		[]byte("module foo\nrequire github.com/triarius/goreleaser v0.161.1"),
   799  		0o666,
   800  	))
   801  
   802  	cmd := exec.Command("go", "mod", "tidy")
   803  	cmd.Dir = proxied
   804  	require.NoError(t, cmd.Run())
   805  
   806  	config := config.Project{
   807  		GoMod: config.GoMod{
   808  			Proxy: true,
   809  		},
   810  		Builds: []config.Build{
   811  			{
   812  				Binary:        "foo",
   813  				Main:          "github.com/triarius/goreleaser",
   814  				Dir:           proxied,
   815  				UnproxiedMain: ".",
   816  				UnproxiedDir:  ".",
   817  				Targets: []string{
   818  					runtimeTarget,
   819  				},
   820  				GoBinary: "go",
   821  				Command:  "build",
   822  			},
   823  		},
   824  	}
   825  	ctx := context.New(config)
   826  
   827  	require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   828  		Target: runtimeTarget,
   829  	}))
   830  }
   831  
   832  func TestRunPipeWithMainFuncNotInMainGoFile(t *testing.T) {
   833  	folder := testlib.Mktmp(t)
   834  	require.NoError(t, os.WriteFile(
   835  		filepath.Join(folder, "foo.go"),
   836  		[]byte("package main\nfunc main() {println(0)}"),
   837  		0o644,
   838  	))
   839  	config := config.Project{
   840  		Builds: []config.Build{
   841  			{
   842  				Binary: "foo",
   843  				Hooks:  config.BuildHookConfig{},
   844  				Targets: []string{
   845  					runtimeTarget,
   846  				},
   847  				BuildDetails: config.BuildDetails{
   848  					Env: []string{"GO111MODULE=off"},
   849  				},
   850  				GoBinary: "go",
   851  				Command:  "build",
   852  			},
   853  		},
   854  	}
   855  	ctx := context.New(config)
   856  	ctx.Git.CurrentTag = "5.6.7"
   857  	t.Run("empty", func(t *testing.T) {
   858  		ctx.Config.Builds[0].Main = ""
   859  		require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   860  			Target: runtimeTarget,
   861  		}))
   862  	})
   863  	t.Run("foo.go", func(t *testing.T) {
   864  		ctx.Config.Builds[0].Main = "foo.go"
   865  		require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   866  			Target: runtimeTarget,
   867  		}))
   868  	})
   869  	t.Run("glob", func(t *testing.T) {
   870  		ctx.Config.Builds[0].Main = "."
   871  		require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   872  			Target: runtimeTarget,
   873  		}))
   874  	})
   875  }
   876  
   877  func TestLdFlagsFullTemplate(t *testing.T) {
   878  	run := time.Now().UTC()
   879  	commit := time.Now().AddDate(-1, 0, 0)
   880  
   881  	ctx := &context.Context{
   882  		Git: context.GitInfo{
   883  			CurrentTag: "v1.2.3",
   884  			Commit:     "123",
   885  			CommitDate: commit,
   886  		},
   887  		Date:    run,
   888  		Version: "1.2.3",
   889  		Env:     map[string]string{"FOO": "123"},
   890  	}
   891  	artifact := &artifact.Artifact{Goarch: "amd64"}
   892  	flags, err := tmpl.New(ctx).WithArtifact(artifact, map[string]string{}).
   893  		Apply(`-s -w -X main.version={{.Version}} -X main.tag={{.Tag}} -X main.date={{.Date}} -X main.commit={{.Commit}} -X "main.foo={{.Env.FOO}}" -X main.time={{ time "20060102" }} -X main.arch={{.Arch}} -X main.commitDate={{.CommitDate}}`)
   894  	require.NoError(t, err)
   895  	require.Contains(t, flags, "-s -w")
   896  	require.Contains(t, flags, "-X main.version=1.2.3")
   897  	require.Contains(t, flags, "-X main.tag=v1.2.3")
   898  	require.Contains(t, flags, "-X main.commit=123")
   899  	require.Contains(t, flags, fmt.Sprintf("-X main.date=%d", run.Year()))
   900  	require.Contains(t, flags, fmt.Sprintf("-X main.time=%d", run.Year()))
   901  	require.Contains(t, flags, `-X "main.foo=123"`)
   902  	require.Contains(t, flags, `-X main.arch=amd64`)
   903  	require.Contains(t, flags, fmt.Sprintf("-X main.commitDate=%d", commit.Year()))
   904  }
   905  
   906  func TestInvalidTemplate(t *testing.T) {
   907  	for _, template := range []string{
   908  		"{{ .Nope }",
   909  		"{{.Env.NOPE}}",
   910  	} {
   911  		t.Run(template, func(t *testing.T) {
   912  			ctx := context.New(config.Project{})
   913  			ctx.Git.CurrentTag = "3.4.1"
   914  			flags, err := tmpl.New(ctx).Apply(template)
   915  			testlib.RequireTemplateError(t, err)
   916  			require.Empty(t, flags)
   917  		})
   918  	}
   919  }
   920  
   921  func TestProcessFlags(t *testing.T) {
   922  	ctx := &context.Context{
   923  		Version: "1.2.3",
   924  	}
   925  	ctx.Git.CurrentTag = "5.6.7"
   926  
   927  	artifact := &artifact.Artifact{
   928  		Name:   "name",
   929  		Goos:   "darwin",
   930  		Goarch: "amd64",
   931  		Goarm:  "7",
   932  		Extra: map[string]interface{}{
   933  			artifact.ExtraBinary: "binary",
   934  		},
   935  	}
   936  
   937  	source := []string{
   938  		"flag",
   939  		"{{.Version}}",
   940  		"{{.Os}}",
   941  		"{{.Arch}}",
   942  		"{{.Arm}}",
   943  		"{{.Binary}}",
   944  		"{{.ArtifactName}}",
   945  	}
   946  
   947  	expected := []string{
   948  		"-testflag=flag",
   949  		"-testflag=1.2.3",
   950  		"-testflag=darwin",
   951  		"-testflag=amd64",
   952  		"-testflag=7",
   953  		"-testflag=binary",
   954  		"-testflag=name",
   955  	}
   956  
   957  	flags, err := processFlags(ctx, artifact, []string{}, source, "-testflag=")
   958  	require.NoError(t, err)
   959  	require.Len(t, flags, 7)
   960  	require.Equal(t, expected, flags)
   961  }
   962  
   963  func TestProcessFlagsInvalid(t *testing.T) {
   964  	ctx := &context.Context{}
   965  
   966  	source := []string{
   967  		"{{.Version}",
   968  	}
   969  
   970  	flags, err := processFlags(ctx, &artifact.Artifact{}, []string{}, source, "-testflag=")
   971  	testlib.RequireTemplateError(t, err)
   972  	require.Nil(t, flags)
   973  }
   974  
   975  func TestBuildModTimestamp(t *testing.T) {
   976  	// round to seconds since this will be a unix timestamp
   977  	modTime := time.Now().AddDate(-1, 0, 0).Round(1 * time.Second).UTC()
   978  
   979  	folder := testlib.Mktmp(t)
   980  	writeGoodMain(t, folder)
   981  
   982  	config := config.Project{
   983  		Builds: []config.Build{
   984  			{
   985  				ID:     "foo",
   986  				Binary: "bin/foo-{{ .Version }}",
   987  				Targets: []string{
   988  					"linux_amd64",
   989  					"darwin_amd64",
   990  					"windows_amd64",
   991  					"linux_arm_6",
   992  					"js_wasm",
   993  					"linux_mips_softfloat",
   994  					"linux_mips64le_softfloat",
   995  				},
   996  				BuildDetails: config.BuildDetails{
   997  					Env:      []string{"GO111MODULE=off"},
   998  					Asmflags: []string{".=", "all="},
   999  					Gcflags:  []string{"all="},
  1000  					Flags:    []string{"{{.Env.GO_FLAGS}}"},
  1001  				},
  1002  				ModTimestamp: fmt.Sprintf("%d", modTime.Unix()),
  1003  				GoBinary:     "go",
  1004  				Command:      "build",
  1005  			},
  1006  		},
  1007  	}
  1008  	ctx := context.New(config)
  1009  	ctx.Env["GO_FLAGS"] = "-v"
  1010  	ctx.Git.CurrentTag = "v5.6.7"
  1011  	ctx.Version = ctx.Git.CurrentTag
  1012  	build := ctx.Config.Builds[0]
  1013  	for _, target := range build.Targets {
  1014  		var ext string
  1015  		if strings.HasPrefix(target, "windows") {
  1016  			ext = ".exe"
  1017  		} else if target == "js_wasm" {
  1018  			ext = ".wasm"
  1019  		}
  1020  		bin, terr := tmpl.New(ctx).Apply(build.Binary)
  1021  		require.NoError(t, terr)
  1022  
  1023  		// injecting some delay here to force inconsistent mod times on bins
  1024  		time.Sleep(2 * time.Second)
  1025  
  1026  		err := Default.Build(ctx, build, api.Options{
  1027  			Target: target,
  1028  			Name:   bin + ext,
  1029  			Path:   filepath.Join(folder, "dist", target, bin+ext),
  1030  			Ext:    ext,
  1031  		})
  1032  		require.NoError(t, err)
  1033  	}
  1034  
  1035  	for _, bin := range ctx.Artifacts.List() {
  1036  		if bin.Type != artifact.Binary {
  1037  			continue
  1038  		}
  1039  
  1040  		fi, err := os.Stat(bin.Path)
  1041  		require.NoError(t, err)
  1042  		require.True(t, modTime.Equal(fi.ModTime()), "inconsistent mod times found when specifying ModTimestamp")
  1043  	}
  1044  }
  1045  
  1046  func TestBuildGoBuildLine(t *testing.T) {
  1047  	requireEqualCmd := func(tb testing.TB, build config.Build, expected []string) {
  1048  		tb.Helper()
  1049  		cfg := config.Project{
  1050  			Builds: []config.Build{build},
  1051  		}
  1052  		ctx := context.New(cfg)
  1053  		ctx.Version = "1.2.3"
  1054  		ctx.Git.Commit = "aaa"
  1055  
  1056  		options := api.Options{
  1057  			Path:   cfg.Builds[0].Binary,
  1058  			Goos:   "linux",
  1059  			Goarch: "amd64",
  1060  		}
  1061  
  1062  		dets, err := withOverrides(ctx, build, options)
  1063  		require.NoError(t, err)
  1064  
  1065  		line, err := buildGoBuildLine(
  1066  			ctx,
  1067  			build,
  1068  			dets,
  1069  			options,
  1070  			&artifact.Artifact{},
  1071  			[]string{},
  1072  		)
  1073  		require.NoError(t, err)
  1074  		require.Equal(t, expected, line)
  1075  	}
  1076  
  1077  	t.Run("full", func(t *testing.T) {
  1078  		requireEqualCmd(t, config.Build{
  1079  			Main: ".",
  1080  			BuildDetails: config.BuildDetails{
  1081  				Asmflags: []string{"asmflag1", "asmflag2"},
  1082  				Gcflags:  []string{"gcflag1", "gcflag2"},
  1083  				Flags:    []string{"-flag1", "-flag2"},
  1084  				Tags:     []string{"tag1", "tag2"},
  1085  				Ldflags:  []string{"ldflag1", "ldflag2"},
  1086  			},
  1087  			Binary:   "foo",
  1088  			GoBinary: "go",
  1089  			Command:  "build",
  1090  		}, []string{
  1091  			"go", "build",
  1092  			"-flag1", "-flag2",
  1093  			"-asmflags=asmflag1", "-asmflags=asmflag2",
  1094  			"-gcflags=gcflag1", "-gcflags=gcflag2",
  1095  			"-tags=tag1,tag2",
  1096  			"-ldflags=ldflag1 ldflag2",
  1097  			"-o", "foo", ".",
  1098  		})
  1099  	})
  1100  
  1101  	t.Run("with overrides", func(t *testing.T) {
  1102  		requireEqualCmd(t, config.Build{
  1103  			Main: ".",
  1104  			BuildDetails: config.BuildDetails{
  1105  				Asmflags: []string{"asmflag1", "asmflag2"},
  1106  				Gcflags:  []string{"gcflag1", "gcflag2"},
  1107  				Flags:    []string{"-flag1", "-flag2"},
  1108  				Tags:     []string{"tag1", "tag2"},
  1109  				Ldflags:  []string{"ldflag1", "ldflag2"},
  1110  			},
  1111  			BuildDetailsOverrides: []config.BuildDetailsOverride{
  1112  				{
  1113  					Goos:   "linux",
  1114  					Goarch: "amd64",
  1115  					BuildDetails: config.BuildDetails{
  1116  						Asmflags: []string{"asmflag3"},
  1117  						Gcflags:  []string{"gcflag3"},
  1118  						Flags:    []string{"-flag3"},
  1119  						Tags:     []string{"tag3"},
  1120  						Ldflags:  []string{"ldflag3"},
  1121  					},
  1122  				},
  1123  			},
  1124  			GoBinary: "go",
  1125  			Binary:   "foo",
  1126  			Command:  "build",
  1127  		}, []string{
  1128  			"go", "build",
  1129  			"-flag3",
  1130  			"-asmflags=asmflag3",
  1131  			"-gcflags=gcflag3",
  1132  			"-tags=tag3",
  1133  			"-ldflags=ldflag3",
  1134  			"-o", "foo", ".",
  1135  		})
  1136  	})
  1137  
  1138  	t.Run("simple", func(t *testing.T) {
  1139  		requireEqualCmd(t, config.Build{
  1140  			Main:     ".",
  1141  			GoBinary: "go",
  1142  			Command:  "build",
  1143  			Binary:   "foo",
  1144  		}, strings.Fields("go build -o foo ."))
  1145  	})
  1146  
  1147  	t.Run("test", func(t *testing.T) {
  1148  		requireEqualCmd(t, config.Build{
  1149  			Main:     ".",
  1150  			GoBinary: "go",
  1151  			Command:  "test",
  1152  			Binary:   "foo.test",
  1153  			BuildDetails: config.BuildDetails{
  1154  				Flags: []string{"-c"},
  1155  			},
  1156  		}, strings.Fields("go test -c -o foo.test ."))
  1157  	})
  1158  
  1159  	t.Run("ldflags1", func(t *testing.T) {
  1160  		requireEqualCmd(t, config.Build{
  1161  			Main: ".",
  1162  			BuildDetails: config.BuildDetails{
  1163  				Ldflags: []string{"-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.builtBy=goreleaser"},
  1164  			},
  1165  			GoBinary: "go",
  1166  			Command:  "build",
  1167  			Binary:   "foo",
  1168  		}, []string{
  1169  			"go", "build",
  1170  			"-ldflags=-s -w -X main.version=1.2.3 -X main.commit=aaa -X main.builtBy=goreleaser",
  1171  			"-o", "foo", ".",
  1172  		})
  1173  	})
  1174  
  1175  	t.Run("ldflags2", func(t *testing.T) {
  1176  		requireEqualCmd(t, config.Build{
  1177  			Main: ".",
  1178  			BuildDetails: config.BuildDetails{
  1179  				Ldflags: []string{"-s -w", "-X main.version={{.Version}}"},
  1180  			},
  1181  			GoBinary: "go",
  1182  			Binary:   "foo",
  1183  			Command:  "build",
  1184  		}, []string{"go", "build", "-ldflags=-s -w -X main.version=1.2.3", "-o", "foo", "."})
  1185  	})
  1186  }
  1187  
  1188  func TestOverrides(t *testing.T) {
  1189  	t.Run("linux amd64", func(t *testing.T) {
  1190  		dets, err := withOverrides(
  1191  			context.New(config.Project{}),
  1192  			config.Build{
  1193  				BuildDetails: config.BuildDetails{
  1194  					Ldflags: []string{"original"},
  1195  					Env:     []string{"BAR=foo", "FOO=bar"},
  1196  				},
  1197  				BuildDetailsOverrides: []config.BuildDetailsOverride{
  1198  					{
  1199  						Goos:   "linux",
  1200  						Goarch: "amd64",
  1201  						BuildDetails: config.BuildDetails{
  1202  							Ldflags: []string{"overridden"},
  1203  							Env:     []string{"FOO=overridden"},
  1204  						},
  1205  					},
  1206  				},
  1207  			}, api.Options{
  1208  				Goos:   "linux",
  1209  				Goarch: "amd64",
  1210  			},
  1211  		)
  1212  		require.NoError(t, err)
  1213  		require.ElementsMatch(t, dets.Ldflags, []string{"overridden"})
  1214  		require.ElementsMatch(t, dets.Env, []string{"BAR=foo", "FOO=overridden"})
  1215  	})
  1216  
  1217  	t.Run("single sided", func(t *testing.T) {
  1218  		dets, err := withOverrides(
  1219  			context.New(config.Project{}),
  1220  			config.Build{
  1221  				BuildDetails: config.BuildDetails{},
  1222  				BuildDetailsOverrides: []config.BuildDetailsOverride{
  1223  					{
  1224  						Goos:   "linux",
  1225  						Goarch: "amd64",
  1226  						BuildDetails: config.BuildDetails{
  1227  							Ldflags:  []string{"overridden"},
  1228  							Tags:     []string{"tag1"},
  1229  							Asmflags: []string{"asm1"},
  1230  							Gcflags:  []string{"gcflag1"},
  1231  						},
  1232  					},
  1233  				},
  1234  			}, api.Options{
  1235  				Goos:   "linux",
  1236  				Goarch: "amd64",
  1237  			},
  1238  		)
  1239  		require.NoError(t, err)
  1240  		require.Equal(t, dets, config.BuildDetails{
  1241  			Ldflags:  []string{"overridden"},
  1242  			Gcflags:  []string{"gcflag1"},
  1243  			Asmflags: []string{"asm1"},
  1244  			Tags:     []string{"tag1"},
  1245  			Env:      []string{},
  1246  		})
  1247  	})
  1248  
  1249  	t.Run("with template", func(t *testing.T) {
  1250  		dets, err := withOverrides(
  1251  			context.New(config.Project{}),
  1252  			config.Build{
  1253  				BuildDetails: config.BuildDetails{
  1254  					Ldflags:  []string{"original"},
  1255  					Asmflags: []string{"asm1"},
  1256  				},
  1257  				BuildDetailsOverrides: []config.BuildDetailsOverride{
  1258  					{
  1259  						Goos:   "{{ .Runtime.Goos }}",
  1260  						Goarch: "{{ .Runtime.Goarch }}",
  1261  						BuildDetails: config.BuildDetails{
  1262  							Ldflags: []string{"overridden"},
  1263  						},
  1264  					},
  1265  				},
  1266  			}, api.Options{
  1267  				Goos:   runtime.GOOS,
  1268  				Goarch: runtime.GOARCH,
  1269  			},
  1270  		)
  1271  		require.NoError(t, err)
  1272  		require.Equal(t, dets, config.BuildDetails{
  1273  			Ldflags:  []string{"overridden"},
  1274  			Asmflags: []string{"asm1"},
  1275  			Env:      []string{},
  1276  		})
  1277  	})
  1278  
  1279  	t.Run("with invalid template", func(t *testing.T) {
  1280  		_, err := withOverrides(
  1281  			context.New(config.Project{}),
  1282  			config.Build{
  1283  				BuildDetailsOverrides: []config.BuildDetailsOverride{
  1284  					{
  1285  						Goos: "{{ .Runtime.Goos }",
  1286  					},
  1287  				},
  1288  			}, api.Options{
  1289  				Goos:   runtime.GOOS,
  1290  				Goarch: runtime.GOARCH,
  1291  			},
  1292  		)
  1293  		testlib.RequireTemplateError(t, err)
  1294  	})
  1295  
  1296  	t.Run("with goarm", func(t *testing.T) {
  1297  		dets, err := withOverrides(
  1298  			context.New(config.Project{}),
  1299  			config.Build{
  1300  				BuildDetails: config.BuildDetails{
  1301  					Ldflags: []string{"original"},
  1302  				},
  1303  				BuildDetailsOverrides: []config.BuildDetailsOverride{
  1304  					{
  1305  						Goos:   "linux",
  1306  						Goarch: "arm",
  1307  						Goarm:  "6",
  1308  						BuildDetails: config.BuildDetails{
  1309  							Ldflags: []string{"overridden"},
  1310  						},
  1311  					},
  1312  				},
  1313  			}, api.Options{
  1314  				Goos:   "linux",
  1315  				Goarch: "arm",
  1316  				Goarm:  "6",
  1317  			},
  1318  		)
  1319  		require.NoError(t, err)
  1320  		require.Equal(t, dets, config.BuildDetails{
  1321  			Ldflags: []string{"overridden"},
  1322  			Env:     []string{},
  1323  		})
  1324  	})
  1325  
  1326  	t.Run("with gomips", func(t *testing.T) {
  1327  		dets, err := withOverrides(
  1328  			context.New(config.Project{}),
  1329  			config.Build{
  1330  				BuildDetails: config.BuildDetails{
  1331  					Ldflags: []string{"original"},
  1332  				},
  1333  				BuildDetailsOverrides: []config.BuildDetailsOverride{
  1334  					{
  1335  						Goos:   "linux",
  1336  						Goarch: "mips",
  1337  						Gomips: "softfloat",
  1338  						BuildDetails: config.BuildDetails{
  1339  							Ldflags: []string{"overridden"},
  1340  						},
  1341  					},
  1342  				},
  1343  			}, api.Options{
  1344  				Goos:   "linux",
  1345  				Goarch: "mips",
  1346  				Gomips: "softfloat",
  1347  			},
  1348  		)
  1349  		require.NoError(t, err)
  1350  		require.Equal(t, dets, config.BuildDetails{
  1351  			Ldflags: []string{"overridden"},
  1352  			Env:     []string{},
  1353  		})
  1354  	})
  1355  }
  1356  
  1357  //
  1358  // Helpers
  1359  //
  1360  
  1361  func writeMainWithoutMainFunc(t *testing.T, folder string) {
  1362  	t.Helper()
  1363  	require.NoError(t, os.WriteFile(
  1364  		filepath.Join(folder, "main.go"),
  1365  		[]byte("package main\nconst a = 2\nfunc notMain() {println(0)}"),
  1366  		0o644,
  1367  	))
  1368  }
  1369  
  1370  func writeGoodMain(t *testing.T, folder string) {
  1371  	t.Helper()
  1372  	require.NoError(t, os.WriteFile(
  1373  		filepath.Join(folder, "main.go"),
  1374  		[]byte("package main\nvar a = 1\nfunc main() {println(0)}"),
  1375  		0o644,
  1376  	))
  1377  }
  1378  
  1379  func writeTest(t *testing.T, folder string) {
  1380  	t.Helper()
  1381  	require.NoError(t, os.WriteFile(
  1382  		filepath.Join(folder, "main_test.go"),
  1383  		[]byte("package main\nimport\"testing\"\nfunc TestFoo(t *testing.T) {t.Log(\"OK\")}"),
  1384  		0o644,
  1385  	))
  1386  	require.NoError(t, os.WriteFile(
  1387  		filepath.Join(folder, "go.mod"),
  1388  		[]byte("module foo\n"),
  1389  		0o666,
  1390  	))
  1391  }
  1392  
  1393  func assertContainsError(t *testing.T, err error, s string) {
  1394  	t.Helper()
  1395  	require.Error(t, err)
  1396  	require.Contains(t, err.Error(), s)
  1397  }