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