github.com/amane3/goreleaser@v0.182.0/internal/builders/golang/build_test.go (about)

     1  package golang
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"runtime"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/amane3/goreleaser/internal/artifact"
    14  	"github.com/amane3/goreleaser/internal/testlib"
    15  	"github.com/amane3/goreleaser/internal/tmpl"
    16  	api "github.com/amane3/goreleaser/pkg/build"
    17  	"github.com/amane3/goreleaser/pkg/config"
    18  	"github.com/amane3/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  				GoBinary: "go1.2.3",
    51  			},
    52  			targets: []string{
    53  				"linux_amd64",
    54  				"linux_mips_softfloat",
    55  				"darwin_amd64",
    56  				"windows_amd64",
    57  				"linux_arm_6",
    58  			},
    59  			goBinary: "go1.2.3",
    60  		},
    61  		"empty": {
    62  			build: config.Build{
    63  				ID:     "foo2",
    64  				Binary: "foo",
    65  			},
    66  			targets: []string{
    67  				"linux_amd64",
    68  				"linux_386",
    69  				"darwin_amd64",
    70  			},
    71  			goBinary: "go",
    72  		},
    73  		"custom targets": {
    74  			build: config.Build{
    75  				ID:     "foo3",
    76  				Binary: "foo",
    77  				Targets: []string{
    78  					"linux_386",
    79  					"darwin_amd64",
    80  				},
    81  			},
    82  			targets: []string{
    83  				"linux_386",
    84  				"darwin_amd64",
    85  			},
    86  			goBinary: "go",
    87  		},
    88  	} {
    89  		t.Run(name, func(tt *testing.T) {
    90  			var config = config.Project{
    91  				Builds: []config.Build{
    92  					testcase.build,
    93  				},
    94  			}
    95  			var ctx = context.New(config)
    96  			ctx.Git.CurrentTag = "5.6.7"
    97  			build, err := Default.WithDefaults(ctx.Config.Builds[0])
    98  			require.NoError(t, err)
    99  			require.ElementsMatch(t, build.Targets, testcase.targets)
   100  			require.EqualValues(t, testcase.goBinary, build.GoBinary)
   101  		})
   102  	}
   103  }
   104  
   105  func TestInvalidTargets(t *testing.T) {
   106  	type testcase struct {
   107  		build       config.Build
   108  		expectedErr string
   109  	}
   110  	for s, tc := range map[string]testcase{
   111  		"goos": {
   112  			build: config.Build{
   113  				Goos: []string{"darwin", "darwim"},
   114  			},
   115  			expectedErr: "invalid goos: darwim",
   116  		},
   117  		"goarch": {
   118  			build: config.Build{
   119  				Goarch: []string{"amd64", "i386", "386"},
   120  			},
   121  			expectedErr: "invalid goarch: i386",
   122  		},
   123  		"goarm": {
   124  			build: config.Build{
   125  				Goarch: []string{"arm"},
   126  				Goarm:  []string{"6", "9", "8", "7"},
   127  			},
   128  			expectedErr: "invalid goarm: 9",
   129  		},
   130  		"gomips": {
   131  			build: config.Build{
   132  				Goarch: []string{"mips"},
   133  				Gomips: []string{"softfloat", "mehfloat", "hardfloat"},
   134  			},
   135  			expectedErr: "invalid gomips: mehfloat",
   136  		},
   137  	} {
   138  		t.Run(s, func(t *testing.T) {
   139  			var config = config.Project{
   140  				Builds: []config.Build{
   141  					tc.build,
   142  				},
   143  			}
   144  			var ctx = context.New(config)
   145  			_, err := Default.WithDefaults(ctx.Config.Builds[0])
   146  			require.EqualError(t, err, tc.expectedErr)
   147  		})
   148  	}
   149  }
   150  
   151  func TestBuild(t *testing.T) {
   152  	var folder = testlib.Mktmp(t)
   153  	writeGoodMain(t, folder)
   154  	var config = config.Project{
   155  		Builds: []config.Build{
   156  			{
   157  				ID:     "foo",
   158  				Env:    []string{"GO111MODULE=off"},
   159  				Binary: "bin/foo-{{ .Version }}",
   160  				Targets: []string{
   161  					"linux_amd64",
   162  					"darwin_amd64",
   163  					"windows_amd64",
   164  					"linux_arm_6",
   165  					"js_wasm",
   166  					"linux_mips_softfloat",
   167  					"linux_mips64le_softfloat",
   168  				},
   169  				Asmflags: []string{".=", "all="},
   170  				Gcflags:  []string{"all="},
   171  				Flags:    []string{"{{.Env.GO_FLAGS}}"},
   172  				GoBinary: "go",
   173  			},
   174  		},
   175  	}
   176  	var ctx = context.New(config)
   177  	ctx.Env["GO_FLAGS"] = "-v"
   178  	ctx.Git.CurrentTag = "5.6.7"
   179  	ctx.Version = "v" + ctx.Git.CurrentTag
   180  	var build = ctx.Config.Builds[0]
   181  	for _, target := range build.Targets {
   182  		var ext string
   183  		if strings.HasPrefix(target, "windows") {
   184  			ext = ".exe"
   185  		} else if target == "js_wasm" {
   186  			ext = ".wasm"
   187  		}
   188  		bin, terr := tmpl.New(ctx).Apply(build.Binary)
   189  		require.NoError(t, terr)
   190  
   191  		// injecting some delay here to force inconsistent mod times on bins
   192  		time.Sleep(2 * time.Second)
   193  
   194  		var err = Default.Build(ctx, build, api.Options{
   195  			Target: target,
   196  			Name:   bin + ext,
   197  			Path:   filepath.Join(folder, "dist", target, bin+ext),
   198  			Ext:    ext,
   199  		})
   200  		require.NoError(t, err)
   201  	}
   202  	require.ElementsMatch(t, ctx.Artifacts.List(), []*artifact.Artifact{
   203  		{
   204  			Name:   "bin/foo-v5.6.7",
   205  			Path:   filepath.Join(folder, "dist", "linux_amd64", "bin", "foo-v5.6.7"),
   206  			Goos:   "linux",
   207  			Goarch: "amd64",
   208  			Type:   artifact.Binary,
   209  			Extra: map[string]interface{}{
   210  				"Ext":    "",
   211  				"Binary": "foo-v5.6.7",
   212  				"ID":     "foo",
   213  			},
   214  		},
   215  		{
   216  			Name:   "bin/foo-v5.6.7",
   217  			Path:   filepath.Join(folder, "dist", "linux_mips_softfloat", "bin", "foo-v5.6.7"),
   218  			Goos:   "linux",
   219  			Goarch: "mips",
   220  			Gomips: "softfloat",
   221  			Type:   artifact.Binary,
   222  			Extra: map[string]interface{}{
   223  				"Ext":    "",
   224  				"Binary": "foo-v5.6.7",
   225  				"ID":     "foo",
   226  			},
   227  		},
   228  		{
   229  			Name:   "bin/foo-v5.6.7",
   230  			Path:   filepath.Join(folder, "dist", "linux_mips64le_softfloat", "bin", "foo-v5.6.7"),
   231  			Goos:   "linux",
   232  			Goarch: "mips64le",
   233  			Gomips: "softfloat",
   234  			Type:   artifact.Binary,
   235  			Extra: map[string]interface{}{
   236  				"Ext":    "",
   237  				"Binary": "foo-v5.6.7",
   238  				"ID":     "foo",
   239  			},
   240  		},
   241  		{
   242  			Name:   "bin/foo-v5.6.7",
   243  			Path:   filepath.Join(folder, "dist", "darwin_amd64", "bin", "foo-v5.6.7"),
   244  			Goos:   "darwin",
   245  			Goarch: "amd64",
   246  			Type:   artifact.Binary,
   247  			Extra: map[string]interface{}{
   248  				"Ext":    "",
   249  				"Binary": "foo-v5.6.7",
   250  				"ID":     "foo",
   251  			},
   252  		},
   253  		{
   254  			Name:   "bin/foo-v5.6.7",
   255  			Path:   filepath.Join(folder, "dist", "linux_arm_6", "bin", "foo-v5.6.7"),
   256  			Goos:   "linux",
   257  			Goarch: "arm",
   258  			Goarm:  "6",
   259  			Type:   artifact.Binary,
   260  			Extra: map[string]interface{}{
   261  				"Ext":    "",
   262  				"Binary": "foo-v5.6.7",
   263  				"ID":     "foo",
   264  			},
   265  		},
   266  		{
   267  			Name:   "bin/foo-v5.6.7.exe",
   268  			Path:   filepath.Join(folder, "dist", "windows_amd64", "bin", "foo-v5.6.7.exe"),
   269  			Goos:   "windows",
   270  			Goarch: "amd64",
   271  			Type:   artifact.Binary,
   272  			Extra: map[string]interface{}{
   273  				"Ext":    ".exe",
   274  				"Binary": "foo-v5.6.7",
   275  				"ID":     "foo",
   276  			},
   277  		},
   278  		{
   279  			Name:   "bin/foo-v5.6.7.wasm",
   280  			Path:   filepath.Join(folder, "dist", "js_wasm", "bin", "foo-v5.6.7.wasm"),
   281  			Goos:   "js",
   282  			Goarch: "wasm",
   283  			Type:   artifact.Binary,
   284  			Extra: map[string]interface{}{
   285  				"Ext":    ".wasm",
   286  				"Binary": "foo-v5.6.7",
   287  				"ID":     "foo",
   288  			},
   289  		},
   290  	})
   291  
   292  	modTimes := map[time.Time]bool{}
   293  	for _, bin := range ctx.Artifacts.List() {
   294  		if bin.Type != artifact.Binary {
   295  			continue
   296  		}
   297  
   298  		fi, err := os.Stat(bin.Path)
   299  		require.NoError(t, err)
   300  
   301  		// make this a suitable map key, per docs: https://golang.org/pkg/time/#Time
   302  		modTime := fi.ModTime().UTC().Round(0)
   303  
   304  		if modTimes[modTime] {
   305  			t.Fatal("duplicate modified time found, times should be different by default")
   306  		}
   307  		modTimes[modTime] = true
   308  	}
   309  }
   310  
   311  func TestBuildCodeInSubdir(t *testing.T) {
   312  	var folder = testlib.Mktmp(t)
   313  	subdir := filepath.Join(folder, "bar")
   314  	err := os.Mkdir(subdir, 0755)
   315  	require.NoError(t, err)
   316  	writeGoodMain(t, subdir)
   317  	var config = config.Project{
   318  		Builds: []config.Build{
   319  			{
   320  				ID:     "foo",
   321  				Env:    []string{"GO111MODULE=off"},
   322  				Dir:    "bar",
   323  				Binary: "foo",
   324  				Targets: []string{
   325  					runtimeTarget,
   326  				},
   327  				GoBinary: "go",
   328  			},
   329  		},
   330  	}
   331  	var ctx = context.New(config)
   332  	ctx.Git.CurrentTag = "5.6.7"
   333  	var build = ctx.Config.Builds[0]
   334  	err = Default.Build(ctx, build, api.Options{
   335  		Target: runtimeTarget,
   336  		Name:   build.Binary,
   337  		Path:   filepath.Join(folder, "dist", runtimeTarget, build.Binary),
   338  		Ext:    "",
   339  	})
   340  	require.NoError(t, err)
   341  }
   342  
   343  func TestBuildWithDotGoDir(t *testing.T) {
   344  	var folder = testlib.Mktmp(t)
   345  	require.NoError(t, os.Mkdir(filepath.Join(folder, ".go"), 0755))
   346  	writeGoodMain(t, folder)
   347  	var config = config.Project{
   348  		Builds: []config.Build{
   349  			{
   350  				ID:       "foo",
   351  				Env:      []string{"GO111MODULE=off"},
   352  				Binary:   "foo",
   353  				Targets:  []string{runtimeTarget},
   354  				GoBinary: "go",
   355  			},
   356  		},
   357  	}
   358  	var ctx = context.New(config)
   359  	ctx.Git.CurrentTag = "5.6.7"
   360  	var build = ctx.Config.Builds[0]
   361  	require.NoError(t, Default.Build(ctx, build, api.Options{
   362  		Target: runtimeTarget,
   363  		Name:   build.Binary,
   364  		Path:   filepath.Join(folder, "dist", runtimeTarget, build.Binary),
   365  		Ext:    "",
   366  	}))
   367  }
   368  
   369  func TestBuildFailed(t *testing.T) {
   370  	var folder = testlib.Mktmp(t)
   371  	writeGoodMain(t, folder)
   372  	var config = config.Project{
   373  		Builds: []config.Build{
   374  			{
   375  				ID:    "buildid",
   376  				Flags: []string{"-flag-that-dont-exists-to-force-failure"},
   377  				Targets: []string{
   378  					runtimeTarget,
   379  				},
   380  				GoBinary: "go",
   381  			},
   382  		},
   383  	}
   384  	var ctx = context.New(config)
   385  	ctx.Git.CurrentTag = "5.6.7"
   386  	var err = Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   387  		Target: "darwin_amd64",
   388  	})
   389  	assertContainsError(t, err, `flag provided but not defined: -flag-that-dont-exists-to-force-failure`)
   390  	require.Empty(t, ctx.Artifacts.List())
   391  }
   392  
   393  func TestBuildInvalidTarget(t *testing.T) {
   394  	var folder = testlib.Mktmp(t)
   395  	writeGoodMain(t, folder)
   396  	var target = "linux"
   397  	var config = config.Project{
   398  		Builds: []config.Build{
   399  			{
   400  				ID:      "foo",
   401  				Binary:  "foo",
   402  				Targets: []string{target},
   403  			},
   404  		},
   405  	}
   406  	var ctx = context.New(config)
   407  	ctx.Git.CurrentTag = "5.6.7"
   408  	var build = ctx.Config.Builds[0]
   409  	var err = Default.Build(ctx, build, api.Options{
   410  		Target: target,
   411  		Name:   build.Binary,
   412  		Path:   filepath.Join(folder, "dist", target, build.Binary),
   413  	})
   414  	require.EqualError(t, err, "linux is not a valid build target")
   415  	require.Len(t, ctx.Artifacts.List(), 0)
   416  }
   417  
   418  func TestRunInvalidAsmflags(t *testing.T) {
   419  	var folder = testlib.Mktmp(t)
   420  	writeGoodMain(t, folder)
   421  	var config = config.Project{
   422  		Builds: []config.Build{
   423  			{
   424  				Binary:   "nametest",
   425  				Asmflags: []string{"{{.Version}"},
   426  				Targets: []string{
   427  					runtimeTarget,
   428  				},
   429  			},
   430  		},
   431  	}
   432  	var ctx = context.New(config)
   433  	ctx.Git.CurrentTag = "5.6.7"
   434  	var err = Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   435  		Target: runtimeTarget,
   436  	})
   437  	require.EqualError(t, err, `template: tmpl:1: unexpected "}" in operand`)
   438  }
   439  
   440  func TestRunInvalidGcflags(t *testing.T) {
   441  	var folder = testlib.Mktmp(t)
   442  	writeGoodMain(t, folder)
   443  	var config = config.Project{
   444  		Builds: []config.Build{
   445  			{
   446  				Binary:  "nametest",
   447  				Gcflags: []string{"{{.Version}"},
   448  				Targets: []string{
   449  					runtimeTarget,
   450  				},
   451  			},
   452  		},
   453  	}
   454  	var ctx = context.New(config)
   455  	ctx.Git.CurrentTag = "5.6.7"
   456  	var err = Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   457  		Target: runtimeTarget,
   458  	})
   459  	require.EqualError(t, err, `template: tmpl:1: unexpected "}" in operand`)
   460  }
   461  
   462  func TestRunInvalidLdflags(t *testing.T) {
   463  	var folder = testlib.Mktmp(t)
   464  	writeGoodMain(t, folder)
   465  	var config = config.Project{
   466  		Builds: []config.Build{
   467  			{
   468  				Binary:  "nametest",
   469  				Flags:   []string{"-v"},
   470  				Ldflags: []string{"-s -w -X main.version={{.Version}"},
   471  				Targets: []string{
   472  					runtimeTarget,
   473  				},
   474  			},
   475  		},
   476  	}
   477  	var ctx = context.New(config)
   478  	ctx.Git.CurrentTag = "5.6.7"
   479  	var err = Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   480  		Target: runtimeTarget,
   481  	})
   482  	require.EqualError(t, err, `template: tmpl:1: unexpected "}" in operand`)
   483  }
   484  
   485  func TestRunInvalidFlags(t *testing.T) {
   486  	var folder = testlib.Mktmp(t)
   487  	writeGoodMain(t, folder)
   488  	var config = config.Project{
   489  		Builds: []config.Build{
   490  			{
   491  				Binary: "nametest",
   492  				Flags:  []string{"{{.Env.GOOS}"},
   493  				Targets: []string{
   494  					runtimeTarget,
   495  				},
   496  			},
   497  		},
   498  	}
   499  	var ctx = context.New(config)
   500  	var err = Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   501  		Target: runtimeTarget,
   502  	})
   503  	require.EqualError(t, err, `template: tmpl:1: unexpected "}" in operand`)
   504  }
   505  
   506  func TestRunPipeWithoutMainFunc(t *testing.T) {
   507  	var folder = testlib.Mktmp(t)
   508  	writeMainWithoutMainFunc(t, folder)
   509  	var config = config.Project{
   510  		Builds: []config.Build{
   511  			{
   512  				Binary: "no-main",
   513  				Hooks:  config.HookConfig{},
   514  				Targets: []string{
   515  					runtimeTarget,
   516  				},
   517  			},
   518  		},
   519  	}
   520  	var ctx = context.New(config)
   521  	ctx.Git.CurrentTag = "5.6.7"
   522  	t.Run("empty", func(t *testing.T) {
   523  		ctx.Config.Builds[0].Main = ""
   524  		require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   525  			Target: runtimeTarget,
   526  		}), `build for no-main does not contain a main function`)
   527  	})
   528  	t.Run("not main.go", func(t *testing.T) {
   529  		ctx.Config.Builds[0].Main = "foo.go"
   530  		require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   531  			Target: runtimeTarget,
   532  		}), `stat foo.go: no such file or directory`)
   533  	})
   534  	t.Run("glob", func(t *testing.T) {
   535  		ctx.Config.Builds[0].Main = "."
   536  		require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   537  			Target: runtimeTarget,
   538  		}), `build for no-main does not contain a main function`)
   539  	})
   540  	t.Run("fixed main.go", func(t *testing.T) {
   541  		ctx.Config.Builds[0].Main = "main.go"
   542  		require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   543  			Target: runtimeTarget,
   544  		}), `build for no-main does not contain a main function`)
   545  	})
   546  }
   547  
   548  func TestRunPipeWithMainFuncNotInMainGoFile(t *testing.T) {
   549  	var folder = testlib.Mktmp(t)
   550  	require.NoError(t, ioutil.WriteFile(
   551  		filepath.Join(folder, "foo.go"),
   552  		[]byte("package main\nfunc main() {println(0)}"),
   553  		0644,
   554  	))
   555  	var config = config.Project{
   556  		Builds: []config.Build{
   557  			{
   558  				Env:    []string{"GO111MODULE=off"},
   559  				Binary: "foo",
   560  				Hooks:  config.HookConfig{},
   561  				Targets: []string{
   562  					runtimeTarget,
   563  				},
   564  				GoBinary: "go",
   565  			},
   566  		},
   567  	}
   568  	var ctx = context.New(config)
   569  	ctx.Git.CurrentTag = "5.6.7"
   570  	t.Run("empty", func(t *testing.T) {
   571  		ctx.Config.Builds[0].Main = ""
   572  		require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   573  			Target: runtimeTarget,
   574  		}))
   575  	})
   576  	t.Run("foo.go", func(t *testing.T) {
   577  		ctx.Config.Builds[0].Main = "foo.go"
   578  		require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   579  			Target: runtimeTarget,
   580  		}))
   581  	})
   582  	t.Run("glob", func(t *testing.T) {
   583  		ctx.Config.Builds[0].Main = "."
   584  		require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   585  			Target: runtimeTarget,
   586  		}))
   587  	})
   588  }
   589  
   590  func TestLdFlagsFullTemplate(t *testing.T) {
   591  	run := time.Now().UTC()
   592  	commit := time.Now().AddDate(-1, 0, 0)
   593  
   594  	var ctx = &context.Context{
   595  		Git: context.GitInfo{
   596  			CurrentTag: "v1.2.3",
   597  			Commit:     "123",
   598  			CommitDate: commit,
   599  		},
   600  		Date:    run,
   601  		Version: "1.2.3",
   602  		Env:     map[string]string{"FOO": "123"},
   603  	}
   604  	var artifact = &artifact.Artifact{Goarch: "amd64"}
   605  	flags, err := tmpl.New(ctx).WithArtifact(artifact, map[string]string{}).
   606  		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}}`)
   607  	require.NoError(t, err)
   608  	require.Contains(t, flags, "-s -w")
   609  	require.Contains(t, flags, "-X main.version=1.2.3")
   610  	require.Contains(t, flags, "-X main.tag=v1.2.3")
   611  	require.Contains(t, flags, "-X main.commit=123")
   612  	require.Contains(t, flags, fmt.Sprintf("-X main.date=%d", run.Year()))
   613  	require.Contains(t, flags, fmt.Sprintf("-X main.time=%d", run.Year()))
   614  	require.Contains(t, flags, `-X "main.foo=123"`)
   615  	require.Contains(t, flags, `-X main.arch=amd64`)
   616  	require.Contains(t, flags, fmt.Sprintf("-X main.commitDate=%d", commit.Year()))
   617  }
   618  
   619  func TestInvalidTemplate(t *testing.T) {
   620  	for template, eerr := range map[string]string{
   621  		"{{ .Nope }":    `template: tmpl:1: unexpected "}" in operand`,
   622  		"{{.Env.NOPE}}": `template: tmpl:1:6: executing "tmpl" at <.Env.NOPE>: map has no entry for key "NOPE"`,
   623  	} {
   624  		t.Run(template, func(tt *testing.T) {
   625  			var ctx = context.New(config.Project{})
   626  			ctx.Git.CurrentTag = "3.4.1"
   627  			flags, err := tmpl.New(ctx).Apply(template)
   628  			require.EqualError(tt, err, eerr)
   629  			require.Empty(tt, flags)
   630  		})
   631  	}
   632  }
   633  
   634  func TestProcessFlags(t *testing.T) {
   635  	var ctx = &context.Context{
   636  		Version: "1.2.3",
   637  	}
   638  	ctx.Git.CurrentTag = "5.6.7"
   639  
   640  	var artifact = &artifact.Artifact{
   641  		Name:   "name",
   642  		Goos:   "darwin",
   643  		Goarch: "amd64",
   644  		Goarm:  "7",
   645  		Extra: map[string]interface{}{
   646  			"Binary": "binary",
   647  		},
   648  	}
   649  
   650  	var source = []string{
   651  		"flag",
   652  		"{{.Version}}",
   653  		"{{.Os}}",
   654  		"{{.Arch}}",
   655  		"{{.Arm}}",
   656  		"{{.Binary}}",
   657  		"{{.ArtifactName}}",
   658  	}
   659  
   660  	var expected = []string{
   661  		"-testflag=flag",
   662  		"-testflag=1.2.3",
   663  		"-testflag=darwin",
   664  		"-testflag=amd64",
   665  		"-testflag=7",
   666  		"-testflag=binary",
   667  		"-testflag=name",
   668  	}
   669  
   670  	flags, err := processFlags(ctx, artifact, []string{}, source, "-testflag=")
   671  	require.NoError(t, err)
   672  	require.Len(t, flags, 7)
   673  	require.Equal(t, expected, flags)
   674  }
   675  
   676  func TestProcessFlagsInvalid(t *testing.T) {
   677  	var ctx = &context.Context{}
   678  
   679  	var source = []string{
   680  		"{{.Version}",
   681  	}
   682  
   683  	var expected = `template: tmpl:1: unexpected "}" in operand`
   684  
   685  	flags, err := processFlags(ctx, &artifact.Artifact{}, []string{}, source, "-testflag=")
   686  	require.EqualError(t, err, expected)
   687  	require.Nil(t, flags)
   688  }
   689  
   690  func TestJoinLdFlags(t *testing.T) {
   691  	tests := []struct {
   692  		input  []string
   693  		output string
   694  	}{
   695  		{[]string{"-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser"}, "-ldflags=-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser"},
   696  		{[]string{"-s -w", "-X main.version={{.Version}}"}, "-ldflags=-s -w -X main.version={{.Version}}"},
   697  	}
   698  
   699  	for _, test := range tests {
   700  		joinedLdFlags := joinLdFlags(test.input)
   701  		require.Equal(t, joinedLdFlags, test.output)
   702  	}
   703  }
   704  
   705  func TestBuildModTimestamp(t *testing.T) {
   706  	// round to seconds since this will be a unix timestamp
   707  	modTime := time.Now().AddDate(-1, 0, 0).Round(1 * time.Second).UTC()
   708  
   709  	var folder = testlib.Mktmp(t)
   710  	writeGoodMain(t, folder)
   711  
   712  	var config = config.Project{
   713  		Builds: []config.Build{
   714  			{
   715  				ID:     "foo",
   716  				Env:    []string{"GO111MODULE=off"},
   717  				Binary: "bin/foo-{{ .Version }}",
   718  				Targets: []string{
   719  					"linux_amd64",
   720  					"darwin_amd64",
   721  					"windows_amd64",
   722  					"linux_arm_6",
   723  					"js_wasm",
   724  					"linux_mips_softfloat",
   725  					"linux_mips64le_softfloat",
   726  				},
   727  				Asmflags:     []string{".=", "all="},
   728  				Gcflags:      []string{"all="},
   729  				Flags:        []string{"{{.Env.GO_FLAGS}}"},
   730  				ModTimestamp: fmt.Sprintf("%d", modTime.Unix()),
   731  				GoBinary:     "go",
   732  			},
   733  		},
   734  	}
   735  	var ctx = context.New(config)
   736  	ctx.Env["GO_FLAGS"] = "-v"
   737  	ctx.Git.CurrentTag = "5.6.7"
   738  	ctx.Version = "v" + ctx.Git.CurrentTag
   739  	var build = ctx.Config.Builds[0]
   740  	for _, target := range build.Targets {
   741  		var ext string
   742  		if strings.HasPrefix(target, "windows") {
   743  			ext = ".exe"
   744  		} else if target == "js_wasm" {
   745  			ext = ".wasm"
   746  		}
   747  		bin, terr := tmpl.New(ctx).Apply(build.Binary)
   748  		require.NoError(t, terr)
   749  
   750  		// injecting some delay here to force inconsistent mod times on bins
   751  		time.Sleep(2 * time.Second)
   752  
   753  		var err = Default.Build(ctx, build, api.Options{
   754  			Target: target,
   755  			Name:   bin + ext,
   756  			Path:   filepath.Join(folder, "dist", target, bin+ext),
   757  			Ext:    ext,
   758  		})
   759  		require.NoError(t, err)
   760  	}
   761  
   762  	for _, bin := range ctx.Artifacts.List() {
   763  		if bin.Type != artifact.Binary {
   764  			continue
   765  		}
   766  
   767  		fi, err := os.Stat(bin.Path)
   768  		require.NoError(t, err)
   769  		require.True(t, modTime.Equal(fi.ModTime()), "inconsistent mod times found when specifying ModTimestamp")
   770  	}
   771  }
   772  
   773  //
   774  // Helpers
   775  //
   776  
   777  func writeMainWithoutMainFunc(t *testing.T, folder string) {
   778  	require.NoError(t, ioutil.WriteFile(
   779  		filepath.Join(folder, "main.go"),
   780  		[]byte("package main\nconst a = 2\nfunc notMain() {println(0)}"),
   781  		0644,
   782  	))
   783  }
   784  
   785  func writeGoodMain(t *testing.T, folder string) {
   786  	require.NoError(t, ioutil.WriteFile(
   787  		filepath.Join(folder, "main.go"),
   788  		[]byte("package main\nvar a = 1\nfunc main() {println(0)}"),
   789  		0644,
   790  	))
   791  }
   792  
   793  func assertContainsError(t *testing.T, err error, s string) {
   794  	require.Error(t, err)
   795  	require.Contains(t, err.Error(), s)
   796  }