github.com/goreleaser/goreleaser@v1.25.1/internal/pipe/nix/nix_test.go (about) 1 package nix 2 3 import ( 4 "html/template" 5 "os" 6 "os/exec" 7 "path/filepath" 8 "strings" 9 "testing" 10 11 "github.com/goreleaser/goreleaser/internal/artifact" 12 "github.com/goreleaser/goreleaser/internal/client" 13 "github.com/goreleaser/goreleaser/internal/golden" 14 "github.com/goreleaser/goreleaser/internal/skips" 15 "github.com/goreleaser/goreleaser/internal/testctx" 16 "github.com/goreleaser/goreleaser/pkg/config" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func TestContinueOnError(t *testing.T) { 21 require.True(t, Pipe{}.ContinueOnError()) 22 } 23 24 func TestString(t *testing.T) { 25 require.NotEmpty(t, Pipe{}.String()) 26 } 27 28 func TestSkip(t *testing.T) { 29 t.Run("no-nix", func(t *testing.T) { 30 require.True(t, Pipe{}.Skip(testctx.New())) 31 }) 32 t.Run("skip flag", func(t *testing.T) { 33 require.True(t, NewPublish().Skip(testctx.NewWithCfg(config.Project{ 34 Nix: []config.Nix{{}}, 35 }, testctx.Skip(skips.Nix)))) 36 }) 37 t.Run("nix-all-good", func(t *testing.T) { 38 require.False(t, NewPublish().Skip(testctx.NewWithCfg(config.Project{ 39 Nix: []config.Nix{{}}, 40 }))) 41 }) 42 t.Run("prefetcher-not-in-path", func(t *testing.T) { 43 t.Setenv("PATH", "nope") 44 require.True(t, NewPublish().Skip(testctx.NewWithCfg(config.Project{ 45 Nix: []config.Nix{{}}, 46 }))) 47 }) 48 } 49 50 const fakeNixPrefetchURLBin = "fake-nix-prefetch-url" 51 52 func TestPrefetcher(t *testing.T) { 53 t.Run("prefetch", func(t *testing.T) { 54 t.Run("build", func(t *testing.T) { 55 sha, err := buildShaPrefetcher{}.Prefetch("any") 56 require.NoError(t, err) 57 require.Equal(t, zeroHash, sha) 58 }) 59 t.Run("publish", func(t *testing.T) { 60 t.Run("no-nix-prefetch-url", func(t *testing.T) { 61 _, err := publishShaPrefetcher{fakeNixPrefetchURLBin}.Prefetch("any") 62 require.ErrorIs(t, err, exec.ErrNotFound) 63 }) 64 t.Run("valid", func(t *testing.T) { 65 sha, err := publishShaPrefetcher{nixPrefetchURLBin}.Prefetch("https://github.com/goreleaser/goreleaser/releases/download/v1.18.2/goreleaser_Darwin_arm64.tar.gz") 66 require.NoError(t, err) 67 require.Equal(t, "0girjxp07srylyq36xk1ska8p68m2fhp05xgyv4wkcl61d6rzv3y", sha) 68 }) 69 }) 70 }) 71 t.Run("available", func(t *testing.T) { 72 t.Run("build", func(t *testing.T) { 73 require.True(t, buildShaPrefetcher{}.Available()) 74 }) 75 t.Run("publish", func(t *testing.T) { 76 t.Run("no-nix-prefetch-url", func(t *testing.T) { 77 require.False(t, publishShaPrefetcher{fakeNixPrefetchURLBin}.Available()) 78 }) 79 t.Run("valid", func(t *testing.T) { 80 require.True(t, publishShaPrefetcher{nixPrefetchURLBin}.Available()) 81 }) 82 }) 83 }) 84 } 85 86 func TestRunPipe(t *testing.T) { 87 for _, tt := range []struct { 88 name string 89 expectDefaultErrorIs error 90 expectRunErrorIs error 91 expectPublishErrorIs error 92 nix config.Nix 93 }{ 94 { 95 name: "minimal", 96 nix: config.Nix{ 97 IDs: []string{"foo"}, 98 Repository: config.RepoRef{ 99 Owner: "foo", 100 Name: "bar", 101 }, 102 }, 103 }, 104 { 105 name: "invalid license", 106 expectDefaultErrorIs: errInvalidLicense, 107 nix: config.Nix{ 108 IDs: []string{"foo"}, 109 License: "mitt", 110 Repository: config.RepoRef{ 111 Owner: "foo", 112 Name: "bar", 113 }, 114 }, 115 }, 116 { 117 name: "deps", 118 nix: config.Nix{ 119 IDs: []string{"foo"}, 120 Repository: config.RepoRef{ 121 Owner: "foo", 122 Name: "bar", 123 }, 124 Dependencies: []config.NixDependency{ 125 {Name: "fish"}, 126 {Name: "bash"}, 127 linuxDep("ttyd"), 128 darwinDep("chromium"), 129 }, 130 }, 131 }, 132 { 133 name: "extra-install", 134 nix: config.Nix{ 135 IDs: []string{"foo"}, 136 Repository: config.RepoRef{ 137 Owner: "foo", 138 Name: "bar", 139 }, 140 Dependencies: []config.NixDependency{ 141 {Name: "fish"}, 142 {Name: "bash"}, 143 linuxDep("ttyd"), 144 darwinDep("chromium"), 145 }, 146 ExtraInstall: "installManPage ./manpages/foo.1.gz", 147 }, 148 }, 149 { 150 name: "open-pr", 151 nix: config.Nix{ 152 Name: "foo", 153 IDs: []string{"foo"}, 154 Description: "my test", 155 Homepage: "https://goreleaser.com", 156 License: "mit", 157 Path: "pkgs/foo.nix", 158 Repository: config.RepoRef{ 159 Owner: "foo", 160 Name: "bar", 161 Branch: "update-{{.Version}}", 162 PullRequest: config.PullRequest{ 163 Enabled: true, 164 }, 165 }, 166 }, 167 }, 168 { 169 name: "wrapped-in-dir", 170 nix: config.Nix{ 171 Name: "wrapped-in-dir", 172 IDs: []string{"wrapped-in-dir"}, 173 Description: "my test", 174 Homepage: "https://goreleaser.com", 175 License: "mit", 176 PostInstall: ` 177 echo "do something" 178 `, 179 Install: ` 180 mkdir -p $out/bin 181 cp foo $out/bin/foo 182 `, 183 ExtraInstall: "installManPage ./manpages/foo.1.gz", 184 Repository: config.RepoRef{ 185 Owner: "foo", 186 Name: "bar", 187 }, 188 }, 189 }, 190 { 191 name: "zip", 192 nix: config.Nix{ 193 Name: "foozip", 194 IDs: []string{"foo-zip"}, 195 Description: "my test", 196 Homepage: "https://goreleaser.com", 197 License: "mit", 198 Repository: config.RepoRef{ 199 Owner: "foo", 200 Name: "bar", 201 }, 202 }, 203 }, 204 { 205 name: "zip-with-dependencies", 206 nix: config.Nix{ 207 Name: "foozip", 208 IDs: []string{"foo-zip"}, 209 Description: "my test", 210 Homepage: "https://goreleaser.com", 211 License: "mit", 212 Dependencies: []config.NixDependency{ 213 {Name: "git"}, 214 }, 215 Repository: config.RepoRef{ 216 Owner: "foo", 217 Name: "bar", 218 }, 219 }, 220 }, 221 { 222 name: "zip-and-tar", 223 nix: config.Nix{ 224 Name: "foozip", 225 IDs: []string{"zip-and-tar"}, 226 Description: "my test", 227 Homepage: "https://goreleaser.com", 228 License: "mit", 229 Repository: config.RepoRef{ 230 Owner: "foo", 231 Name: "bar", 232 }, 233 }, 234 }, 235 { 236 name: "unibin", 237 expectRunErrorIs: ErrMultipleArchivesSamePlatform, 238 nix: config.Nix{ 239 Name: "unibin", 240 IDs: []string{"unibin"}, 241 Description: "my test", 242 Homepage: "https://goreleaser.com", 243 License: "mit", 244 Repository: config.RepoRef{ 245 Owner: "foo", 246 Name: "bar", 247 }, 248 }, 249 }, 250 { 251 name: "no-archives", 252 expectRunErrorIs: errNoArchivesFound{ 253 goamd64: "v2", 254 ids: []string{"nopenopenope"}, 255 }, 256 nix: config.Nix{ 257 Name: "no-archives", 258 IDs: []string{"nopenopenope"}, 259 Goamd64: "v2", 260 Repository: config.RepoRef{ 261 Owner: "foo", 262 Name: "bar", 263 }, 264 }, 265 }, 266 { 267 name: "unibin-replaces", 268 nix: config.Nix{ 269 Name: "unibin-replaces", 270 IDs: []string{"unibin-replaces"}, 271 Description: "my test", 272 Homepage: "https://goreleaser.com", 273 License: "mit", 274 Repository: config.RepoRef{ 275 Owner: "foo", 276 Name: "bar", 277 }, 278 }, 279 }, 280 { 281 name: "partial", 282 nix: config.Nix{ 283 Name: "partial", 284 IDs: []string{"partial"}, 285 Repository: config.RepoRef{ 286 Owner: "foo", 287 Name: "bar", 288 }, 289 }, 290 }, 291 { 292 name: "no-repo-name", 293 expectRunErrorIs: errNoRepoName, 294 nix: config.Nix{ 295 Name: "doesnotmatter", 296 Repository: config.RepoRef{ 297 Owner: "foo", 298 }, 299 }, 300 }, 301 { 302 name: "bad-name-tmpl", 303 expectRunErrorIs: &template.Error{}, 304 nix: config.Nix{ 305 Name: "{{ .Nope }}", 306 Repository: config.RepoRef{ 307 Owner: "foo", 308 Name: "bar", 309 }, 310 }, 311 }, 312 { 313 name: "bad-description-tmpl", 314 expectRunErrorIs: &template.Error{}, 315 nix: config.Nix{ 316 Description: "{{ .Nope }}", 317 Repository: config.RepoRef{ 318 Owner: "foo", 319 Name: "bar", 320 }, 321 }, 322 }, 323 { 324 name: "bad-homepage-tmpl", 325 expectRunErrorIs: &template.Error{}, 326 nix: config.Nix{ 327 Homepage: "{{ .Nope }}", 328 Repository: config.RepoRef{ 329 Owner: "foo", 330 Name: "bar", 331 }, 332 }, 333 }, 334 { 335 name: "bad-repo-tmpl", 336 expectRunErrorIs: &template.Error{}, 337 nix: config.Nix{ 338 Name: "doesnotmatter", 339 Repository: config.RepoRef{ 340 Owner: "foo", 341 Name: "{{ .Nope }}", 342 }, 343 }, 344 }, 345 { 346 name: "bad-skip-upload-tmpl", 347 expectRunErrorIs: &template.Error{}, 348 nix: config.Nix{ 349 Name: "doesnotmatter", 350 SkipUpload: "{{ .Nope }}", 351 Repository: config.RepoRef{ 352 Owner: "foo", 353 Name: "bar", 354 }, 355 }, 356 }, 357 { 358 name: "bad-install-tmpl", 359 expectRunErrorIs: &template.Error{}, 360 nix: config.Nix{ 361 Name: "foo", 362 Install: `{{.NoInstall}}`, 363 Repository: config.RepoRef{ 364 Owner: "foo", 365 Name: "bar", 366 }, 367 }, 368 }, 369 { 370 name: "bad-post-install-tmpl", 371 expectRunErrorIs: &template.Error{}, 372 nix: config.Nix{ 373 Name: "foo", 374 PostInstall: `{{.NoPostInstall}}`, 375 Repository: config.RepoRef{ 376 Owner: "foo", 377 Name: "bar", 378 }, 379 }, 380 }, 381 { 382 name: "bad-path-tmpl", 383 expectRunErrorIs: &template.Error{}, 384 nix: config.Nix{ 385 Name: "foo", 386 Path: `{{.Foo}}/bar/foo.nix`, 387 Repository: config.RepoRef{ 388 Owner: "foo", 389 Name: "bar", 390 }, 391 }, 392 }, 393 { 394 name: "bad-release-url-tmpl", 395 expectRunErrorIs: &template.Error{}, 396 nix: config.Nix{ 397 Name: "foo", 398 URLTemplate: "{{.BadURL}}", 399 Repository: config.RepoRef{ 400 Owner: "foo", 401 Name: "bar", 402 }, 403 }, 404 }, 405 { 406 name: "skip-upload", 407 expectPublishErrorIs: errSkipUpload, 408 nix: config.Nix{ 409 Name: "doesnotmatter", 410 SkipUpload: "true", 411 IDs: []string{"foo"}, 412 Repository: config.RepoRef{ 413 Owner: "foo", 414 Name: "bar", 415 }, 416 }, 417 }, 418 { 419 name: "skip-upload-auto", 420 expectPublishErrorIs: errSkipUploadAuto, 421 nix: config.Nix{ 422 Name: "doesnotmatter", 423 SkipUpload: "auto", 424 IDs: []string{"foo"}, 425 Repository: config.RepoRef{ 426 Owner: "foo", 427 Name: "bar", 428 }, 429 }, 430 }, 431 } { 432 t.Run(tt.name, func(t *testing.T) { 433 folder := t.TempDir() 434 ctx := testctx.NewWithCfg( 435 config.Project{ 436 Dist: folder, 437 ProjectName: "foo", 438 Nix: []config.Nix{tt.nix}, 439 }, 440 testctx.WithVersion("1.2.1"), 441 testctx.WithCurrentTag("v1.2.1"), 442 testctx.WithSemver(1, 2, 1, "rc1"), 443 ) 444 createFakeArtifact := func(id, goos, goarch, goamd64, goarm, format string, extra map[string]any) { 445 if goarch != "arm" { 446 goarm = "" 447 } 448 if goarch != "amd64" { 449 goamd64 = "" 450 } 451 path := filepath.Join(folder, "dist/foo_"+goos+goarch+goamd64+goarm+"."+format) 452 art := artifact.Artifact{ 453 Name: "foo_" + goos + "_" + goarch + goamd64 + goarm + "." + format, 454 Path: path, 455 Goos: goos, 456 Goarch: goarch, 457 Goarm: goarm, 458 Goamd64: goamd64, 459 Type: artifact.UploadableArchive, 460 Extra: map[string]interface{}{ 461 artifact.ExtraID: id, 462 artifact.ExtraFormat: format, 463 artifact.ExtraBinaries: []string{"foo"}, 464 artifact.ExtraWrappedIn: "", 465 }, 466 } 467 for k, v := range extra { 468 art.Extra[k] = v 469 } 470 ctx.Artifacts.Add(&art) 471 472 require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755)) 473 f, err := os.Create(path) 474 require.NoError(t, err) 475 require.NoError(t, f.Close()) 476 } 477 478 createFakeArtifact("unibin-replaces", "darwin", "all", "", "", "tar.gz", map[string]any{artifact.ExtraReplaces: true}) 479 createFakeArtifact("unibin", "darwin", "all", "", "", "tar.gz", nil) 480 for _, goos := range []string{"linux", "darwin", "windows"} { 481 for _, goarch := range []string{"amd64", "arm64", "386", "arm"} { 482 if goos+goarch == "darwin386" { 483 continue 484 } 485 if goarch == "amd64" { 486 createFakeArtifact("partial", goos, goarch, "v1", "", "tar.gz", nil) 487 createFakeArtifact("foo", goos, goarch, "v1", "", "tar.gz", nil) 488 createFakeArtifact("unibin", goos, goarch, "v1", "", "tar.gz", nil) 489 if goos != "darwin" { 490 createFakeArtifact("unibin-replaces", goos, goarch, "v1", "", "tar.gz", nil) 491 } 492 createFakeArtifact("wrapped-in-dir", goos, goarch, "v1", "", "tar.gz", map[string]any{artifact.ExtraWrappedIn: "./foo_" + goarch}) 493 createFakeArtifact("foo-zip", goos, goarch, "v1", "", "zip", nil) 494 continue 495 } 496 if goarch == "arm" { 497 if goos != "linux" { 498 continue 499 } 500 createFakeArtifact("foo", goos, goarch, "", "6", "tar.gz", nil) 501 createFakeArtifact("foo", goos, goarch, "", "7", "tar.gz", nil) 502 createFakeArtifact("foo-zip", goos, goarch, "", "", "zip", nil) 503 createFakeArtifact("unibin-replaces", goos, goarch, "", "", "tar.gz", nil) 504 continue 505 } 506 createFakeArtifact("foo", goos, goarch, "", "", "tar.gz", nil) 507 createFakeArtifact("unibin", goos, goarch, "", "", "tar.gz", nil) 508 if goos != "darwin" { 509 createFakeArtifact("unibin-replaces", goos, goarch, "", "", "tar.gz", nil) 510 } 511 createFakeArtifact("wrapped-in-dir", goos, goarch, "", "", "tar.gz", map[string]any{artifact.ExtraWrappedIn: "./foo_" + goarch}) 512 createFakeArtifact("foo-zip", goos, goarch, "v1", "", "zip", nil) 513 if goos == "darwin" { 514 createFakeArtifact("zip-and-tar", goos, goarch, "v1", "", "zip", nil) 515 } else { 516 createFakeArtifact("zip-and-tar", goos, goarch, "v1", "", "tar.gz", nil) 517 } 518 } 519 } 520 521 client := client.NewMock() 522 bpipe := NewBuild() 523 ppipe := Pipe{ 524 fakeNixShaPrefetcher{ 525 "https://dummyhost/download/v1.2.1/foo_linux_amd64v1.tar.gz": "sha1", 526 "https://dummyhost/download/v1.2.1/foo_linux_arm64.tar.gz": "sha2", 527 "https://dummyhost/download/v1.2.1/foo_darwin_amd64v1.tar.gz": "sha3", 528 "https://dummyhost/download/v1.2.1/foo_darwin_arm64.tar.gz": "sha4", 529 "https://dummyhost/download/v1.2.1/foo_darwin_all.tar.gz": "sha5", 530 "https://dummyhost/download/v1.2.1/foo_linux_arm6.tar.gz": "sha6", 531 "https://dummyhost/download/v1.2.1/foo_linux_arm7.tar.gz": "sha7", 532 "https://dummyhost/download/v1.2.1/foo_linux_amd64v1.zip": "sha8", 533 "https://dummyhost/download/v1.2.1/foo_linux_arm64.zip": "sha9", 534 "https://dummyhost/download/v1.2.1/foo_darwin_amd64v1.zip": "sha10", 535 "https://dummyhost/download/v1.2.1/foo_darwin_arm64.zip": "sha11", 536 "https://dummyhost/download/v1.2.1/foo_darwin_all.zip": "sha12", 537 "https://dummyhost/download/v1.2.1/foo_linux_arm6.zip": "sha13", 538 "https://dummyhost/download/v1.2.1/foo_linux_arm7.zip": "sha14", 539 "https://dummyhost/download/v1.2.1/foo_linux_386.zip": "sha15", 540 "https://dummyhost/download/v1.2.1/foo_linux_386.tar.gz": "sha16", 541 }, 542 } 543 544 // default 545 if tt.expectDefaultErrorIs != nil { 546 err := bpipe.Default(ctx) 547 require.ErrorAs(t, err, &tt.expectDefaultErrorIs) 548 return 549 550 } 551 require.NoError(t, bpipe.Default(ctx)) 552 553 // run 554 if tt.expectRunErrorIs != nil { 555 err := bpipe.runAll(ctx, client) 556 require.ErrorAs(t, err, &tt.expectRunErrorIs) 557 return 558 } 559 require.NoError(t, bpipe.runAll(ctx, client)) 560 bts, err := os.ReadFile(ctx.Artifacts.Filter(artifact.ByType(artifact.Nixpkg)).Paths()[0]) 561 require.NoError(t, err) 562 golden.RequireEqualExt(t, bts, "_build.nix") 563 564 // publish 565 if tt.expectPublishErrorIs != nil { 566 err := ppipe.publishAll(ctx, client) 567 require.ErrorAs(t, err, &tt.expectPublishErrorIs) 568 return 569 } 570 require.NoError(t, ppipe.publishAll(ctx, client)) 571 require.True(t, client.CreatedFile) 572 golden.RequireEqualExt(t, []byte(client.Content), "_publish.nix") 573 require.NotContains(t, client.Content, strings.Repeat("0", 52)) 574 575 if tt.nix.Repository.PullRequest.Enabled { 576 require.True(t, client.OpenedPullRequest) 577 require.True(t, client.SyncedFork) 578 } 579 if tt.nix.Path != "" { 580 require.Equal(t, tt.nix.Path, client.Path) 581 } else { 582 if tt.nix.Name == "" { 583 tt.nix.Name = "foo" 584 } 585 require.Equal(t, "pkgs/"+tt.nix.Name+"/default.nix", client.Path) 586 } 587 }) 588 } 589 } 590 591 func TestErrNoArchivesFound(t *testing.T) { 592 require.EqualError(t, errNoArchivesFound{ 593 goamd64: "v1", 594 ids: []string{"foo", "bar"}, 595 }, "no archives found matching goos=[darwin linux] goarch=[amd64 arm arm64 386] goarm=[6 7] goamd64=v1 ids=[foo bar]") 596 } 597 598 func TestDependencies(t *testing.T) { 599 require.Equal(t, []string{"nix-prefetch-url"}, Pipe{}.Dependencies(nil)) 600 } 601 602 func TestBinInstallFormats(t *testing.T) { 603 t.Run("no-deps", func(t *testing.T) { 604 golden.RequireEqual(t, []byte(strings.Join( 605 binInstallFormats(config.Nix{}), 606 "\n", 607 ))) 608 }) 609 t.Run("deps", func(t *testing.T) { 610 golden.RequireEqual(t, []byte(strings.Join( 611 binInstallFormats(config.Nix{ 612 Dependencies: []config.NixDependency{ 613 {Name: "fish"}, 614 {Name: "bash"}, 615 {Name: "zsh"}, 616 }, 617 }), 618 "\n", 619 ))) 620 }) 621 t.Run("linux-only-deps", func(t *testing.T) { 622 golden.RequireEqual(t, []byte(strings.Join( 623 binInstallFormats(config.Nix{ 624 Dependencies: []config.NixDependency{ 625 linuxDep("foo"), 626 linuxDep("bar"), 627 }, 628 }), 629 "\n", 630 ))) 631 }) 632 t.Run("darwin-only-deps", func(t *testing.T) { 633 golden.RequireEqual(t, []byte(strings.Join( 634 binInstallFormats(config.Nix{ 635 Dependencies: []config.NixDependency{ 636 darwinDep("foo"), 637 darwinDep("bar"), 638 }, 639 }), 640 "\n", 641 ))) 642 }) 643 t.Run("mixed-deps", func(t *testing.T) { 644 golden.RequireEqual(t, []byte(strings.Join( 645 binInstallFormats(config.Nix{ 646 Dependencies: []config.NixDependency{ 647 {Name: "fish"}, 648 linuxDep("foo"), 649 darwinDep("bar"), 650 }, 651 }), 652 "\n", 653 ))) 654 }) 655 } 656 657 func darwinDep(s string) config.NixDependency { 658 return config.NixDependency{ 659 Name: s, 660 OS: "darwin", 661 } 662 } 663 664 func linuxDep(s string) config.NixDependency { 665 return config.NixDependency{ 666 Name: s, 667 OS: "linux", 668 } 669 } 670 671 type fakeNixShaPrefetcher map[string]string 672 673 func (m fakeNixShaPrefetcher) Prefetch(url string) (string, error) { 674 return m[url], nil 675 } 676 func (m fakeNixShaPrefetcher) Available() bool { return true }