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