github.com/ahmet2mir/goreleaser@v0.180.3-0.20210927151101-8e5ee5a9b8c5/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/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 "windows_arm_6", 58 "linux_arm_6", 59 }, 60 goBinary: "go1.2.3", 61 }, 62 "empty": { 63 build: config.Build{ 64 ID: "foo2", 65 Binary: "foo", 66 }, 67 targets: []string{ 68 "linux_amd64", 69 "linux_386", 70 "linux_arm64", 71 "darwin_amd64", 72 "darwin_arm64", 73 }, 74 goBinary: "go", 75 }, 76 "custom targets": { 77 build: config.Build{ 78 ID: "foo3", 79 Binary: "foo", 80 Targets: []string{ 81 "linux_386", 82 "darwin_amd64", 83 }, 84 }, 85 targets: []string{ 86 "linux_386", 87 "darwin_amd64", 88 }, 89 goBinary: "go", 90 }, 91 "empty with custom dir": { 92 build: config.Build{ 93 ID: "foo2", 94 Binary: "foo", 95 Dir: "./testdata", 96 }, 97 targets: []string{ 98 "linux_amd64", 99 "linux_386", 100 "linux_arm64", 101 "darwin_amd64", 102 "darwin_arm64", 103 }, 104 goBinary: "go", 105 }, 106 "empty with custom dir that doest exist": { 107 build: config.Build{ 108 ID: "foo2", 109 Binary: "foo", 110 Dir: "./nope", 111 }, 112 targets: []string{ 113 "linux_amd64", 114 "linux_386", 115 "linux_arm64", 116 "darwin_amd64", 117 "darwin_arm64", 118 }, 119 goBinary: "go", 120 }, 121 } { 122 t.Run(name, func(t *testing.T) { 123 if testcase.build.GoBinary != "" && testcase.build.GoBinary != "go" { 124 createFakeGoBinaryWithVersion(t, testcase.build.GoBinary, "go1.17") 125 } 126 config := config.Project{ 127 Builds: []config.Build{ 128 testcase.build, 129 }, 130 } 131 ctx := context.New(config) 132 ctx.Git.CurrentTag = "5.6.7" 133 build, err := Default.WithDefaults(ctx.Config.Builds[0]) 134 require.NoError(t, err) 135 require.ElementsMatch(t, build.Targets, testcase.targets) 136 require.EqualValues(t, testcase.goBinary, build.GoBinary) 137 }) 138 } 139 } 140 141 // createFakeGoBinaryWithVersion creates a temporary executable with the 142 // given name, which will output a go version string with the given version. 143 // The temporary directory created by this function will be placed in the PATH 144 // variable for the duration of (and cleaned up at the end of) the 145 // current test run. 146 func createFakeGoBinaryWithVersion(tb testing.TB, name, version string) { 147 tb.Helper() 148 d := tb.TempDir() 149 150 require.NoError(tb, os.WriteFile( 151 filepath.Join(d, name), 152 []byte(fmt.Sprintf("#!/bin/sh\necho %s", version)), 153 0o755, 154 )) 155 156 currentPath := os.Getenv("PATH") 157 tb.Cleanup(func() { 158 require.NoError(tb, os.Setenv("PATH", currentPath)) 159 }) 160 161 path := fmt.Sprintf("%s%c%s", d, os.PathListSeparator, currentPath) 162 require.NoError(tb, os.Setenv("PATH", path)) 163 } 164 165 func TestInvalidTargets(t *testing.T) { 166 type testcase struct { 167 build config.Build 168 expectedErr string 169 } 170 for s, tc := range map[string]testcase{ 171 "goos": { 172 build: config.Build{ 173 Goos: []string{"darwin", "darwim"}, 174 }, 175 expectedErr: "invalid goos: darwim", 176 }, 177 "goarch": { 178 build: config.Build{ 179 Goarch: []string{"amd64", "i386", "386"}, 180 }, 181 expectedErr: "invalid goarch: i386", 182 }, 183 "goarm": { 184 build: config.Build{ 185 Goarch: []string{"arm"}, 186 Goarm: []string{"6", "9", "8", "7"}, 187 }, 188 expectedErr: "invalid goarm: 9", 189 }, 190 "gomips": { 191 build: config.Build{ 192 Goarch: []string{"mips"}, 193 Gomips: []string{"softfloat", "mehfloat", "hardfloat"}, 194 }, 195 expectedErr: "invalid gomips: mehfloat", 196 }, 197 } { 198 t.Run(s, func(t *testing.T) { 199 config := config.Project{ 200 Builds: []config.Build{ 201 tc.build, 202 }, 203 } 204 ctx := context.New(config) 205 _, err := Default.WithDefaults(ctx.Config.Builds[0]) 206 require.EqualError(t, err, tc.expectedErr) 207 }) 208 } 209 } 210 211 func TestBuild(t *testing.T) { 212 folder := testlib.Mktmp(t) 213 writeGoodMain(t, folder) 214 config := config.Project{ 215 Builds: []config.Build{ 216 { 217 ID: "foo", 218 Env: []string{"GO111MODULE=off"}, 219 Binary: "bin/foo-{{ .Version }}", 220 Targets: []string{ 221 "linux_amd64", 222 "darwin_amd64", 223 "windows_amd64", 224 "linux_arm_6", 225 "js_wasm", 226 "linux_mips_softfloat", 227 "linux_mips64le_softfloat", 228 }, 229 Asmflags: []string{".=", "all="}, 230 Gcflags: []string{"all="}, 231 Flags: []string{"{{.Env.GO_FLAGS}}"}, 232 Tags: []string{"osusergo", "netgo", "static_build"}, 233 GoBinary: "go", 234 }, 235 }, 236 } 237 ctx := context.New(config) 238 ctx.Env["GO_FLAGS"] = "-v" 239 ctx.Git.CurrentTag = "5.6.7" 240 ctx.Version = "v" + ctx.Git.CurrentTag 241 build := ctx.Config.Builds[0] 242 for _, target := range build.Targets { 243 var ext string 244 if strings.HasPrefix(target, "windows") { 245 ext = ".exe" 246 } else if target == "js_wasm" { 247 ext = ".wasm" 248 } 249 bin, terr := tmpl.New(ctx).Apply(build.Binary) 250 require.NoError(t, terr) 251 252 // injecting some delay here to force inconsistent mod times on bins 253 time.Sleep(2 * time.Second) 254 255 parts := strings.Split(target, "_") 256 goos := parts[0] 257 goarch := parts[1] 258 goarm := "" 259 gomips := "" 260 if len(parts) > 2 { 261 if strings.Contains(goarch, "arm") { 262 goarm = parts[2] 263 } 264 if strings.Contains(goarch, "mips") { 265 gomips = parts[2] 266 } 267 } 268 err := Default.Build(ctx, build, api.Options{ 269 Target: target, 270 Name: bin + ext, 271 Path: filepath.Join(folder, "dist", target, bin+ext), 272 Goos: goos, 273 Goarch: goarch, 274 Goarm: goarm, 275 Gomips: gomips, 276 Ext: ext, 277 }) 278 require.NoError(t, err) 279 } 280 require.ElementsMatch(t, ctx.Artifacts.List(), []*artifact.Artifact{ 281 { 282 Name: "bin/foo-v5.6.7", 283 Path: filepath.Join(folder, "dist", "linux_amd64", "bin", "foo-v5.6.7"), 284 Goos: "linux", 285 Goarch: "amd64", 286 Type: artifact.Binary, 287 Extra: map[string]interface{}{ 288 "Ext": "", 289 "Binary": "foo-v5.6.7", 290 "ID": "foo", 291 }, 292 }, 293 { 294 Name: "bin/foo-v5.6.7", 295 Path: filepath.Join(folder, "dist", "linux_mips_softfloat", "bin", "foo-v5.6.7"), 296 Goos: "linux", 297 Goarch: "mips", 298 Gomips: "softfloat", 299 Type: artifact.Binary, 300 Extra: map[string]interface{}{ 301 "Ext": "", 302 "Binary": "foo-v5.6.7", 303 "ID": "foo", 304 }, 305 }, 306 { 307 Name: "bin/foo-v5.6.7", 308 Path: filepath.Join(folder, "dist", "linux_mips64le_softfloat", "bin", "foo-v5.6.7"), 309 Goos: "linux", 310 Goarch: "mips64le", 311 Gomips: "softfloat", 312 Type: artifact.Binary, 313 Extra: map[string]interface{}{ 314 "Ext": "", 315 "Binary": "foo-v5.6.7", 316 "ID": "foo", 317 }, 318 }, 319 { 320 Name: "bin/foo-v5.6.7", 321 Path: filepath.Join(folder, "dist", "darwin_amd64", "bin", "foo-v5.6.7"), 322 Goos: "darwin", 323 Goarch: "amd64", 324 Type: artifact.Binary, 325 Extra: map[string]interface{}{ 326 "Ext": "", 327 "Binary": "foo-v5.6.7", 328 "ID": "foo", 329 }, 330 }, 331 { 332 Name: "bin/foo-v5.6.7", 333 Path: filepath.Join(folder, "dist", "linux_arm_6", "bin", "foo-v5.6.7"), 334 Goos: "linux", 335 Goarch: "arm", 336 Goarm: "6", 337 Type: artifact.Binary, 338 Extra: map[string]interface{}{ 339 "Ext": "", 340 "Binary": "foo-v5.6.7", 341 "ID": "foo", 342 }, 343 }, 344 { 345 Name: "bin/foo-v5.6.7.exe", 346 Path: filepath.Join(folder, "dist", "windows_amd64", "bin", "foo-v5.6.7.exe"), 347 Goos: "windows", 348 Goarch: "amd64", 349 Type: artifact.Binary, 350 Extra: map[string]interface{}{ 351 "Ext": ".exe", 352 "Binary": "foo-v5.6.7", 353 "ID": "foo", 354 }, 355 }, 356 { 357 Name: "bin/foo-v5.6.7.wasm", 358 Path: filepath.Join(folder, "dist", "js_wasm", "bin", "foo-v5.6.7.wasm"), 359 Goos: "js", 360 Goarch: "wasm", 361 Type: artifact.Binary, 362 Extra: map[string]interface{}{ 363 "Ext": ".wasm", 364 "Binary": "foo-v5.6.7", 365 "ID": "foo", 366 }, 367 }, 368 }) 369 370 modTimes := map[time.Time]bool{} 371 for _, bin := range ctx.Artifacts.List() { 372 if bin.Type != artifact.Binary { 373 continue 374 } 375 376 fi, err := os.Stat(bin.Path) 377 require.NoError(t, err) 378 379 // make this a suitable map key, per docs: https://golang.org/pkg/time/#Time 380 modTime := fi.ModTime().UTC().Round(0) 381 382 if modTimes[modTime] { 383 t.Fatal("duplicate modified time found, times should be different by default") 384 } 385 modTimes[modTime] = true 386 } 387 } 388 389 func TestBuildCodeInSubdir(t *testing.T) { 390 folder := testlib.Mktmp(t) 391 subdir := filepath.Join(folder, "bar") 392 err := os.Mkdir(subdir, 0o755) 393 require.NoError(t, err) 394 writeGoodMain(t, subdir) 395 config := config.Project{ 396 Builds: []config.Build{ 397 { 398 ID: "foo", 399 Env: []string{"GO111MODULE=off"}, 400 Dir: "bar", 401 Binary: "foo", 402 Targets: []string{ 403 runtimeTarget, 404 }, 405 GoBinary: "go", 406 }, 407 }, 408 } 409 ctx := context.New(config) 410 ctx.Git.CurrentTag = "5.6.7" 411 build := ctx.Config.Builds[0] 412 err = Default.Build(ctx, build, api.Options{ 413 Target: runtimeTarget, 414 Name: build.Binary, 415 Path: filepath.Join(folder, "dist", runtimeTarget, build.Binary), 416 Ext: "", 417 }) 418 require.NoError(t, err) 419 } 420 421 func TestBuildWithDotGoDir(t *testing.T) { 422 folder := testlib.Mktmp(t) 423 require.NoError(t, os.Mkdir(filepath.Join(folder, ".go"), 0o755)) 424 writeGoodMain(t, folder) 425 config := config.Project{ 426 Builds: []config.Build{ 427 { 428 ID: "foo", 429 Env: []string{"GO111MODULE=off"}, 430 Binary: "foo", 431 Targets: []string{runtimeTarget}, 432 GoBinary: "go", 433 }, 434 }, 435 } 436 ctx := context.New(config) 437 ctx.Git.CurrentTag = "5.6.7" 438 build := ctx.Config.Builds[0] 439 require.NoError(t, Default.Build(ctx, build, api.Options{ 440 Target: runtimeTarget, 441 Name: build.Binary, 442 Path: filepath.Join(folder, "dist", runtimeTarget, build.Binary), 443 Ext: "", 444 })) 445 } 446 447 func TestBuildFailed(t *testing.T) { 448 folder := testlib.Mktmp(t) 449 writeGoodMain(t, folder) 450 config := config.Project{ 451 Builds: []config.Build{ 452 { 453 ID: "buildid", 454 Flags: []string{"-flag-that-dont-exists-to-force-failure"}, 455 Targets: []string{ 456 runtimeTarget, 457 }, 458 GoBinary: "go", 459 }, 460 }, 461 } 462 ctx := context.New(config) 463 ctx.Git.CurrentTag = "5.6.7" 464 err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{ 465 Target: "darwin_amd64", 466 }) 467 assertContainsError(t, err, `flag provided but not defined: -flag-that-dont-exists-to-force-failure`) 468 require.Empty(t, ctx.Artifacts.List()) 469 } 470 471 func TestRunInvalidAsmflags(t *testing.T) { 472 folder := testlib.Mktmp(t) 473 writeGoodMain(t, folder) 474 config := config.Project{ 475 Builds: []config.Build{ 476 { 477 Binary: "nametest", 478 Asmflags: []string{"{{.Version}"}, 479 Targets: []string{ 480 runtimeTarget, 481 }, 482 }, 483 }, 484 } 485 ctx := context.New(config) 486 ctx.Git.CurrentTag = "5.6.7" 487 err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{ 488 Target: runtimeTarget, 489 }) 490 require.EqualError(t, err, `template: tmpl:1: unexpected "}" in operand`) 491 } 492 493 func TestRunInvalidGcflags(t *testing.T) { 494 folder := testlib.Mktmp(t) 495 writeGoodMain(t, folder) 496 config := config.Project{ 497 Builds: []config.Build{ 498 { 499 Binary: "nametest", 500 Gcflags: []string{"{{.Version}"}, 501 Targets: []string{ 502 runtimeTarget, 503 }, 504 }, 505 }, 506 } 507 ctx := context.New(config) 508 ctx.Git.CurrentTag = "5.6.7" 509 err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{ 510 Target: runtimeTarget, 511 }) 512 require.EqualError(t, err, `template: tmpl:1: unexpected "}" in operand`) 513 } 514 515 func TestRunInvalidLdflags(t *testing.T) { 516 folder := testlib.Mktmp(t) 517 writeGoodMain(t, folder) 518 config := config.Project{ 519 Builds: []config.Build{ 520 { 521 Binary: "nametest", 522 Flags: []string{"-v"}, 523 Ldflags: []string{"-s -w -X main.version={{.Version}"}, 524 Targets: []string{ 525 runtimeTarget, 526 }, 527 }, 528 }, 529 } 530 ctx := context.New(config) 531 ctx.Git.CurrentTag = "5.6.7" 532 err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{ 533 Target: runtimeTarget, 534 }) 535 require.EqualError(t, err, `template: tmpl:1: unexpected "}" in operand`) 536 } 537 538 func TestRunInvalidFlags(t *testing.T) { 539 folder := testlib.Mktmp(t) 540 writeGoodMain(t, folder) 541 config := config.Project{ 542 Builds: []config.Build{ 543 { 544 Binary: "nametest", 545 Flags: []string{"{{.Env.GOOS}"}, 546 Targets: []string{ 547 runtimeTarget, 548 }, 549 }, 550 }, 551 } 552 ctx := context.New(config) 553 err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{ 554 Target: runtimeTarget, 555 }) 556 require.EqualError(t, err, `template: tmpl:1: unexpected "}" in operand`) 557 } 558 559 func TestRunPipeWithoutMainFunc(t *testing.T) { 560 newCtx := func(t *testing.T) *context.Context { 561 t.Helper() 562 folder := testlib.Mktmp(t) 563 writeMainWithoutMainFunc(t, folder) 564 config := config.Project{ 565 Builds: []config.Build{ 566 { 567 Binary: "no-main", 568 Hooks: config.HookConfig{}, 569 Targets: []string{ 570 runtimeTarget, 571 }, 572 }, 573 }, 574 } 575 ctx := context.New(config) 576 ctx.Git.CurrentTag = "5.6.7" 577 return ctx 578 } 579 t.Run("empty", func(t *testing.T) { 580 ctx := newCtx(t) 581 ctx.Config.Builds[0].Main = "" 582 require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ 583 Target: runtimeTarget, 584 }), `build for no-main does not contain a main function`) 585 }) 586 t.Run("not main.go", func(t *testing.T) { 587 ctx := newCtx(t) 588 ctx.Config.Builds[0].Main = "foo.go" 589 require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ 590 Target: runtimeTarget, 591 }), `couldn't find main file: stat foo.go: no such file or directory`) 592 }) 593 t.Run("glob", func(t *testing.T) { 594 ctx := newCtx(t) 595 ctx.Config.Builds[0].Main = "." 596 require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ 597 Target: runtimeTarget, 598 }), `build for no-main does not contain a main function`) 599 }) 600 t.Run("fixed main.go", func(t *testing.T) { 601 ctx := newCtx(t) 602 ctx.Config.Builds[0].Main = "main.go" 603 require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ 604 Target: runtimeTarget, 605 }), `build for no-main does not contain a main function`) 606 }) 607 t.Run("using gomod.proxy", func(t *testing.T) { 608 ctx := newCtx(t) 609 ctx.Config.GoMod.Proxy = true 610 ctx.Config.Builds[0].Dir = "dist/proxy/test" 611 ctx.Config.Builds[0].Main = "github.com/caarlos0/test" 612 ctx.Config.Builds[0].UnproxiedDir = "." 613 ctx.Config.Builds[0].UnproxiedMain = "." 614 require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ 615 Target: runtimeTarget, 616 }), `build for no-main does not contain a main function`) 617 }) 618 } 619 620 func TestRunPipeWithProxiedRepo(t *testing.T) { 621 folder := testlib.Mktmp(t) 622 out, err := exec.Command("git", "clone", "https://github.com/goreleaser/goreleaser", "-b", "v0.161.1", "--depth=1", ".").CombinedOutput() 623 require.NoError(t, err, string(out)) 624 625 proxied := filepath.Join(folder, "dist/proxy/default") 626 require.NoError(t, os.MkdirAll(proxied, 0o750)) 627 require.NoError(t, os.WriteFile( 628 filepath.Join(proxied, "main.go"), 629 []byte(`// +build main 630 package main 631 632 import _ "github.com/goreleaser/goreleaser" 633 `), 634 0o666, 635 )) 636 require.NoError(t, os.WriteFile( 637 filepath.Join(proxied, "go.mod"), 638 []byte("module foo\nrequire github.com/goreleaser/goreleaser v0.161.1"), 639 0o666, 640 )) 641 642 cmd := exec.Command("go", "mod", "tidy") 643 cmd.Dir = proxied 644 require.NoError(t, cmd.Run()) 645 646 config := config.Project{ 647 GoMod: config.GoMod{ 648 Proxy: true, 649 }, 650 Builds: []config.Build{ 651 { 652 Binary: "foo", 653 Main: "github.com/goreleaser/goreleaser", 654 Dir: proxied, 655 UnproxiedMain: ".", 656 UnproxiedDir: ".", 657 Targets: []string{ 658 runtimeTarget, 659 }, 660 GoBinary: "go", 661 }, 662 }, 663 } 664 ctx := context.New(config) 665 666 require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ 667 Target: runtimeTarget, 668 })) 669 } 670 671 func TestRunPipeWithMainFuncNotInMainGoFile(t *testing.T) { 672 folder := testlib.Mktmp(t) 673 require.NoError(t, os.WriteFile( 674 filepath.Join(folder, "foo.go"), 675 []byte("package main\nfunc main() {println(0)}"), 676 0o644, 677 )) 678 config := config.Project{ 679 Builds: []config.Build{ 680 { 681 Env: []string{"GO111MODULE=off"}, 682 Binary: "foo", 683 Hooks: config.HookConfig{}, 684 Targets: []string{ 685 runtimeTarget, 686 }, 687 GoBinary: "go", 688 }, 689 }, 690 } 691 ctx := context.New(config) 692 ctx.Git.CurrentTag = "5.6.7" 693 t.Run("empty", func(t *testing.T) { 694 ctx.Config.Builds[0].Main = "" 695 require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ 696 Target: runtimeTarget, 697 })) 698 }) 699 t.Run("foo.go", func(t *testing.T) { 700 ctx.Config.Builds[0].Main = "foo.go" 701 require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ 702 Target: runtimeTarget, 703 })) 704 }) 705 t.Run("glob", func(t *testing.T) { 706 ctx.Config.Builds[0].Main = "." 707 require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ 708 Target: runtimeTarget, 709 })) 710 }) 711 } 712 713 func TestLdFlagsFullTemplate(t *testing.T) { 714 run := time.Now().UTC() 715 commit := time.Now().AddDate(-1, 0, 0) 716 717 ctx := &context.Context{ 718 Git: context.GitInfo{ 719 CurrentTag: "v1.2.3", 720 Commit: "123", 721 CommitDate: commit, 722 }, 723 Date: run, 724 Version: "1.2.3", 725 Env: map[string]string{"FOO": "123"}, 726 } 727 artifact := &artifact.Artifact{Goarch: "amd64"} 728 flags, err := tmpl.New(ctx).WithArtifact(artifact, map[string]string{}). 729 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}}`) 730 require.NoError(t, err) 731 require.Contains(t, flags, "-s -w") 732 require.Contains(t, flags, "-X main.version=1.2.3") 733 require.Contains(t, flags, "-X main.tag=v1.2.3") 734 require.Contains(t, flags, "-X main.commit=123") 735 require.Contains(t, flags, fmt.Sprintf("-X main.date=%d", run.Year())) 736 require.Contains(t, flags, fmt.Sprintf("-X main.time=%d", run.Year())) 737 require.Contains(t, flags, `-X "main.foo=123"`) 738 require.Contains(t, flags, `-X main.arch=amd64`) 739 require.Contains(t, flags, fmt.Sprintf("-X main.commitDate=%d", commit.Year())) 740 } 741 742 func TestInvalidTemplate(t *testing.T) { 743 for template, eerr := range map[string]string{ 744 "{{ .Nope }": `template: tmpl:1: unexpected "}" in operand`, 745 "{{.Env.NOPE}}": `template: tmpl:1:6: executing "tmpl" at <.Env.NOPE>: map has no entry for key "NOPE"`, 746 } { 747 t.Run(template, func(t *testing.T) { 748 ctx := context.New(config.Project{}) 749 ctx.Git.CurrentTag = "3.4.1" 750 flags, err := tmpl.New(ctx).Apply(template) 751 require.EqualError(t, err, eerr) 752 require.Empty(t, flags) 753 }) 754 } 755 } 756 757 func TestProcessFlags(t *testing.T) { 758 ctx := &context.Context{ 759 Version: "1.2.3", 760 } 761 ctx.Git.CurrentTag = "5.6.7" 762 763 artifact := &artifact.Artifact{ 764 Name: "name", 765 Goos: "darwin", 766 Goarch: "amd64", 767 Goarm: "7", 768 Extra: map[string]interface{}{ 769 "Binary": "binary", 770 }, 771 } 772 773 source := []string{ 774 "flag", 775 "{{.Version}}", 776 "{{.Os}}", 777 "{{.Arch}}", 778 "{{.Arm}}", 779 "{{.Binary}}", 780 "{{.ArtifactName}}", 781 } 782 783 expected := []string{ 784 "-testflag=flag", 785 "-testflag=1.2.3", 786 "-testflag=darwin", 787 "-testflag=amd64", 788 "-testflag=7", 789 "-testflag=binary", 790 "-testflag=name", 791 } 792 793 flags, err := processFlags(ctx, artifact, []string{}, source, "-testflag=") 794 require.NoError(t, err) 795 require.Len(t, flags, 7) 796 require.Equal(t, expected, flags) 797 } 798 799 func TestProcessFlagsInvalid(t *testing.T) { 800 ctx := &context.Context{} 801 802 source := []string{ 803 "{{.Version}", 804 } 805 806 expected := `template: tmpl:1: unexpected "}" in operand` 807 808 flags, err := processFlags(ctx, &artifact.Artifact{}, []string{}, source, "-testflag=") 809 require.EqualError(t, err, expected) 810 require.Nil(t, flags) 811 } 812 813 func TestBuildModTimestamp(t *testing.T) { 814 // round to seconds since this will be a unix timestamp 815 modTime := time.Now().AddDate(-1, 0, 0).Round(1 * time.Second).UTC() 816 817 folder := testlib.Mktmp(t) 818 writeGoodMain(t, folder) 819 820 config := config.Project{ 821 Builds: []config.Build{ 822 { 823 ID: "foo", 824 Env: []string{"GO111MODULE=off"}, 825 Binary: "bin/foo-{{ .Version }}", 826 Targets: []string{ 827 "linux_amd64", 828 "darwin_amd64", 829 "windows_amd64", 830 "linux_arm_6", 831 "js_wasm", 832 "linux_mips_softfloat", 833 "linux_mips64le_softfloat", 834 }, 835 Asmflags: []string{".=", "all="}, 836 Gcflags: []string{"all="}, 837 Flags: []string{"{{.Env.GO_FLAGS}}"}, 838 ModTimestamp: fmt.Sprintf("%d", modTime.Unix()), 839 GoBinary: "go", 840 }, 841 }, 842 } 843 ctx := context.New(config) 844 ctx.Env["GO_FLAGS"] = "-v" 845 ctx.Git.CurrentTag = "5.6.7" 846 ctx.Version = "v" + ctx.Git.CurrentTag 847 build := ctx.Config.Builds[0] 848 for _, target := range build.Targets { 849 var ext string 850 if strings.HasPrefix(target, "windows") { 851 ext = ".exe" 852 } else if target == "js_wasm" { 853 ext = ".wasm" 854 } 855 bin, terr := tmpl.New(ctx).Apply(build.Binary) 856 require.NoError(t, terr) 857 858 // injecting some delay here to force inconsistent mod times on bins 859 time.Sleep(2 * time.Second) 860 861 err := Default.Build(ctx, build, api.Options{ 862 Target: target, 863 Name: bin + ext, 864 Path: filepath.Join(folder, "dist", target, bin+ext), 865 Ext: ext, 866 }) 867 require.NoError(t, err) 868 } 869 870 for _, bin := range ctx.Artifacts.List() { 871 if bin.Type != artifact.Binary { 872 continue 873 } 874 875 fi, err := os.Stat(bin.Path) 876 require.NoError(t, err) 877 require.True(t, modTime.Equal(fi.ModTime()), "inconsistent mod times found when specifying ModTimestamp") 878 } 879 } 880 881 func TestBuildGoBuildLine(t *testing.T) { 882 requireEqualCmd := func(tb testing.TB, build config.Build, expected []string) { 883 tb.Helper() 884 config := config.Project{ 885 Builds: []config.Build{build}, 886 } 887 ctx := context.New(config) 888 ctx.Version = "v1.2.3" 889 ctx.Git.Commit = "aaa" 890 891 line, err := buildGoBuildLine(ctx, config.Builds[0], api.Options{Path: "foo"}, &artifact.Artifact{}, []string{}) 892 require.NoError(t, err) 893 require.Equal(t, expected, line) 894 } 895 896 t.Run("full", func(t *testing.T) { 897 requireEqualCmd(t, config.Build{ 898 Main: ".", 899 Asmflags: []string{"asmflag1", "asmflag2"}, 900 Gcflags: []string{"gcflag1", "gcflag2"}, 901 Flags: []string{"-flag1", "-flag2"}, 902 Tags: []string{"tag1", "tag2"}, 903 Ldflags: []string{"ldflag1", "ldflag2"}, 904 GoBinary: "go", 905 }, []string{ 906 "go", "build", 907 "-flag1", "-flag2", 908 "-asmflags=asmflag1", "-asmflags=asmflag2", 909 "-gcflags=gcflag1", "-gcflags=gcflag2", 910 "-tags=tag1,tag2", 911 "-ldflags=ldflag1 ldflag2", 912 "-o", "foo", ".", 913 }) 914 }) 915 916 t.Run("simple", func(t *testing.T) { 917 requireEqualCmd(t, config.Build{ 918 Main: ".", 919 GoBinary: "go", 920 }, strings.Fields("go build -o foo .")) 921 }) 922 923 t.Run("ldflags1", func(t *testing.T) { 924 requireEqualCmd(t, config.Build{ 925 Main: ".", 926 Ldflags: []string{"-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.builtBy=goreleaser"}, 927 GoBinary: "go", 928 }, []string{ 929 "go", "build", 930 "-ldflags=-s -w -X main.version=v1.2.3 -X main.commit=aaa -X main.builtBy=goreleaser", 931 "-o", "foo", ".", 932 }) 933 }) 934 935 t.Run("ldflags2", func(t *testing.T) { 936 requireEqualCmd(t, config.Build{ 937 Main: ".", 938 Ldflags: []string{"-s -w", "-X main.version={{.Version}}"}, 939 GoBinary: "go", 940 }, []string{"go", "build", "-ldflags=-s -w -X main.version=v1.2.3", "-o", "foo", "."}) 941 }) 942 } 943 944 // 945 // Helpers 946 // 947 948 func writeMainWithoutMainFunc(t *testing.T, folder string) { 949 t.Helper() 950 require.NoError(t, os.WriteFile( 951 filepath.Join(folder, "main.go"), 952 []byte("package main\nconst a = 2\nfunc notMain() {println(0)}"), 953 0o644, 954 )) 955 } 956 957 func writeGoodMain(t *testing.T, folder string) { 958 t.Helper() 959 require.NoError(t, os.WriteFile( 960 filepath.Join(folder, "main.go"), 961 []byte("package main\nvar a = 1\nfunc main() {println(0)}"), 962 0o644, 963 )) 964 } 965 966 func assertContainsError(t *testing.T, err error, s string) { 967 t.Helper() 968 require.Error(t, err) 969 require.Contains(t, err.Error(), s) 970 }