github.com/yogeshkumararora/slsa-github-generator@v1.10.1-0.20240520161934-11278bd5afb4/internal/builders/go/pkg/build_test.go (about) 1 // Copyright 2022 SLSA Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package pkg 16 17 import ( 18 "errors" 19 "fmt" 20 "os" 21 "os/exec" 22 "testing" 23 24 "github.com/google/go-cmp/cmp" 25 "github.com/google/go-cmp/cmp/cmpopts" 26 ) 27 28 func errEnvVariableNameEmptyFunc(t *testing.T, got error) { 29 want := errEnvVariableNameEmpty 30 if !errors.Is(got, want) { 31 t.Fatalf("unexpected error: %v", cmp.Diff(got, want, cmpopts.EquateErrors())) 32 } 33 } 34 35 func errUnsupportedArgumentsFunc(t *testing.T, got error) { 36 want := errUnsupportedArguments 37 if !errors.Is(got, want) { 38 t.Fatalf("unexpected error: %v", cmp.Diff(got, want, cmpopts.EquateErrors())) 39 } 40 } 41 42 func errInvalidEnvArgumentFunc(t *testing.T, got error) { 43 want := errInvalidEnvArgument 44 if !errors.Is(got, want) { 45 t.Fatalf("unexpected error: %v", cmp.Diff(got, want, cmpopts.EquateErrors())) 46 } 47 } 48 49 func errEnvVariableNameNotAllowedFunc(t *testing.T, got error) { 50 want := errEnvVariableNameNotAllowed 51 if !errors.Is(got, want) { 52 t.Fatalf("unexpected error: %v", cmp.Diff(got, want, cmpopts.EquateErrors())) 53 } 54 } 55 56 func errInvalidFilenameFunc(t *testing.T, got error) { 57 want := errInvalidFilename 58 if !errors.Is(got, want) { 59 t.Fatalf("unexpected error: %v", cmp.Diff(got, want, cmpopts.EquateErrors())) 60 } 61 } 62 63 func Test_isAllowedEnvVariable(t *testing.T) { 64 t.Parallel() 65 66 tests := []struct { 67 name string 68 variable string 69 expected bool 70 }{ 71 { 72 name: "BLA variable", 73 variable: "BLA", 74 expected: false, 75 }, 76 { 77 name: "random variable", 78 variable: "random", 79 expected: false, 80 }, 81 { 82 name: "GOSOMETHING variable", 83 variable: "GOSOMETHING", 84 expected: true, 85 }, 86 { 87 name: "CGO_SOMETHING variable", 88 variable: "CGO_SOMETHING", 89 expected: true, 90 }, 91 } 92 for _, tt := range tests { 93 tt := tt // Re-initializing variable so it is not changed while executing the closure below 94 t.Run(tt.name, func(t *testing.T) { 95 t.Parallel() 96 97 r := isAllowedEnvVariable(tt.variable) 98 if !cmp.Equal(r, tt.expected) { 99 t.Errorf(cmp.Diff(r, tt.expected)) 100 } 101 }) 102 } 103 } 104 105 func Test_getOutputBinaryPath(t *testing.T) { 106 t.Parallel() 107 108 tests := []struct { 109 err func(*testing.T, error) 110 name string 111 path string 112 }{ 113 { 114 name: "empty output", 115 path: "", 116 err: errInvalidFilenameFunc, 117 }, 118 { 119 name: "not absolute", 120 path: "./some/path/to/binary", 121 err: errInvalidFilenameFunc, 122 }, 123 { 124 name: "absolute path", 125 path: "/to/absolute/path/to/binary", 126 }, 127 } 128 for _, tt := range tests { 129 tt := tt // Re-initializing variable so it is not changed while executing the closure below 130 t.Run(tt.name, func(t *testing.T) { 131 t.Parallel() 132 133 r, err := getOutputBinaryPath(tt.path) 134 if tt.err != nil { 135 tt.err(t, err) 136 } 137 138 if err != nil { 139 return 140 } 141 142 if !cmp.Equal(r, tt.path) { 143 t.Errorf(cmp.Diff(r, tt.path)) 144 } 145 }) 146 } 147 } 148 149 func Test_isAllowedArg(t *testing.T) { 150 t.Parallel() 151 152 type test struct { 153 name string 154 argument string 155 expected bool 156 } 157 158 var tests []test 159 160 for k := range allowedBuildArgs { 161 tests = append(tests, test{ 162 name: fmt.Sprintf("%s argument", k), 163 argument: k, 164 expected: true, 165 }, test{ 166 name: fmt.Sprintf("%sbla argument", k), 167 argument: fmt.Sprintf("%sbla", k), 168 expected: true, 169 }, test{ 170 name: fmt.Sprintf("bla %s argument", k), 171 argument: fmt.Sprintf("bla%s", k), 172 expected: false, 173 }, test{ 174 name: fmt.Sprintf("space %s argument", k), 175 argument: fmt.Sprintf(" %s", k), 176 expected: false, 177 }) 178 } 179 for _, tt := range tests { 180 tt := tt // Re-initializing variable so it is not changed while executing the closure below 181 t.Run(tt.name, func(t *testing.T) { 182 t.Parallel() 183 184 r := isAllowedArg(tt.argument) 185 if !cmp.Equal(r, tt.expected) { 186 t.Errorf(cmp.Diff(r, tt.expected)) 187 } 188 }) 189 } 190 } 191 192 func Test_generateOutputFilename(t *testing.T) { 193 // Disable to avoid env clobbering between tests. 194 // t.Parallel() 195 196 tests := []struct { 197 name string 198 filename string 199 goos string 200 goarch string 201 envs map[string]string 202 argEnv string 203 expected struct { 204 err func(*testing.T, error) 205 fn string 206 } 207 }{ 208 { 209 name: "invalid filename", 210 filename: "../filename", 211 expected: struct { 212 err func(*testing.T, error) 213 fn string 214 }{ 215 err: errInvalidFilenameFunc, 216 }, 217 }, 218 { 219 name: "valid filename", 220 filename: "", 221 expected: struct { 222 err func(*testing.T, error) 223 fn string 224 }{ 225 err: errInvalidFilenameFunc, 226 }, 227 }, 228 { 229 name: "filename arch", 230 filename: "name-{{ .Arch }}", 231 expected: struct { 232 err func(*testing.T, error) 233 fn string 234 }{ 235 err: errEnvVariableNameEmptyFunc, 236 }, 237 }, 238 { 239 name: "filename os", 240 filename: "name-{{ .Os }}", 241 expected: struct { 242 err func(*testing.T, error) 243 fn string 244 }{ 245 err: errEnvVariableNameEmptyFunc, 246 }, 247 }, 248 { 249 name: "filename invalid letter ^", 250 filename: "Name-AB^", 251 goarch: "amd64", 252 expected: struct { 253 err func(*testing.T, error) 254 fn string 255 }{ 256 err: errInvalidFilenameFunc, 257 }, 258 }, 259 { 260 filename: "filename invalid letter $", 261 expected: struct { 262 err func(*testing.T, error) 263 fn string 264 }{ 265 err: errInvalidFilenameFunc, 266 }, 267 }, 268 { 269 name: "filename os", 270 filename: "name-{{ .Os }}", 271 expected: struct { 272 err func(*testing.T, error) 273 fn string 274 }{ 275 err: errEnvVariableNameEmptyFunc, 276 }, 277 }, 278 { 279 name: "filename linux os", 280 filename: "name-{{ .Os }}", 281 goos: "linux", 282 expected: struct { 283 err func(*testing.T, error) 284 fn string 285 }{ 286 err: nil, 287 fn: "name-linux", 288 }, 289 }, 290 { 291 name: "filename amd64 arch", 292 filename: "name-{{ .Arch }}", 293 goarch: "amd64", 294 expected: struct { 295 err func(*testing.T, error) 296 fn string 297 }{ 298 err: nil, 299 fn: "name-amd64", 300 }, 301 }, 302 { 303 name: "filename capital letter", 304 filename: "Name-{{ .Arch }}", 305 goarch: "amd64", 306 expected: struct { 307 err func(*testing.T, error) 308 fn string 309 }{ 310 err: nil, 311 fn: "Name-amd64", 312 }, 313 }, 314 { 315 name: "filename amd64/linux arch", 316 filename: "name-{{ .Os }}-{{ .Arch }}", 317 goarch: "amd64", 318 goos: "linux", 319 expected: struct { 320 err func(*testing.T, error) 321 fn string 322 }{ 323 err: nil, 324 fn: "name-linux-amd64", 325 }, 326 }, 327 { 328 name: "filename invalid arch", 329 filename: "name-{{ .Arch }}", 330 goarch: "something/../../", 331 expected: struct { 332 err func(*testing.T, error) 333 fn string 334 }{ 335 err: errInvalidFilenameFunc, 336 }, 337 }, 338 { 339 name: "filename invalid not supported", 340 filename: "name-{{ .Bla }}", 341 goarch: "something/../../", 342 expected: struct { 343 err func(*testing.T, error) 344 fn string 345 }{ 346 err: errInvalidEnvArgumentFunc, 347 }, 348 }, 349 { 350 name: "filename amd64/linux v1.2.3", 351 filename: "name-{{ .Os }}-{{ .Arch }}-{{ .Tag }}", 352 goarch: "amd64", 353 goos: "linux", 354 envs: map[string]string{ 355 "GITHUB_REF_NAME": "v1.2.3", 356 }, 357 expected: struct { 358 err func(*testing.T, error) 359 fn string 360 }{ 361 err: nil, 362 fn: "name-linux-amd64-v1.2.3", 363 }, 364 }, 365 { 366 name: "filename twice v1.2.3", 367 filename: "name-{{ .Tag }}-{{ .Tag }}", 368 goarch: "amd64", 369 goos: "linux", 370 envs: map[string]string{ 371 "GITHUB_REF_NAME": "v1.2.3", 372 }, 373 expected: struct { 374 err func(*testing.T, error) 375 fn string 376 }{ 377 err: nil, 378 fn: "name-v1.2.3-v1.2.3", 379 }, 380 }, 381 { 382 name: "filename twice empty versions", 383 filename: "name-{{ .Tag }}-{{ .Tag }}", 384 goarch: "amd64", 385 goos: "linux", 386 envs: map[string]string{ 387 "GITHUB_REF_NAME": "", 388 }, 389 expected: struct { 390 err func(*testing.T, error) 391 fn string 392 }{ 393 err: nil, 394 fn: fmt.Sprintf("name-%s-%s", unknownTag, unknownTag), 395 }, 396 }, 397 { 398 name: "invalid name with version", 399 filename: "name-{{ .Tag }}/../bla", 400 goarch: "amd64", 401 goos: "linux", 402 envs: map[string]string{ 403 "GITHUB_REF_NAME": "v1.2.3", 404 }, 405 expected: struct { 406 err func(*testing.T, error) 407 fn string 408 }{ 409 err: errInvalidFilenameFunc, 410 }, 411 }, 412 { 413 name: "filename twice unset versions", 414 filename: "name-{{ .Tag }}-{{ .Tag }}", 415 goarch: "amd64", 416 goos: "linux", 417 expected: struct { 418 err func(*testing.T, error) 419 fn string 420 }{ 421 err: nil, 422 fn: fmt.Sprintf("name-%s-%s", unknownTag, unknownTag), 423 }, 424 }, 425 { 426 name: "filename envs", 427 filename: "name-{{ .Env.VAR1 }}-{{ .Os }}-{{ .Arch }}-{{ .Env.VAR2 }}-{{ .Tag }}-end", 428 goarch: "amd64", 429 goos: "linux", 430 envs: map[string]string{ 431 "GITHUB_REF_NAME": "v1.2.3", 432 }, 433 argEnv: "VAR1:var1, VAR2:var2", 434 expected: struct { 435 err func(*testing.T, error) 436 fn string 437 }{ 438 err: nil, 439 fn: "name-var1-linux-amd64-var2-v1.2.3-end", 440 }, 441 }, 442 } 443 444 for _, tt := range tests { 445 tt := tt // Re-initializing variable so it is not changed while executing the closure below 446 t.Run(tt.name, func(t *testing.T) { 447 // Note: disable parallelism to avoid env variable clobbering between tests. 448 // t.Parallel() 449 450 cfg := goReleaserConfigFile{ 451 Binary: tt.filename, 452 Version: 1, 453 Goos: tt.goos, 454 Goarch: tt.goarch, 455 } 456 c, err := fromConfig(&cfg) 457 if err != nil { 458 t.Errorf("fromConfig: %v", err) 459 } 460 461 // Unset env variables, in case the workflow environment sets them. 462 for _, k := range []string{"GITHUB_REF_NAME"} { 463 os.Unsetenv(k) 464 } 465 466 // Set env variables. 467 for k, v := range tt.envs { 468 t.Setenv(k, v) 469 } 470 471 b := GoBuildNew("go compiler", c) 472 473 err = b.SetArgEnvVariables(tt.argEnv) 474 if err != nil { 475 t.Errorf("SetArgEnvVariables: %v", err) 476 } 477 478 fn, err := b.generateOutputFilename() 479 if tt.expected.err != nil { 480 tt.expected.err(t, err) 481 } 482 483 // Unset env variables, so that they don't 484 // affect other tests. 485 for k := range tt.envs { 486 os.Unsetenv(k) 487 } 488 489 if err != nil { 490 return 491 } 492 493 if fn != tt.expected.fn { 494 t.Errorf(cmp.Diff(fn, tt.expected.fn)) 495 } 496 }) 497 } 498 } 499 500 func Test_SetArgEnvVariables(t *testing.T) { 501 t.Parallel() 502 503 tests := []struct { 504 expected struct { 505 env map[string]string 506 err func(*testing.T, error) 507 } 508 name string 509 argEnv string 510 }{ 511 { 512 name: "valid arg envs", 513 argEnv: "VAR1:value1, VAR2:value2", 514 expected: struct { 515 env map[string]string 516 err func(*testing.T, error) 517 }{ 518 env: map[string]string{"VAR1": "value1", "VAR2": "value2"}, 519 err: nil, 520 }, 521 }, 522 { 523 name: "empty arg envs", 524 argEnv: "", 525 expected: struct { 526 env map[string]string 527 err func(*testing.T, error) 528 }{ 529 env: map[string]string{}, 530 err: nil, 531 }, 532 }, 533 { 534 name: "valid arg envs not space", 535 argEnv: "VAR1:value1,VAR2:value2", 536 expected: struct { 537 env map[string]string 538 err func(*testing.T, error) 539 }{ 540 env: map[string]string{"VAR1": "value1", "VAR2": "value2"}, 541 err: nil, 542 }, 543 }, 544 { 545 name: "invalid arg empty 2 values", 546 argEnv: "VAR1:value1,", 547 expected: struct { 548 env map[string]string 549 err func(*testing.T, error) 550 }{ 551 err: errInvalidEnvArgumentFunc, 552 }, 553 }, 554 { 555 name: "invalid arg empty 3 values", 556 argEnv: "VAR1:value1,, VAR3:value3", 557 expected: struct { 558 env map[string]string 559 err func(*testing.T, error) 560 }{ 561 err: errInvalidEnvArgumentFunc, 562 }, 563 }, 564 { 565 name: "invalid arg uses equal", 566 argEnv: "VAR1=value1", 567 expected: struct { 568 env map[string]string 569 err func(*testing.T, error) 570 }{ 571 err: errInvalidEnvArgumentFunc, 572 }, 573 }, 574 { 575 name: "valid single arg", 576 argEnv: "VAR1:value1", 577 expected: struct { 578 env map[string]string 579 err func(*testing.T, error) 580 }{ 581 env: map[string]string{"VAR1": "value1"}, 582 err: nil, 583 }, 584 }, 585 { 586 name: "invalid valid single arg with empty", 587 argEnv: "VAR1:value1:", 588 expected: struct { 589 env map[string]string 590 err func(*testing.T, error) 591 }{ 592 err: errInvalidEnvArgumentFunc, 593 }, 594 }, 595 } 596 597 for _, tt := range tests { 598 tt := tt // Re-initializing variable so it is not changed while executing the closure below 599 t.Run(tt.name, func(t *testing.T) { 600 t.Parallel() 601 602 cfg := goReleaserConfigFile{ 603 Version: 1, 604 } 605 c, err := fromConfig(&cfg) 606 if err != nil { 607 t.Errorf("fromConfig: %v", err) 608 } 609 b := GoBuildNew("go compiler", c) 610 611 err = b.SetArgEnvVariables(tt.argEnv) 612 if tt.expected.err != nil { 613 tt.expected.err(t, err) 614 } 615 616 if err != nil { 617 return 618 } 619 620 sorted := cmpopts.SortSlices(func(a, b string) bool { return a < b }) 621 if !cmp.Equal(b.argEnv, tt.expected.env, sorted) { 622 t.Errorf(cmp.Diff(b.argEnv, tt.expected.env)) 623 } 624 }) 625 } 626 } 627 628 func Test_generateEnvVariables(t *testing.T) { 629 t.Parallel() 630 631 tests := []struct { 632 name string 633 goos string 634 goarch string 635 env []string 636 expected struct { 637 err func(*testing.T, error) 638 flags []string 639 } 640 }{ 641 { 642 name: "empty flags", 643 goos: "linux", 644 goarch: "x86", 645 expected: struct { 646 err func(*testing.T, error) 647 flags []string 648 }{ 649 flags: []string{"GOOS=linux", "GOARCH=x86"}, 650 err: nil, 651 }, 652 }, 653 { 654 name: "empty goos", 655 goarch: "x86", 656 expected: struct { 657 err func(*testing.T, error) 658 flags []string 659 }{ 660 flags: []string{}, 661 err: errEnvVariableNameEmptyFunc, 662 }, 663 }, 664 { 665 name: "empty goarch", 666 goos: "windows", 667 expected: struct { 668 err func(*testing.T, error) 669 flags []string 670 }{ 671 flags: []string{}, 672 err: errEnvVariableNameEmptyFunc, 673 }, 674 }, 675 { 676 name: "invalid flags", 677 goos: "windows", 678 goarch: "amd64", 679 env: []string{"VAR1=value1", "VAR2=value2"}, 680 expected: struct { 681 err func(*testing.T, error) 682 flags []string 683 }{ 684 err: errEnvVariableNameNotAllowedFunc, 685 }, 686 }, 687 { 688 name: "invalid flags", 689 goos: "windows", 690 goarch: "amd64", 691 env: []string{"GOVAR1=value1", "GOVAR2=value2", "CGO_VAR1=val1", "CGO_VAR2=val2"}, 692 expected: struct { 693 err func(*testing.T, error) 694 flags []string 695 }{ 696 flags: []string{ 697 "GOOS=windows", "GOARCH=amd64", 698 "GOVAR1=value1", "GOVAR2=value2", 699 "CGO_VAR1=val1", "CGO_VAR2=val2", 700 }, 701 err: nil, 702 }, 703 }, 704 } 705 706 for _, tt := range tests { 707 tt := tt // Re-initializing variable so it is not changed while executing the closure below 708 t.Run(tt.name, func(t *testing.T) { 709 t.Parallel() 710 711 cfg := goReleaserConfigFile{ 712 Version: 1, 713 Goos: tt.goos, 714 Goarch: tt.goarch, 715 Env: tt.env, 716 } 717 c, err := fromConfig(&cfg) 718 if err != nil { 719 t.Errorf("fromConfig: %v", err) 720 } 721 b := GoBuildNew("go compiler", c) 722 723 flags, err := b.generateCommandEnvVariables() 724 725 if tt.expected.err != nil { 726 tt.expected.err(t, err) 727 } 728 if err != nil { 729 return 730 } 731 expectedFlags := tt.expected.flags 732 sorted := cmpopts.SortSlices(func(a, b string) bool { return a < b }) 733 if !cmp.Equal(flags, expectedFlags, sorted) { 734 t.Errorf(cmp.Diff(flags, expectedFlags)) 735 } 736 }) 737 } 738 } 739 740 func Test_generateLdflags(t *testing.T) { 741 // Disable to avoid env clobbering between tests. 742 // t.Parallel() 743 744 tests := []struct { 745 githubEnv map[string]string 746 name string 747 argEnv string 748 outldflags string 749 err func(*testing.T, error) 750 inldflags []string 751 }{ 752 { 753 name: "version ldflags", 754 argEnv: "VERSION_LDFLAGS:value1", 755 inldflags: []string{"{{ .Env.VERSION_LDFLAGS }}"}, 756 outldflags: "value1", 757 }, 758 { 759 name: "one value with text", 760 argEnv: "VAR1:value1, VAR2:value2", 761 inldflags: []string{"name-{{ .Env.VAR1 }}"}, 762 outldflags: "name-value1", 763 }, 764 { 765 name: "two values with text", 766 argEnv: "VAR1:value1, VAR2:value2", 767 inldflags: []string{"name-{{ .Env.VAR1 }}-{{ .Env.VAR2 }}"}, 768 outldflags: "name-value1-value2", 769 }, 770 { 771 name: "two values with text and not space between env", 772 argEnv: "VAR1:value1,VAR2:value2", 773 inldflags: []string{"name-{{ .Env.VAR1 }}-{{ .Env.VAR2 }}"}, 774 outldflags: "name-value1-value2", 775 }, 776 { 777 name: "same two values with text", 778 argEnv: "VAR1:value1, VAR2:value2", 779 inldflags: []string{"name-{{ .Env.VAR1 }}-{{ .Env.VAR1 }}"}, 780 outldflags: "name-value1-value1", 781 }, 782 { 783 name: "same value extremeties", 784 argEnv: "VAR1:value1, VAR2:value2", 785 inldflags: []string{"{{ .Env.VAR1 }}-name-{{ .Env.VAR1 }}"}, 786 outldflags: "value1-name-value1", 787 }, 788 { 789 name: "two different value extremeties", 790 argEnv: "VAR1:value1, VAR2:value2", 791 inldflags: []string{"{{ .Env.VAR1 }}-name-{{ .Env.VAR2 }}"}, 792 outldflags: "value1-name-value2", 793 }, 794 { 795 name: "undefined env variable", 796 argEnv: "VAR2:value2", 797 inldflags: []string{"{{ .Env.VAR1 }}-name-{{ .Env.VAR1 }}"}, 798 err: errEnvVariableNameEmptyFunc, 799 }, 800 { 801 name: "undefined env variable 1", 802 argEnv: "VAR2:value2", 803 inldflags: []string{"{{ .Env.VAR2 }}-name-{{ .Env.VAR1 }}"}, 804 err: errEnvVariableNameEmptyFunc, 805 }, 806 { 807 name: "empty env variable", 808 argEnv: "", 809 inldflags: []string{"{{ .Env.VAR1 }}-name-{{ .Env.VAR1 }}"}, 810 err: errEnvVariableNameEmptyFunc, 811 }, 812 { 813 name: "several ldflags", 814 argEnv: "VAR1:value1, VAR2:value2, VAR3:value3", 815 inldflags: []string{ 816 "{{ .Env.VAR1 }}-name-{{ .Env.VAR2 }}", 817 "{{ .Env.VAR1 }}-name-{{ .Env.VAR3 }}", 818 "{{ .Env.VAR3 }}-name-{{ .Env.VAR1 }}", 819 "{{ .Env.VAR3 }}-name-{{ .Env.VAR2 }}", 820 }, 821 outldflags: "value1-name-value2 value1-name-value3 value3-name-value1 value3-name-value2", 822 }, 823 { 824 name: "several ldflags with start/end", 825 argEnv: "VAR1:value1, VAR2:value2, VAR3:value3", 826 inldflags: []string{ 827 "start-{{ .Env.VAR1 }}-name-{{ .Env.VAR2 }}-end", 828 "start-{{ .Env.VAR1 }}-name-{{ .Env.VAR3 }}-end", 829 "start-{{ .Env.VAR3 }}-name-{{ .Env.VAR1 }}-end", 830 "start-{{ .Env.VAR3 }}-name-{{ .Env.VAR2 }}-end", 831 }, 832 outldflags: "start-value1-name-value2-end start-value1-name-value3-end " + 833 "start-value3-name-value1-end start-value3-name-value2-end", 834 }, 835 { 836 name: "several ldflags and tag", 837 argEnv: "VAR1:value1, VAR2:value2, VAR3:value3", 838 githubEnv: map[string]string{ 839 "GITHUB_REF_NAME": "v1.2.3", 840 }, 841 inldflags: []string{ 842 "start-{{ .Env.VAR1 }}-name-{{ .Env.VAR2 }}-{{ .Tag }}-end", 843 "{{ .Env.VAR1 }}-name-{{ .Env.VAR3 }}", 844 "{{ .Env.VAR3 }}-name-{{ .Env.VAR1 }}-{{ .Tag }}-{{ .Tag }}", 845 "{{ .Env.VAR3 }}-name-{{ .Env.VAR2 }}-{{ .Tag }}-end", 846 }, 847 outldflags: "start-value1-name-value2-v1.2.3-end value1-name-value3 " + 848 "value3-name-value1-v1.2.3-v1.2.3 value3-name-value2-v1.2.3-end", 849 }, 850 { 851 name: "several ldflags and Arch and Os", 852 argEnv: "VAR1:value1, VAR2:value2, VAR3:value3", 853 githubEnv: map[string]string{ 854 "GITHUB_REF_NAME": "v1.2.3", 855 }, 856 inldflags: []string{ 857 "start-{{ .Env.VAR1 }}-name-{{ .Env.VAR2 }}-{{ .Tag }}-{{ .Arch }}-end", 858 "{{ .Env.VAR1 }}-name-{{ .Env.VAR3 }}-{{ .Os }}-end", 859 }, 860 outldflags: "start-value1-name-value2-v1.2.3-amd64-end value1-name-value3-linux-end", 861 }, 862 } 863 864 for _, tt := range tests { 865 tt := tt // Re-initializing variable so it is not changed while executing the closure below 866 t.Run(tt.name, func(t *testing.T) { 867 // Disable to avoid env clobbering between tests. 868 // t.Parallel() 869 870 cfg := goReleaserConfigFile{ 871 Version: 1, 872 Ldflags: tt.inldflags, 873 Goarch: "amd64", 874 Goos: "linux", 875 } 876 c, err := fromConfig(&cfg) 877 if err != nil { 878 t.Errorf("fromConfig: %v", err) 879 } 880 881 // Set GitHub env variables. 882 for k, v := range tt.githubEnv { 883 t.Setenv(k, v) 884 } 885 886 b := GoBuildNew("go compiler", c) 887 888 err = b.SetArgEnvVariables(tt.argEnv) 889 if err != nil { 890 t.Errorf("SetArgEnvVariables: %v", err) 891 } 892 ldflags, err := b.generateLdflags() 893 894 // Unset env variables, so that they don't 895 // affect other tests. 896 for k := range tt.githubEnv { 897 os.Unsetenv(k) 898 } 899 900 if tt.err != nil { 901 tt.err(t, err) 902 } 903 if err != nil { 904 return 905 } 906 // Note: generated env variables contain the process's env variables too. 907 if !cmp.Equal(ldflags, tt.outldflags) { 908 t.Errorf(cmp.Diff(ldflags, tt.outldflags)) 909 } 910 }) 911 } 912 } 913 914 func Test_generateFlags(t *testing.T) { 915 t.Parallel() 916 917 tests := []struct { 918 name string 919 err func(*testing.T, error) 920 flags []string 921 }{ 922 { 923 name: "valid flags", 924 flags: []string{"-race", "-x"}, 925 err: nil, 926 }, 927 { 928 name: "invalid -mod flags", 929 flags: []string{"-mod=whatever", "-x"}, 930 err: errUnsupportedArgumentsFunc, 931 }, 932 { 933 name: "invalid random flags", 934 flags: []string{ 935 "-a", "-race", "-msan", "-asan", 936 "-v", "-x", "-buildinfo", "-buildmode", 937 "-buildvcs", "-compiler", "-gccgoflags", 938 "-gcflags", "-ldflags", "-linkshared", 939 "-tags", "-trimpath", "bla", 940 }, 941 err: errUnsupportedArgumentsFunc, 942 }, 943 { 944 name: "valid all flags", 945 flags: []string{ 946 "-a", "-race", "-msan", "-asan", 947 "-v", "-x", "-buildinfo", "-buildmode", 948 "-buildvcs", "-compiler", "-gccgoflags", 949 "-gcflags", "-ldflags", "-linkshared", 950 "-tags", "-trimpath", 951 }, 952 err: nil, 953 }, 954 } 955 956 for _, tt := range tests { 957 tt := tt // Re-initializing variable so it is not changed while executing the closure below 958 t.Run(tt.name, func(t *testing.T) { 959 t.Parallel() 960 961 cfg := goReleaserConfigFile{ 962 Version: 1, 963 Flags: tt.flags, 964 } 965 c, err := fromConfig(&cfg) 966 if err != nil { 967 t.Errorf("fromConfig: %v", err) 968 } 969 b := GoBuildNew("gocompiler", c) 970 971 flags, err := b.generateFlags() 972 expectedFlags := append([]string{"gocompiler", "build", "-mod=vendor"}, tt.flags...) 973 974 if tt.err != nil { 975 tt.err(t, err) 976 } 977 if err != nil { 978 return 979 } 980 // Note: generated env variables contain the process's env variables too. 981 sorted := cmpopts.SortSlices(func(a, b string) bool { return a < b }) 982 if !cmp.Equal(flags, expectedFlags, sorted) { 983 t.Errorf(cmp.Diff(flags, expectedFlags)) 984 } 985 }) 986 } 987 } 988 989 func Test_generateCommand(t *testing.T) { 990 t.Parallel() 991 992 tests := []struct { 993 name string 994 flags []string 995 }{ 996 { 997 name: "some flags", 998 flags: []string{"-race", "-x"}, 999 }, 1000 { 1001 name: "some other flags", 1002 flags: []string{"-x"}, 1003 }, 1004 { 1005 name: "other random flags", 1006 flags: []string{ 1007 "-a", "-race", "-msan", "-asan", 1008 "-v", "-x", "-buildinfo", "-buildmode", 1009 "-buildvcs", "-compiler", "-gccgoflags", 1010 "-gcflags", "-ldflags", "-linkshared", 1011 "-tags", "-trimpath", "bla", 1012 }, 1013 }, 1014 { 1015 name: "even more flags", 1016 flags: []string{ 1017 "-a", "-race", "-msan", "-asan", 1018 "-v", "-x", "-buildinfo", "-buildmode", 1019 "-buildvcs", "-compiler", "-gccgoflags", 1020 "-gcflags", "-ldflags", "-linkshared", 1021 "-tags", "-trimpath", 1022 }, 1023 }, 1024 } 1025 1026 for _, tt := range tests { 1027 tt := tt // Re-initializing variable so it is not changed while executing the closure below 1028 t.Run(tt.name, func(t *testing.T) { 1029 t.Parallel() 1030 1031 cfgs := []*goReleaserConfigFile{ 1032 { 1033 Version: 1, 1034 Flags: tt.flags, 1035 Main: asPointer("./some/path/main.go"), 1036 }, 1037 { 1038 Version: 1, 1039 Flags: tt.flags, 1040 }, 1041 } 1042 1043 for _, cfg := range cfgs { 1044 c, err := fromConfig(cfg) 1045 if err != nil { 1046 t.Errorf("fromConfig: %v", err) 1047 } 1048 b := GoBuildNew("gocompiler", c) 1049 1050 command := b.generateCommand(tt.flags, "out-binary") 1051 expectedCommand := tt.flags 1052 expectedCommand = append(expectedCommand, "-o", "out-binary") 1053 if cfg.Main != nil { 1054 expectedCommand = append(expectedCommand, *cfg.Main) 1055 } 1056 1057 sorted := cmpopts.SortSlices(func(a, b string) bool { return a < b }) 1058 if !cmp.Equal(command, expectedCommand, sorted) { 1059 t.Errorf(cmp.Diff(command, expectedCommand)) 1060 } 1061 } 1062 }) 1063 } 1064 } 1065 1066 func asPointer(s string) *string { 1067 return &s 1068 } 1069 1070 func TestGoBuild_Run(t *testing.T) { 1071 type fields struct { 1072 cfg *GoReleaserConfig 1073 argEnv map[string]string 1074 goc string 1075 } 1076 type args struct { 1077 dry bool 1078 } 1079 tests := []struct { 1080 name string 1081 err func(*testing.T, error) 1082 fields fields 1083 args args 1084 wantErr bool 1085 }{ 1086 { 1087 name: "dry run valid flags", 1088 fields: fields{ 1089 cfg: &GoReleaserConfig{ 1090 Goos: "linux", 1091 Goarch: "amd64", 1092 Binary: "binary", 1093 Main: asPointer("../builders/go/main.go"), 1094 Dir: asPointer("../builders/go"), 1095 Ldflags: []string{ 1096 "-X main.version=1.0.0", 1097 }, 1098 }, 1099 }, 1100 args: args{ 1101 dry: true, 1102 }, 1103 }, 1104 { 1105 name: "non-dry valid flags", 1106 fields: fields{ 1107 cfg: &GoReleaserConfig{ 1108 Goos: "linux", 1109 Goarch: "amd64", 1110 Binary: "/tmp/binary", 1111 Main: asPointer("main.go"), 1112 Dir: asPointer("./testdata/go"), 1113 Ldflags: []string{ 1114 "-X main.version=1.0.0", 1115 }, 1116 }, 1117 }, 1118 args: args{ 1119 dry: false, 1120 }, 1121 }, 1122 { 1123 name: "slash in the binary name", 1124 fields: fields{ 1125 cfg: &GoReleaserConfig{ 1126 Goos: "linux", 1127 Goarch: "amd64", 1128 Binary: "tmp/binary", 1129 Main: asPointer("../builders/go/main.go"), 1130 Dir: asPointer("../builders/go"), 1131 }, 1132 }, 1133 args: args{ 1134 dry: true, 1135 }, 1136 wantErr: true, 1137 err: errInvalidFilenameFunc, 1138 }, 1139 { 1140 name: "dry run - invalid flags", 1141 fields: fields{ 1142 cfg: &GoReleaserConfig{ 1143 Goos: "linux", 1144 Goarch: "amd64", 1145 Binary: "binary", 1146 Main: asPointer("../builders/go/main.go"), 1147 Dir: asPointer("../builders/go"), 1148 Ldflags: []string{}, 1149 }, 1150 }, 1151 args: args{ 1152 dry: true, 1153 }, 1154 wantErr: false, 1155 }, 1156 } 1157 for _, tt := range tests { 1158 t.Run(tt.name, func(t *testing.T) { 1159 b := &GoBuild{ 1160 cfg: tt.fields.cfg, 1161 goc: tt.fields.goc, 1162 argEnv: tt.fields.argEnv, 1163 } 1164 t.Setenv("OUTPUT_BINARY", tt.fields.cfg.Binary) 1165 // if the test is not dry run , then code has to look for golang binary 1166 if !tt.args.dry { 1167 path, err := exec.LookPath("go") 1168 if err != nil { 1169 t.Errorf("exec.LookPath: %v", err) 1170 } 1171 b.goc = path 1172 } 1173 err := b.Run(tt.args.dry) 1174 if err != nil != tt.wantErr { 1175 t.Errorf("Run() error = %v, wantErr %v", err, tt.wantErr) 1176 } 1177 if tt.err != nil { 1178 if err == nil { 1179 t.Errorf("Run() error = nil, but wanted error") 1180 } else if tt.err != nil { 1181 tt.err(t, err) 1182 } 1183 } 1184 }) 1185 } 1186 }