github.com/windmeup/goreleaser@v1.21.95/internal/pipe/winget/winget_test.go (about) 1 package winget 2 3 import ( 4 "html/template" 5 "os" 6 "path/filepath" 7 "strings" 8 "testing" 9 "time" 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("should", 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, Pipe{}.Skip(testctx.NewWithCfg(config.Project{ 34 Winget: []config.Winget{{}}, 35 }, testctx.Skip(skips.Winget)))) 36 }) 37 t.Run("should not", func(t *testing.T) { 38 require.False(t, Pipe{}.Skip(testctx.NewWithCfg(config.Project{ 39 Winget: []config.Winget{{}}, 40 }))) 41 }) 42 } 43 44 func TestRunPipe(t *testing.T) { 45 for _, tt := range []struct { 46 name string 47 expectRunErrorIs error 48 expectPublishErrorIs error 49 expectPath string 50 winget config.Winget 51 }{ 52 { 53 name: "minimal", 54 expectPath: "manifests/f/Foo/min/1.2.1/Foo.min.", 55 winget: config.Winget{ 56 Name: "min", 57 Publisher: "Foo", 58 License: "MIT", 59 ShortDescription: "foo bar zaz", 60 IDs: []string{"foo"}, 61 Repository: config.RepoRef{ 62 Owner: "foo", 63 Name: "bar", 64 }, 65 }, 66 }, 67 { 68 name: "full", 69 expectPath: "manifests/b/Beckersoft LTDA/foo/1.2.1", 70 winget: config.Winget{ 71 Name: "foo", 72 Publisher: "Beckersoft", 73 PublisherURL: "https://carlosbecker.com", 74 PublisherSupportURL: "https://carlosbecker.com/support", 75 Copyright: "bla bla bla", 76 CopyrightURL: "https://goreleaser.com/copyright", 77 Author: "Carlos Becker", 78 Path: "manifests/b/Beckersoft LTDA/foo/{{.Version}}", 79 Repository: config.RepoRef{Owner: "foo", Name: "bar"}, 80 CommitAuthor: config.CommitAuthor{}, 81 IDs: []string{"foo"}, 82 Goamd64: "v1", 83 SkipUpload: "false", 84 ShortDescription: "foo", 85 Description: `long foo bar 86 87 yadaa yada yada loooaaasssss 88 89 sss`, 90 Homepage: "https://goreleaser.com", 91 License: "MIT", 92 LicenseURL: "https://goreleaser.com/eula/", 93 ReleaseNotesURL: "https://github.com/windmeup/goreleaser/tags/{{.Tag}}", 94 ReleaseNotes: "{{.Changelog}}", 95 Tags: []string{"foo", "bar"}, 96 }, 97 }, 98 { 99 name: "open-pr", 100 winget: config.Winget{ 101 Name: "foo", 102 Publisher: "Beckersoft", 103 IDs: []string{"foo"}, 104 Description: "my test", 105 Homepage: "https://goreleaser.com", 106 License: "mit", 107 Path: "pkgs/foo.winget", 108 ShortDescription: "foo bar zaz", 109 Repository: config.RepoRef{ 110 Owner: "foo", 111 Name: "bar", 112 Branch: "update-{{.Version}}", 113 PullRequest: config.PullRequest{ 114 Enabled: true, 115 }, 116 }, 117 }, 118 }, 119 { 120 name: "wrapped-in-dir", 121 winget: config.Winget{ 122 Name: "wrapped-in-dir", 123 Publisher: "Beckersoft", 124 IDs: []string{"wrapped-in-dir"}, 125 Description: "my test", 126 Homepage: "https://goreleaser.com", 127 License: "mit", 128 LicenseURL: "https://goreleaser.com/license", 129 ReleaseNotesURL: "https://github.com/windmeup/goreleaser/tags/{{.Tag}}", 130 ShortDescription: "foo bar zaz", 131 Repository: config.RepoRef{ 132 Owner: "foo", 133 Name: "bar", 134 }, 135 }, 136 }, 137 { 138 name: "no-archives", 139 expectRunErrorIs: errNoArchivesFound{ 140 goamd64: "v2", 141 ids: []string{"nopenopenope"}, 142 }, 143 winget: config.Winget{ 144 Name: "no-archives", 145 Publisher: "Beckersoft", 146 IDs: []string{"nopenopenope"}, 147 Goamd64: "v2", 148 License: "MIT", 149 ShortDescription: "foo bar zaz", 150 Repository: config.RepoRef{ 151 Owner: "foo", 152 Name: "bar", 153 }, 154 }, 155 }, 156 { 157 name: "too-many-archives", 158 expectRunErrorIs: errMultipleArchives, 159 winget: config.Winget{ 160 Name: "min", 161 Publisher: "Foo", 162 License: "MIT", 163 ShortDescription: "foo bar zaz", 164 IDs: []string{}, 165 Repository: config.RepoRef{ 166 Owner: "foo", 167 Name: "bar", 168 }, 169 }, 170 }, 171 { 172 name: "partial", 173 winget: config.Winget{ 174 Name: "partial", 175 Publisher: "Beckersoft", 176 IDs: []string{"partial"}, 177 License: "MIT", 178 ShortDescription: "foo bar zaz", 179 Repository: config.RepoRef{ 180 Owner: "foo", 181 Name: "bar", 182 }, 183 }, 184 }, 185 { 186 name: "no-repo-name", 187 expectRunErrorIs: errNoRepoName, 188 winget: config.Winget{ 189 Name: "doesnotmatter", 190 Publisher: "Beckersoft", 191 License: "MIT", 192 ShortDescription: "foo bar zaz", 193 Repository: config.RepoRef{ 194 Owner: "foo", 195 }, 196 }, 197 }, 198 { 199 name: "no-license", 200 expectRunErrorIs: errNoLicense, 201 winget: config.Winget{ 202 Name: "doesnotmatter", 203 Publisher: "Beckersoft", 204 ShortDescription: "aa", 205 Repository: config.RepoRef{ 206 Name: "foo", 207 Owner: "foo", 208 }, 209 }, 210 }, 211 { 212 name: "no-short-description", 213 expectRunErrorIs: errNoShortDescription, 214 winget: config.Winget{ 215 Name: "doesnotmatter", 216 Publisher: "Beckersoft", 217 License: "MIT", 218 Repository: config.RepoRef{ 219 Name: "foo", 220 Owner: "foo", 221 }, 222 }, 223 }, 224 { 225 name: "invalid-package-identifier", 226 expectRunErrorIs: errInvalidPackageIdentifier, 227 winget: config.Winget{ 228 Name: "min", 229 PackageIdentifier: "foobar", 230 Publisher: "Foo", 231 License: "MIT", 232 ShortDescription: "foo bar zaz", 233 Repository: config.RepoRef{ 234 Owner: "foo", 235 Name: "bar", 236 }, 237 }, 238 }, 239 { 240 name: "no-publisher", 241 expectRunErrorIs: errNoPublisher, 242 winget: config.Winget{ 243 Name: "min", 244 License: "MIT", 245 ShortDescription: "foo bar zaz", 246 Repository: config.RepoRef{ 247 Owner: "foo", 248 Name: "bar", 249 }, 250 }, 251 }, 252 { 253 name: "bad-name-tmpl", 254 expectRunErrorIs: &template.Error{}, 255 winget: config.Winget{ 256 Name: "{{ .Nope }}", 257 Publisher: "Beckersoft", 258 License: "MIT", 259 ShortDescription: "foo bar zaz", 260 Repository: config.RepoRef{ 261 Owner: "foo", 262 Name: "bar", 263 }, 264 }, 265 }, 266 { 267 name: "bad-releasenotes-tmpl", 268 expectRunErrorIs: &template.Error{}, 269 winget: config.Winget{ 270 ReleaseNotes: "{{ .Nope }}", 271 Publisher: "Beckersoft", 272 License: "MIT", 273 ShortDescription: "foo bar zaz", 274 Repository: config.RepoRef{ 275 Owner: "foo", 276 Name: "bar", 277 }, 278 }, 279 }, 280 { 281 name: "bad-publisher-tmpl", 282 expectRunErrorIs: &template.Error{}, 283 winget: config.Winget{ 284 Name: "foo", 285 Publisher: "{{ .Nope }}", 286 License: "MIT", 287 ShortDescription: "foo bar zaz", 288 Repository: config.RepoRef{ 289 Owner: "foo", 290 Name: "bar", 291 }, 292 }, 293 }, 294 { 295 name: "bad-publisher-url-tmpl", 296 expectRunErrorIs: &template.Error{}, 297 winget: config.Winget{ 298 Name: "foo", 299 Publisher: "Beckersoft", 300 PublisherURL: "{{ .Nope }}", 301 License: "MIT", 302 ShortDescription: "foo bar zaz", 303 Repository: config.RepoRef{ 304 Owner: "foo", 305 Name: "bar", 306 }, 307 }, 308 }, 309 { 310 name: "bad-author-tmpl", 311 expectRunErrorIs: &template.Error{}, 312 winget: config.Winget{ 313 Name: "foobar", 314 Publisher: "Beckersoft", 315 Author: "{{ .Nope }}", 316 License: "MIT", 317 ShortDescription: "foo bar zaz", 318 Repository: config.RepoRef{ 319 Owner: "foo", 320 Name: "bar", 321 }, 322 }, 323 }, 324 { 325 name: "bad-homepage-tmpl", 326 expectRunErrorIs: &template.Error{}, 327 winget: config.Winget{ 328 Name: "foobar", 329 Publisher: "Beckersoft", 330 Homepage: "{{ .Nope }}", 331 License: "MIT", 332 ShortDescription: "foo bar zaz", 333 Repository: config.RepoRef{ 334 Owner: "foo", 335 Name: "bar", 336 }, 337 }, 338 }, 339 { 340 name: "bad-description-tmpl", 341 expectRunErrorIs: &template.Error{}, 342 winget: config.Winget{ 343 Name: "foobar", 344 Publisher: "Beckersoft", 345 Description: "{{ .Nope }}", 346 License: "MIT", 347 ShortDescription: "foo bar zaz", 348 Repository: config.RepoRef{ 349 Owner: "foo", 350 Name: "bar", 351 }, 352 }, 353 }, 354 { 355 name: "bad-short-description-tmpl", 356 expectRunErrorIs: &template.Error{}, 357 winget: config.Winget{ 358 Name: "foobar", 359 Publisher: "Beckersoft", 360 ShortDescription: "{{ .Nope }}", 361 License: "MIT", 362 Repository: config.RepoRef{ 363 Owner: "foo", 364 Name: "bar", 365 }, 366 }, 367 }, 368 { 369 name: "bad-repo-tmpl", 370 expectRunErrorIs: &template.Error{}, 371 winget: config.Winget{ 372 Name: "doesnotmatter", 373 Publisher: "Beckersoft", 374 License: "MIT", 375 ShortDescription: "foo bar zaz", 376 Repository: config.RepoRef{ 377 Owner: "foo", 378 Name: "{{ .Nope }}", 379 }, 380 }, 381 }, 382 { 383 name: "bad-skip-upload-tmpl", 384 expectRunErrorIs: &template.Error{}, 385 winget: config.Winget{ 386 Name: "doesnotmatter", 387 Publisher: "Beckersoft", 388 SkipUpload: "{{ .Nope }}", 389 License: "MIT", 390 ShortDescription: "foo bar zaz", 391 Repository: config.RepoRef{ 392 Owner: "foo", 393 Name: "bar", 394 }, 395 }, 396 }, 397 { 398 name: "bad-release-notes-url-tmpl", 399 expectRunErrorIs: &template.Error{}, 400 winget: config.Winget{ 401 Name: "foo", 402 Publisher: "Beckersoft", 403 ReleaseNotesURL: `https://goo/bar/asdfsd/{{.nope}}`, 404 License: "MIT", 405 ShortDescription: "foo bar zaz", 406 Repository: config.RepoRef{ 407 Owner: "foo", 408 Name: "bar", 409 }, 410 }, 411 }, 412 { 413 name: "bad-release-url-tmpl", 414 expectRunErrorIs: &template.Error{}, 415 winget: config.Winget{ 416 Name: "foo", 417 Publisher: "Beckersoft", 418 URLTemplate: "{{.BadURL}}", 419 License: "MIT", 420 ShortDescription: "foo bar zaz", 421 Repository: config.RepoRef{ 422 Owner: "foo", 423 Name: "bar", 424 }, 425 }, 426 }, 427 { 428 name: "bad-path-tmpl", 429 expectRunErrorIs: &template.Error{}, 430 winget: config.Winget{ 431 Name: "foo", 432 Publisher: "Beckersoft", 433 License: "MIT", 434 Path: "{{ .Nope }}", 435 ShortDescription: "foo bar zaz", 436 Repository: config.RepoRef{ 437 Owner: "foo", 438 Name: "bar", 439 }, 440 }, 441 }, 442 { 443 name: "bad-commit-msg-tmpl", 444 expectPublishErrorIs: &template.Error{}, 445 winget: config.Winget{ 446 Name: "foo", 447 Publisher: "Beckersoft", 448 License: "MIT", 449 ShortDescription: "foo bar zaz", 450 CommitMessageTemplate: "{{.Foo}}", 451 IDs: []string{"foo"}, 452 Repository: config.RepoRef{ 453 Owner: "foo", 454 Name: "bar", 455 }, 456 }, 457 }, 458 { 459 name: "bad-publisher-support-url-tmpl", 460 expectRunErrorIs: &template.Error{}, 461 winget: config.Winget{ 462 Name: "foo", 463 Publisher: "Beckersoft", 464 PublisherSupportURL: "{{.Nope}}", 465 License: "MIT", 466 ShortDescription: "foo bar zaz", 467 Repository: config.RepoRef{ 468 Owner: "foo", 469 Name: "bar", 470 }, 471 }, 472 }, 473 { 474 name: "bad-copyright-tmpl", 475 expectRunErrorIs: &template.Error{}, 476 winget: config.Winget{ 477 Name: "foo", 478 Publisher: "Beckersoft", 479 License: "MIT", 480 Copyright: "{{ .Nope }}", 481 ShortDescription: "foo bar zaz", 482 Repository: config.RepoRef{ 483 Owner: "foo", 484 Name: "bar", 485 }, 486 }, 487 }, 488 { 489 name: "bad-copyright-url-tmpl", 490 expectRunErrorIs: &template.Error{}, 491 winget: config.Winget{ 492 Name: "{{ .Nope }}", 493 Publisher: "Beckersoft", 494 License: "MIT", 495 CopyrightURL: "{{ .Nope }}", 496 ShortDescription: "foo bar zaz", 497 Repository: config.RepoRef{ 498 Owner: "foo", 499 Name: "bar", 500 }, 501 }, 502 }, 503 { 504 name: "bad-license-tmpl", 505 expectRunErrorIs: &template.Error{}, 506 winget: config.Winget{ 507 Name: "foo", 508 Publisher: "Beckersoft", 509 License: "{{ .Nope }}", 510 ShortDescription: "foo bar zaz", 511 Repository: config.RepoRef{ 512 Owner: "foo", 513 Name: "bar", 514 }, 515 }, 516 }, 517 { 518 name: "bad-license-url-tmpl", 519 expectRunErrorIs: &template.Error{}, 520 winget: config.Winget{ 521 Name: "foo", 522 Publisher: "Beckersoft", 523 License: "MIT", 524 LicenseURL: "{{ .Nope }}", 525 ShortDescription: "foo bar zaz", 526 Repository: config.RepoRef{ 527 Owner: "foo", 528 Name: "bar", 529 }, 530 }, 531 }, 532 { 533 name: "skip-upload", 534 expectPublishErrorIs: errSkipUpload, 535 winget: config.Winget{ 536 Name: "doesnotmatter", 537 Publisher: "Beckersoft", 538 SkipUpload: "true", 539 License: "MIT", 540 IDs: []string{"foo"}, 541 ShortDescription: "foo bar zaz", 542 Repository: config.RepoRef{ 543 Owner: "foo", 544 Name: "bar", 545 }, 546 }, 547 }, 548 { 549 name: "skip-upload-auto", 550 expectPublishErrorIs: errSkipUploadAuto, 551 winget: config.Winget{ 552 Name: "doesnotmatter", 553 Publisher: "Beckersoft", 554 License: "MIT", 555 ShortDescription: "foo bar zaz", 556 IDs: []string{"foo"}, 557 SkipUpload: "auto", 558 Repository: config.RepoRef{ 559 Owner: "foo", 560 Name: "bar", 561 }, 562 }, 563 }, 564 { 565 name: "with-deps", 566 expectPath: "manifests/f/Foo/deps/1.2.1/Foo.deps.", 567 winget: config.Winget{ 568 Name: "deps", 569 Publisher: "Foo", 570 License: "MIT", 571 ShortDescription: "foo bar zaz", 572 IDs: []string{"foo"}, 573 Repository: config.RepoRef{ 574 Owner: "foo", 575 Name: "bar", 576 }, 577 Dependencies: []config.WingetDependency{ 578 { 579 PackageIdentifier: "foo.bar", 580 MinimumVersion: "1.2.3", 581 }, 582 { 583 PackageIdentifier: "zaz.bar", 584 }, 585 }, 586 }, 587 }, 588 { 589 name: "bad-dependency-template", 590 expectRunErrorIs: &template.Error{}, 591 winget: config.Winget{ 592 Name: "foo", 593 Publisher: "Beckersoft", 594 License: "MIT", 595 LicenseURL: "https://foo.bar", 596 ShortDescription: "foo bar zaz", 597 Repository: config.RepoRef{ 598 Owner: "foo", 599 Name: "bar", 600 }, 601 Dependencies: []config.WingetDependency{ 602 {PackageIdentifier: "{{.Nope}}"}, 603 }, 604 }, 605 }, 606 } { 607 t.Run(tt.name, func(t *testing.T) { 608 folder := t.TempDir() 609 ctx := testctx.NewWithCfg( 610 config.Project{ 611 Dist: folder, 612 ProjectName: "foo", 613 Winget: []config.Winget{tt.winget}, 614 }, 615 testctx.WithVersion("1.2.1"), 616 testctx.WithCurrentTag("v1.2.1"), 617 testctx.WithSemver(1, 2, 1, "rc1"), 618 testctx.WithDate(time.Date(2023, 6, 12, 20, 32, 10, 12, time.Local)), 619 ) 620 ctx.ReleaseNotes = "the changelog for this release..." 621 createFakeArtifact := func(id, goos, goarch, goamd64, goarm string, extra map[string]any) { 622 path := filepath.Join(folder, "dist/foo_"+goos+goarch+goamd64+goarm+".zip") 623 art := artifact.Artifact{ 624 Name: "foo_" + goos + "_" + goarch + goamd64 + goarm + ".zip", 625 Path: path, 626 Goos: goos, 627 Goarch: goarch, 628 Goarm: goarm, 629 Goamd64: goamd64, 630 Type: artifact.UploadableArchive, 631 Extra: map[string]interface{}{ 632 artifact.ExtraID: id, 633 artifact.ExtraFormat: "zip", 634 artifact.ExtraBinaries: []string{"foo.exe"}, 635 artifact.ExtraWrappedIn: "", 636 }, 637 } 638 for k, v := range extra { 639 art.Extra[k] = v 640 } 641 ctx.Artifacts.Add(&art) 642 643 require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755)) 644 f, err := os.Create(path) 645 require.NoError(t, err) 646 require.NoError(t, f.Close()) 647 } 648 649 goos := "windows" 650 goarch := "amd64" 651 createFakeArtifact("partial", goos, goarch, "v1", "", nil) 652 createFakeArtifact("foo", goos, goarch, "v1", "", nil) 653 createFakeArtifact("wrapped-in-dir", goos, goarch, "v1", "", map[string]any{ 654 artifact.ExtraWrappedIn: "foo", 655 artifact.ExtraBinaries: []string{"bin/foo.exe"}, 656 }) 657 658 goarch = "386" 659 createFakeArtifact("foo", goos, goarch, "", "", nil) 660 createFakeArtifact("wrapped-in-dir", goos, goarch, "", "", map[string]any{ 661 artifact.ExtraWrappedIn: "foo", 662 artifact.ExtraBinaries: []string{"bin/foo.exe"}, 663 }) 664 665 goarch = "arm64" 666 createFakeArtifact("foo", goos, goarch, "", "", nil) 667 createFakeArtifact("wrapped-in-dir", goos, goarch, "", "", map[string]any{ 668 artifact.ExtraWrappedIn: "foo", 669 artifact.ExtraBinaries: []string{"bin/foo.exe"}, 670 }) 671 672 client := client.NewMock() 673 pipe := Pipe{} 674 675 // default 676 require.NoError(t, pipe.Default(ctx)) 677 678 // run 679 if tt.expectRunErrorIs != nil { 680 err := pipe.runAll(ctx, client) 681 require.Error(t, err) 682 require.ErrorAs(t, err, &tt.expectPublishErrorIs) 683 return 684 } 685 686 require.NoError(t, pipe.runAll(ctx, client)) 687 for _, winget := range ctx.Artifacts.Filter(artifact.Or( 688 artifact.ByType(artifact.WingetInstaller), 689 artifact.ByType(artifact.WingetVersion), 690 artifact.ByType(artifact.WingetDefaultLocale), 691 )).List() { 692 bts, err := os.ReadFile(winget.Path) 693 require.NoError(t, err) 694 golden.RequireEqualExtSubfolder(t, bts, extFor(winget.Type)) 695 } 696 697 // publish 698 if tt.expectPublishErrorIs != nil { 699 err := pipe.publishAll(ctx, client) 700 require.Error(t, err) 701 require.ErrorAs(t, err, &tt.expectPublishErrorIs) 702 return 703 } 704 require.NoError(t, pipe.publishAll(ctx, client)) 705 require.True(t, client.CreatedFile) 706 707 require.Len(t, client.Messages, 3) 708 expected := map[string]bool{ 709 "locale": false, 710 "version": false, 711 "installer": false, 712 } 713 for _, msg := range client.Messages { 714 require.Regexp(t, "New version: \\w+\\.[\\w-]+ 1.2.1", msg) 715 for k, v := range expected { 716 if strings.Contains(msg, ": add "+k) { 717 require.False(t, v, "multiple commits for "+k) 718 expected[k] = true 719 } 720 } 721 } 722 723 require.NotEmpty(t, client.Path) 724 if tt.expectPath != "" { 725 require.Truef(t, strings.HasPrefix(client.Path, tt.expectPath), "expected %q to begin with %q", client.Path, tt.expectPath) 726 } 727 728 if tt.winget.Repository.PullRequest.Enabled { 729 require.True(t, client.OpenedPullRequest) 730 } 731 }) 732 } 733 } 734 735 func TestErrNoArchivesFound(t *testing.T) { 736 require.EqualError(t, errNoArchivesFound{ 737 goamd64: "v1", 738 ids: []string{"foo", "bar"}, 739 }, "no zip archives found matching goos=[windows] goarch=[amd64 386] goamd64=v1 ids=[foo bar]") 740 } 741 742 func TestDefault(t *testing.T) { 743 ctx := testctx.NewWithCfg(config.Project{ 744 ProjectName: "foo", 745 Winget: []config.Winget{{}}, 746 }) 747 require.NoError(t, Pipe{}.Default(ctx)) 748 winget := ctx.Config.Winget[0] 749 require.Equal(t, "v1", winget.Goamd64) 750 require.NotEmpty(t, winget.CommitMessageTemplate) 751 require.Equal(t, "foo", winget.Name) 752 }