github.com/windmeup/goreleaser@v1.21.95/internal/pipe/archive/archive_test.go (about) 1 package archive 2 3 import ( 4 "archive/tar" 5 "compress/gzip" 6 "fmt" 7 "io" 8 "io/fs" 9 "os" 10 "path/filepath" 11 "testing" 12 13 "github.com/stretchr/testify/require" 14 "github.com/windmeup/goreleaser/internal/artifact" 15 "github.com/windmeup/goreleaser/internal/testctx" 16 "github.com/windmeup/goreleaser/internal/testlib" 17 "github.com/windmeup/goreleaser/pkg/archive" 18 "github.com/windmeup/goreleaser/pkg/config" 19 ) 20 21 func TestDescription(t *testing.T) { 22 require.NotEmpty(t, Pipe{}.String()) 23 } 24 25 func createFakeBinary(t *testing.T, dist, arch, bin string) { 26 t.Helper() 27 path := filepath.Join(dist, arch, bin) 28 require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755)) 29 f, err := os.Create(path) 30 require.NoError(t, err) 31 require.NoError(t, f.Close()) 32 } 33 34 func TestRunPipe(t *testing.T) { 35 folder := testlib.Mktmp(t) 36 for _, dets := range []struct { 37 Format string 38 Strip bool 39 }{ 40 { 41 Format: "tar.gz", 42 Strip: true, 43 }, 44 { 45 Format: "tar.gz", 46 Strip: false, 47 }, 48 49 { 50 Format: "zip", 51 Strip: true, 52 }, 53 { 54 Format: "zip", 55 Strip: false, 56 }, 57 } { 58 format := dets.Format 59 name := "archive." + format 60 if dets.Strip { 61 name = "strip_" + name 62 } 63 t.Run(name, func(t *testing.T) { 64 dist := filepath.Join(folder, name+"_dist") 65 require.NoError(t, os.Mkdir(dist, 0o755)) 66 for _, arch := range []string{"darwinamd64v1", "darwinall", "linux386", "linuxarm7", "linuxmipssoftfloat", "linuxamd64v3"} { 67 createFakeBinary(t, dist, arch, "bin/mybin") 68 } 69 createFakeBinary(t, dist, "windowsamd64", "bin/mybin.exe") 70 for _, tt := range []string{"darwin", "linux", "windows"} { 71 f, err := os.Create(filepath.Join(folder, fmt.Sprintf("README.%s.md", tt))) 72 require.NoError(t, err) 73 require.NoError(t, f.Close()) 74 } 75 require.NoError(t, os.MkdirAll(filepath.Join(folder, "foo", "bar", "foobar"), 0o755)) 76 f, err := os.Create(filepath.Join(filepath.Join(folder, "foo", "bar", "foobar", "blah.txt"))) 77 require.NoError(t, err) 78 require.NoError(t, f.Close()) 79 ctx := testctx.NewWithCfg( 80 config.Project{ 81 Dist: dist, 82 ProjectName: "foobar", 83 Archives: []config.Archive{ 84 { 85 ID: "myid", 86 Builds: []string{"default"}, 87 BuildsInfo: config.FileInfo{ 88 Owner: "root", 89 Group: "root", 90 }, 91 NameTemplate: defaultNameTemplate, 92 StripParentBinaryFolder: dets.Strip, 93 Files: []config.File{ 94 {Source: "README.{{.Os}}.*"}, 95 {Source: "./foo/**/*"}, 96 }, 97 FormatOverrides: []config.FormatOverride{ 98 { 99 Goos: "windows", 100 Format: "zip", 101 }, 102 }, 103 }, 104 }, 105 }, 106 ) 107 darwinUniversalBinary := &artifact.Artifact{ 108 Goos: "darwin", 109 Goarch: "all", 110 Name: "bin/mybin", 111 Path: filepath.Join(dist, "darwinall", "bin", "mybin"), 112 Type: artifact.UniversalBinary, 113 Extra: map[string]interface{}{ 114 artifact.ExtraBinary: "bin/mybin", 115 artifact.ExtraID: "default", 116 artifact.ExtraReplaces: true, 117 }, 118 } 119 darwinBuild := &artifact.Artifact{ 120 Goos: "darwin", 121 Goarch: "amd64", 122 Goamd64: "v1", 123 Name: "bin/mybin", 124 Path: filepath.Join(dist, "darwinamd64v1", "bin", "mybin"), 125 Type: artifact.Binary, 126 Extra: map[string]interface{}{ 127 artifact.ExtraBinary: "bin/mybin", 128 artifact.ExtraID: "default", 129 }, 130 } 131 linux386Build := &artifact.Artifact{ 132 Goos: "linux", 133 Goarch: "386", 134 Name: "bin/mybin", 135 Path: filepath.Join(dist, "linux386", "bin", "mybin"), 136 Type: artifact.Binary, 137 Extra: map[string]interface{}{ 138 artifact.ExtraBinary: "bin/mybin", 139 artifact.ExtraID: "default", 140 }, 141 } 142 linuxArmBuild := &artifact.Artifact{ 143 Goos: "linux", 144 Goarch: "arm", 145 Goarm: "7", 146 Name: "bin/mybin", 147 Path: filepath.Join(dist, "linuxarm7", "bin", "mybin"), 148 Type: artifact.Binary, 149 Extra: map[string]interface{}{ 150 artifact.ExtraBinary: "bin/mybin", 151 artifact.ExtraID: "default", 152 }, 153 } 154 linuxMipsBuild := &artifact.Artifact{ 155 Goos: "linux", 156 Goarch: "mips", 157 Gomips: "softfloat", 158 Name: "bin/mybin", 159 Path: filepath.Join(dist, "linuxmipssoftfloat", "bin", "mybin"), 160 Type: artifact.Binary, 161 Extra: map[string]interface{}{ 162 artifact.ExtraBinary: "mybin", 163 artifact.ExtraID: "default", 164 }, 165 } 166 windowsBuild := &artifact.Artifact{ 167 Goos: "windows", 168 Goarch: "amd64", 169 Goamd64: "v1", 170 Name: "bin/mybin.exe", 171 Path: filepath.Join(dist, "windowsamd64", "bin", "mybin.exe"), 172 Type: artifact.Binary, 173 Extra: map[string]interface{}{ 174 artifact.ExtraBinary: "mybin", 175 artifact.ExtraExt: ".exe", 176 artifact.ExtraID: "default", 177 }, 178 } 179 linuxAmd64Build := &artifact.Artifact{ 180 Goos: "linux", 181 Goarch: "amd64", 182 Goamd64: "v3", 183 Name: "bin/mybin", 184 Path: filepath.Join(dist, "linuxamd64v3", "bin", "mybin"), 185 Type: artifact.Binary, 186 Extra: map[string]interface{}{ 187 artifact.ExtraBinary: "mybin", 188 artifact.ExtraID: "default", 189 }, 190 } 191 ctx.Artifacts.Add(darwinBuild) 192 ctx.Artifacts.Add(darwinUniversalBinary) 193 ctx.Artifacts.Add(linux386Build) 194 ctx.Artifacts.Add(linuxArmBuild) 195 ctx.Artifacts.Add(linuxMipsBuild) 196 ctx.Artifacts.Add(windowsBuild) 197 ctx.Artifacts.Add(linuxAmd64Build) 198 ctx.Version = "0.0.1" 199 ctx.Git.CurrentTag = "v0.0.1" 200 ctx.Config.Archives[0].Format = format 201 require.NoError(t, Pipe{}.Run(ctx)) 202 archives := ctx.Artifacts.Filter(artifact.ByType(artifact.UploadableArchive)).List() 203 204 for _, arch := range archives { 205 expectBin := "bin/mybin" 206 if arch.Goos == "windows" { 207 expectBin += ".exe" 208 } 209 require.Equal(t, "myid", arch.ID(), "all archives must have the archive ID set") 210 require.Equal(t, []string{expectBin}, artifact.ExtraOr(*arch, artifact.ExtraBinaries, []string{})) 211 require.Equal(t, "", artifact.ExtraOr(*arch, artifact.ExtraBinary, "")) 212 } 213 require.Len(t, archives, 7) 214 // TODO: should verify the artifact fields here too 215 216 expectBin := "bin/mybin" 217 if dets.Strip { 218 expectBin = "mybin" 219 } 220 221 if format == "tar.gz" { 222 // Check archive contents 223 for name, os := range map[string]string{ 224 "foobar_0.0.1_darwin_amd64.tar.gz": "darwin", 225 "foobar_0.0.1_darwin_all.tar.gz": "darwin", 226 "foobar_0.0.1_linux_386.tar.gz": "linux", 227 "foobar_0.0.1_linux_armv7.tar.gz": "linux", 228 "foobar_0.0.1_linux_mips_softfloat.tar.gz": "linux", 229 "foobar_0.0.1_linux_amd64v3.tar.gz": "linux", 230 } { 231 require.Equal( 232 t, 233 []string{ 234 fmt.Sprintf("README.%s.md", os), 235 "foo/bar/foobar/blah.txt", 236 expectBin, 237 }, 238 testlib.LsArchive(t, filepath.Join(dist, name), "tar.gz"), 239 ) 240 241 header := tarInfo(t, filepath.Join(dist, name), expectBin) 242 require.Equal(t, "root", header.Uname) 243 require.Equal(t, "root", header.Gname) 244 245 } 246 } 247 if format == "zip" { 248 require.Equal( 249 t, 250 []string{ 251 "README.windows.md", 252 "foo/bar/foobar/blah.txt", 253 expectBin + ".exe", 254 }, 255 testlib.LsArchive(t, filepath.Join(dist, "foobar_0.0.1_windows_amd64.zip"), "zip"), 256 ) 257 } 258 }) 259 } 260 } 261 262 func TestRunPipeDifferentBinaryCount(t *testing.T) { 263 folder := testlib.Mktmp(t) 264 dist := filepath.Join(folder, "dist") 265 require.NoError(t, os.Mkdir(dist, 0o755)) 266 for _, arch := range []string{"darwinamd64", "linuxamd64"} { 267 createFakeBinary(t, dist, arch, "bin/mybin") 268 } 269 createFakeBinary(t, dist, "darwinamd64", "bin/foobar") 270 ctx := testctx.NewWithCfg(config.Project{ 271 Dist: dist, 272 ProjectName: "foobar", 273 Archives: []config.Archive{ 274 { 275 ID: "myid", 276 Format: "tar.gz", 277 Builds: []string{"default", "foobar"}, 278 NameTemplate: defaultNameTemplate, 279 }, 280 }, 281 }) 282 darwinBuild := &artifact.Artifact{ 283 Goos: "darwin", 284 Goarch: "amd64", 285 Name: "bin/mybin", 286 Path: filepath.Join(dist, "darwinamd64", "bin", "mybin"), 287 Type: artifact.Binary, 288 Extra: map[string]interface{}{ 289 artifact.ExtraBinary: "bin/mybin", 290 artifact.ExtraID: "default", 291 }, 292 } 293 darwinBuild2 := &artifact.Artifact{ 294 Goos: "darwin", 295 Goarch: "amd64", 296 Name: "bin/foobar", 297 Path: filepath.Join(dist, "darwinamd64", "bin", "foobar"), 298 Type: artifact.Binary, 299 Extra: map[string]interface{}{ 300 artifact.ExtraBinary: "bin/foobar", 301 artifact.ExtraID: "foobar", 302 }, 303 } 304 linuxArmBuild := &artifact.Artifact{ 305 Goos: "linux", 306 Goarch: "amd64", 307 Name: "bin/mybin", 308 Path: filepath.Join(dist, "linuxamd64", "bin", "mybin"), 309 Type: artifact.Binary, 310 Extra: map[string]interface{}{ 311 artifact.ExtraBinary: "bin/mybin", 312 artifact.ExtraID: "default", 313 }, 314 } 315 316 ctx.Artifacts.Add(darwinBuild) 317 ctx.Artifacts.Add(darwinBuild2) 318 ctx.Artifacts.Add(linuxArmBuild) 319 ctx.Version = "0.0.1" 320 ctx.Git.CurrentTag = "v0.0.1" 321 322 t.Run("check enabled", func(t *testing.T) { 323 ctx.Config.Archives[0].AllowDifferentBinaryCount = false 324 require.EqualError(t, Pipe{}.Run(ctx), "invalid archive: 0: "+ErrArchiveDifferentBinaryCount.Error()) 325 }) 326 327 t.Run("check disabled", func(t *testing.T) { 328 ctx.Config.Archives[0].AllowDifferentBinaryCount = true 329 require.NoError(t, Pipe{}.Run(ctx)) 330 }) 331 } 332 333 func TestRunPipeNoBinaries(t *testing.T) { 334 folder := testlib.Mktmp(t) 335 dist := filepath.Join(folder, "dist") 336 require.NoError(t, os.Mkdir(dist, 0o755)) 337 ctx := testctx.NewWithCfg(config.Project{ 338 Dist: dist, 339 ProjectName: "foobar", 340 Archives: []config.Archive{{ 341 Builds: []string{"not-default"}, 342 }}, 343 }, testctx.WithVersion("0.0.1"), testctx.WithCurrentTag("v1.0.0")) 344 ctx.Artifacts.Add(&artifact.Artifact{ 345 Goos: "linux", 346 Goarch: "amd64", 347 Name: "bin/mybin", 348 Path: filepath.Join(dist, "linuxamd64", "bin", "mybin"), 349 Type: artifact.Binary, 350 Extra: map[string]interface{}{ 351 artifact.ExtraBinary: "bin/mybin", 352 artifact.ExtraID: "default", 353 }, 354 }) 355 require.NoError(t, Pipe{}.Run(ctx)) 356 } 357 358 func tarInfo(t *testing.T, path, name string) *tar.Header { 359 t.Helper() 360 f, err := os.Open(path) 361 require.NoError(t, err) 362 defer f.Close() 363 gr, err := gzip.NewReader(f) 364 require.NoError(t, err) 365 defer gr.Close() 366 r := tar.NewReader(gr) 367 for { 368 next, err := r.Next() 369 if err == io.EOF { 370 break 371 } 372 if next.Name == name { 373 return next 374 } 375 } 376 return nil 377 } 378 379 func TestRunPipeBinary(t *testing.T) { 380 folder := testlib.Mktmp(t) 381 dist := filepath.Join(folder, "dist") 382 require.NoError(t, os.Mkdir(dist, 0o755)) 383 require.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0o755)) 384 require.NoError(t, os.Mkdir(filepath.Join(dist, "windowsamd64"), 0o755)) 385 f, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin")) 386 require.NoError(t, err) 387 require.NoError(t, f.Close()) 388 f, err = os.Create(filepath.Join(dist, "windowsamd64", "mybin.exe")) 389 require.NoError(t, err) 390 require.NoError(t, f.Close()) 391 f, err = os.Create(filepath.Join(dist, "windowsamd64", "myotherbin")) 392 require.NoError(t, err) 393 require.NoError(t, f.Close()) 394 f, err = os.Create(filepath.Join(folder, "README.md")) 395 require.NoError(t, err) 396 require.NoError(t, f.Close()) 397 ctx := testctx.NewWithCfg( 398 config.Project{ 399 Dist: dist, 400 Archives: []config.Archive{ 401 { 402 Format: "binary", 403 NameTemplate: defaultBinaryNameTemplate, 404 Builds: []string{"default", "default2"}, 405 }, 406 }, 407 }, 408 testctx.WithVersion("0.0.1"), 409 testctx.WithCurrentTag("v0.0.1"), 410 ) 411 ctx.Artifacts.Add(&artifact.Artifact{ 412 Goos: "darwin", 413 Goarch: "amd64", 414 Name: "mybin", 415 Path: filepath.Join(dist, "darwinamd64", "mybin"), 416 Type: artifact.Binary, 417 Extra: map[string]interface{}{ 418 artifact.ExtraBinary: "mybin", 419 artifact.ExtraID: "default", 420 }, 421 }) 422 ctx.Artifacts.Add(&artifact.Artifact{ 423 Goos: "darwin", 424 Goarch: "all", 425 Name: "myunibin", 426 Path: filepath.Join(dist, "darwinamd64", "mybin"), 427 Type: artifact.UniversalBinary, 428 Extra: map[string]interface{}{ 429 artifact.ExtraBinary: "myunibin", 430 artifact.ExtraID: "default", 431 artifact.ExtraReplaces: true, 432 }, 433 }) 434 ctx.Artifacts.Add(&artifact.Artifact{ 435 Goos: "windows", 436 Goarch: "amd64", 437 Name: "mybin.exe", 438 Path: filepath.Join(dist, "windowsamd64", "mybin.exe"), 439 Type: artifact.Binary, 440 Extra: map[string]interface{}{ 441 artifact.ExtraBinary: "mybin", 442 artifact.ExtraExt: ".exe", 443 artifact.ExtraID: "default", 444 }, 445 }) 446 ctx.Artifacts.Add(&artifact.Artifact{ 447 Goos: "windows", 448 Goarch: "amd64", 449 Name: "myotherbin.exe", 450 Path: filepath.Join(dist, "windowsamd64", "myotherbin.exe"), 451 Type: artifact.Binary, 452 Extra: map[string]interface{}{ 453 artifact.ExtraBinary: "myotherbin", 454 artifact.ExtraExt: ".exe", 455 artifact.ExtraID: "default2", 456 }, 457 }) 458 459 require.NoError(t, Pipe{}.Run(ctx)) 460 binaries := ctx.Artifacts.Filter(artifact.ByType(artifact.UploadableBinary)) 461 require.Len(t, binaries.List(), 4) 462 darwinThin := binaries.Filter(artifact.And( 463 artifact.ByGoos("darwin"), 464 artifact.ByGoarch("amd64"), 465 )).List()[0] 466 darwinUniversal := binaries.Filter(artifact.And( 467 artifact.ByGoos("darwin"), 468 artifact.ByGoarch("all"), 469 )).List()[0] 470 require.True(t, artifact.ExtraOr(*darwinUniversal, artifact.ExtraReplaces, false)) 471 windows := binaries.Filter(artifact.ByGoos("windows")).List()[0] 472 windows2 := binaries.Filter(artifact.ByGoos("windows")).List()[1] 473 require.Equal(t, "mybin_0.0.1_darwin_amd64", darwinThin.Name) 474 require.Equal(t, "mybin", artifact.ExtraOr(*darwinThin, artifact.ExtraBinary, "")) 475 require.Equal(t, "myunibin_0.0.1_darwin_all", darwinUniversal.Name) 476 require.Equal(t, "myunibin", artifact.ExtraOr(*darwinUniversal, artifact.ExtraBinary, "")) 477 require.Equal(t, "mybin_0.0.1_windows_amd64.exe", windows.Name) 478 require.Equal(t, "mybin.exe", artifact.ExtraOr(*windows, artifact.ExtraBinary, "")) 479 require.Equal(t, "myotherbin_0.0.1_windows_amd64.exe", windows2.Name) 480 require.Equal(t, "myotherbin.exe", artifact.ExtraOr(*windows2, artifact.ExtraBinary, "")) 481 } 482 483 func TestRunPipeDistRemoved(t *testing.T) { 484 ctx := testctx.NewWithCfg( 485 config.Project{ 486 Dist: "/tmp/path/to/nope", 487 Archives: []config.Archive{ 488 { 489 NameTemplate: "nope", 490 Format: "zip", 491 Builds: []string{"default"}, 492 }, 493 }, 494 }, 495 testctx.WithCurrentTag("v0.0.1"), 496 ) 497 ctx.Artifacts.Add(&artifact.Artifact{ 498 Goos: "windows", 499 Goarch: "amd64", 500 Name: "mybin.exe", 501 Path: filepath.Join("/tmp/path/to/nope", "windowsamd64", "mybin.exe"), 502 Type: artifact.Binary, 503 Extra: map[string]interface{}{ 504 artifact.ExtraBinary: "mybin", 505 artifact.ExtraExt: ".exe", 506 artifact.ExtraID: "default", 507 }, 508 }) 509 // not checking on error msg because it may change depending on OS/version 510 require.Error(t, Pipe{}.Run(ctx)) 511 } 512 513 func TestRunPipeInvalidGlob(t *testing.T) { 514 folder := testlib.Mktmp(t) 515 dist := filepath.Join(folder, "dist") 516 require.NoError(t, os.Mkdir(dist, 0o755)) 517 require.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0o755)) 518 f, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin")) 519 require.NoError(t, err) 520 require.NoError(t, f.Close()) 521 ctx := testctx.NewWithCfg( 522 config.Project{ 523 Dist: dist, 524 Archives: []config.Archive{ 525 { 526 Builds: []string{"default"}, 527 NameTemplate: "foo", 528 Format: "zip", 529 Files: []config.File{ 530 {Source: "[x-]"}, 531 }, 532 }, 533 }, 534 }, 535 testctx.WithCurrentTag("v0.0.1"), 536 ) 537 ctx.Git.CurrentTag = "v0.0.1" 538 ctx.Artifacts.Add(&artifact.Artifact{ 539 Goos: "darwin", 540 Goarch: "amd64", 541 Name: "mybin", 542 Path: filepath.Join("dist", "darwinamd64", "mybin"), 543 Type: artifact.Binary, 544 Extra: map[string]interface{}{ 545 artifact.ExtraBinary: "mybin", 546 artifact.ExtraID: "default", 547 }, 548 }) 549 require.EqualError(t, Pipe{}.Run(ctx), `failed to find files to archive: globbing failed for pattern [x-]: compile glob pattern: unexpected end of input`) 550 } 551 552 func TestRunPipeInvalidNameTemplate(t *testing.T) { 553 folder := testlib.Mktmp(t) 554 dist := filepath.Join(folder, "dist") 555 require.NoError(t, os.Mkdir(dist, 0o755)) 556 require.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0o755)) 557 f, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin")) 558 require.NoError(t, err) 559 require.NoError(t, f.Close()) 560 ctx := testctx.NewWithCfg( 561 config.Project{ 562 Dist: dist, 563 Archives: []config.Archive{ 564 { 565 Builds: []string{"default"}, 566 NameTemplate: "foo{{ .fff }", 567 Format: "zip", 568 }, 569 }, 570 }, 571 testctx.WithCurrentTag("v0.0.1"), 572 ) 573 ctx.Artifacts.Add(&artifact.Artifact{ 574 Goos: "darwin", 575 Goarch: "amd64", 576 Name: "mybin", 577 Path: filepath.Join("dist", "darwinamd64", "mybin"), 578 Type: artifact.Binary, 579 Extra: map[string]interface{}{ 580 artifact.ExtraBinary: "mybin", 581 artifact.ExtraID: "default", 582 }, 583 }) 584 testlib.RequireTemplateError(t, Pipe{}.Run(ctx)) 585 } 586 587 func TestRunPipeInvalidFilesNameTemplate(t *testing.T) { 588 folder := testlib.Mktmp(t) 589 dist := filepath.Join(folder, "dist") 590 require.NoError(t, os.Mkdir(dist, 0o755)) 591 require.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0o755)) 592 f, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin")) 593 require.NoError(t, err) 594 require.NoError(t, f.Close()) 595 ctx := testctx.NewWithCfg( 596 config.Project{ 597 Dist: dist, 598 Archives: []config.Archive{ 599 { 600 Builds: []string{"default"}, 601 NameTemplate: "foo", 602 Format: "zip", 603 Files: []config.File{ 604 {Source: "{{.asdsd}"}, 605 }, 606 }, 607 }, 608 }, 609 testctx.WithCurrentTag("v0.0.1"), 610 ) 611 ctx.Artifacts.Add(&artifact.Artifact{ 612 Goos: "darwin", 613 Goarch: "amd64", 614 Name: "mybin", 615 Path: filepath.Join("dist", "darwinamd64", "mybin"), 616 Type: artifact.Binary, 617 Extra: map[string]interface{}{ 618 artifact.ExtraBinary: "mybin", 619 artifact.ExtraID: "default", 620 }, 621 }) 622 testlib.RequireTemplateError(t, Pipe{}.Run(ctx)) 623 } 624 625 func TestRunPipeInvalidWrapInDirectoryTemplate(t *testing.T) { 626 folder := testlib.Mktmp(t) 627 dist := filepath.Join(folder, "dist") 628 require.NoError(t, os.Mkdir(dist, 0o755)) 629 require.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0o755)) 630 f, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin")) 631 require.NoError(t, err) 632 require.NoError(t, f.Close()) 633 ctx := testctx.NewWithCfg( 634 config.Project{ 635 Dist: dist, 636 Archives: []config.Archive{ 637 { 638 Builds: []string{"default"}, 639 NameTemplate: "foo", 640 WrapInDirectory: "foo{{ .fff }", 641 Format: "zip", 642 }, 643 }, 644 }, 645 testctx.WithCurrentTag("v0.0.1"), 646 ) 647 ctx.Artifacts.Add(&artifact.Artifact{ 648 Goos: "darwin", 649 Goarch: "amd64", 650 Name: "mybin", 651 Path: filepath.Join("dist", "darwinamd64", "mybin"), 652 Type: artifact.Binary, 653 Extra: map[string]interface{}{ 654 artifact.ExtraBinary: "mybin", 655 artifact.ExtraID: "default", 656 }, 657 }) 658 testlib.RequireTemplateError(t, Pipe{}.Run(ctx)) 659 } 660 661 func TestRunPipeWrap(t *testing.T) { 662 folder := testlib.Mktmp(t) 663 dist := filepath.Join(folder, "dist") 664 require.NoError(t, os.Mkdir(dist, 0o755)) 665 require.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0o755)) 666 f, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin")) 667 require.NoError(t, err) 668 require.NoError(t, f.Close()) 669 f, err = os.Create(filepath.Join(folder, "README.md")) 670 require.NoError(t, err) 671 require.NoError(t, f.Close()) 672 ctx := testctx.NewWithCfg( 673 config.Project{ 674 Dist: dist, 675 Archives: []config.Archive{ 676 { 677 Builds: []string{"default"}, 678 NameTemplate: "foo", 679 WrapInDirectory: "foo_{{ .Os }}", 680 Format: "tar.gz", 681 Files: []config.File{ 682 {Source: "README.*"}, 683 }, 684 }, 685 }, 686 }, 687 testctx.WithCurrentTag("v0.0.1"), 688 ) 689 ctx.Artifacts.Add(&artifact.Artifact{ 690 Goos: "darwin", 691 Goarch: "amd64", 692 Name: "mybin", 693 Path: filepath.Join("dist", "darwinamd64", "mybin"), 694 Type: artifact.Binary, 695 Extra: map[string]interface{}{ 696 artifact.ExtraBinary: "mybin", 697 artifact.ExtraID: "default", 698 }, 699 }) 700 require.NoError(t, Pipe{}.Run(ctx)) 701 702 archives := ctx.Artifacts.Filter(artifact.ByType(artifact.UploadableArchive)).List() 703 require.Len(t, archives, 1) 704 require.Equal(t, "foo_darwin", artifact.ExtraOr(*archives[0], artifact.ExtraWrappedIn, "")) 705 706 require.ElementsMatch( 707 t, 708 []string{"foo_darwin/README.md", "foo_darwin/mybin"}, 709 testlib.LsArchive(t, filepath.Join(dist, "foo.tar.gz"), "tar.gz"), 710 ) 711 } 712 713 func TestDefault(t *testing.T) { 714 ctx := testctx.NewWithCfg(config.Project{ 715 Archives: []config.Archive{}, 716 }) 717 require.NoError(t, Pipe{}.Default(ctx)) 718 require.NotEmpty(t, ctx.Config.Archives[0].NameTemplate) 719 require.Equal(t, "tar.gz", ctx.Config.Archives[0].Format) 720 require.NotEmpty(t, ctx.Config.Archives[0].Files) 721 } 722 723 func TestDefaultSet(t *testing.T) { 724 ctx := testctx.NewWithCfg(config.Project{ 725 Archives: []config.Archive{ 726 { 727 Builds: []string{"default"}, 728 NameTemplate: "foo", 729 Format: "zip", 730 Files: []config.File{ 731 {Source: "foo"}, 732 }, 733 }, 734 }, 735 }) 736 require.NoError(t, Pipe{}.Default(ctx)) 737 require.Equal(t, "foo", ctx.Config.Archives[0].NameTemplate) 738 require.Equal(t, "zip", ctx.Config.Archives[0].Format) 739 require.Equal(t, config.File{Source: "foo"}, ctx.Config.Archives[0].Files[0]) 740 } 741 742 func TestDefaultNoFiles(t *testing.T) { 743 ctx := testctx.NewWithCfg(config.Project{ 744 Archives: []config.Archive{ 745 { 746 Format: "tar.gz", 747 }, 748 }, 749 }) 750 require.NoError(t, Pipe{}.Default(ctx)) 751 require.Equal(t, defaultNameTemplate, ctx.Config.Archives[0].NameTemplate) 752 } 753 754 func TestDefaultFormatBinary(t *testing.T) { 755 ctx := testctx.NewWithCfg(config.Project{ 756 Archives: []config.Archive{ 757 { 758 Format: "binary", 759 }, 760 }, 761 }) 762 require.NoError(t, Pipe{}.Default(ctx)) 763 require.Equal(t, defaultBinaryNameTemplate, ctx.Config.Archives[0].NameTemplate) 764 } 765 766 func TestFormatFor(t *testing.T) { 767 ctx := testctx.NewWithCfg(config.Project{ 768 Archives: []config.Archive{ 769 { 770 Builds: []string{"default"}, 771 Format: "tar.gz", 772 FormatOverrides: []config.FormatOverride{ 773 { 774 Goos: "windows", 775 Format: "zip", 776 }, 777 }, 778 }, 779 }, 780 }) 781 require.Equal(t, "zip", packageFormat(ctx.Config.Archives[0], "windows")) 782 require.Equal(t, "tar.gz", packageFormat(ctx.Config.Archives[0], "linux")) 783 } 784 785 func TestBinaryOverride(t *testing.T) { 786 folder := testlib.Mktmp(t) 787 dist := filepath.Join(folder, "dist") 788 require.NoError(t, os.Mkdir(dist, 0o755)) 789 require.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0o755)) 790 require.NoError(t, os.Mkdir(filepath.Join(dist, "windowsamd64"), 0o755)) 791 f, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin")) 792 require.NoError(t, err) 793 require.NoError(t, f.Close()) 794 f, err = os.Create(filepath.Join(dist, "windowsamd64", "mybin.exe")) 795 require.NoError(t, err) 796 require.NoError(t, f.Close()) 797 f, err = os.Create(filepath.Join(folder, "README.md")) 798 require.NoError(t, err) 799 require.NoError(t, f.Close()) 800 for _, format := range []string{"tar.gz", "zip"} { 801 t.Run("Archive format "+format, func(t *testing.T) { 802 ctx := testctx.NewWithCfg( 803 config.Project{ 804 Dist: dist, 805 ProjectName: "foobar", 806 Archives: []config.Archive{ 807 { 808 Builds: []string{"default"}, 809 NameTemplate: defaultNameTemplate, 810 Files: []config.File{ 811 {Source: "README.*"}, 812 }, 813 FormatOverrides: []config.FormatOverride{ 814 { 815 Goos: "windows", 816 Format: "binary", 817 }, 818 }, 819 }, 820 }, 821 }, 822 testctx.WithCurrentTag("v0.0.1"), 823 ) 824 ctx.Artifacts.Add(&artifact.Artifact{ 825 Goos: "darwin", 826 Goarch: "amd64", 827 Name: "mybin", 828 Path: filepath.Join(dist, "darwinamd64", "mybin"), 829 Type: artifact.Binary, 830 Extra: map[string]interface{}{ 831 artifact.ExtraBinary: "mybin", 832 artifact.ExtraID: "default", 833 }, 834 }) 835 ctx.Artifacts.Add(&artifact.Artifact{ 836 Goos: "windows", 837 Goarch: "amd64", 838 Name: "mybin.exe", 839 Path: filepath.Join(dist, "windowsamd64", "mybin.exe"), 840 Type: artifact.Binary, 841 Extra: map[string]interface{}{ 842 artifact.ExtraBinary: "mybin", 843 artifact.ExtraExt: ".exe", 844 artifact.ExtraID: "default", 845 }, 846 }) 847 ctx.Version = "0.0.1" 848 ctx.Config.Archives[0].Format = format 849 850 require.NoError(t, Pipe{}.Run(ctx)) 851 archives := ctx.Artifacts.Filter(artifact.ByType(artifact.UploadableArchive)) 852 darwin := archives.Filter(artifact.ByGoos("darwin")).List()[0] 853 require.Equal(t, "foobar_0.0.1_darwin_amd64."+format, darwin.Name) 854 require.Equal(t, format, darwin.Format()) 855 require.Empty(t, artifact.ExtraOr(*darwin, artifact.ExtraWrappedIn, "")) 856 857 archives = ctx.Artifacts.Filter(artifact.ByType(artifact.UploadableBinary)) 858 windows := archives.Filter(artifact.ByGoos("windows")).List()[0] 859 require.Equal(t, "foobar_0.0.1_windows_amd64.exe", windows.Name) 860 require.Empty(t, artifact.ExtraOr(*windows, artifact.ExtraWrappedIn, "")) 861 require.Equal(t, "mybin.exe", artifact.ExtraOr(*windows, artifact.ExtraBinary, "")) 862 }) 863 } 864 } 865 866 func TestRunPipeSameArchiveFilename(t *testing.T) { 867 folder := testlib.Mktmp(t) 868 dist := filepath.Join(folder, "dist") 869 require.NoError(t, os.Mkdir(dist, 0o755)) 870 require.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0o755)) 871 require.NoError(t, os.Mkdir(filepath.Join(dist, "windowsamd64"), 0o755)) 872 f, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin")) 873 require.NoError(t, err) 874 require.NoError(t, f.Close()) 875 f, err = os.Create(filepath.Join(dist, "windowsamd64", "mybin.exe")) 876 require.NoError(t, err) 877 require.NoError(t, f.Close()) 878 ctx := testctx.NewWithCfg( 879 config.Project{ 880 Dist: dist, 881 ProjectName: "foobar", 882 Archives: []config.Archive{ 883 { 884 Builds: []string{"default"}, 885 NameTemplate: "same-filename", 886 Files: []config.File{ 887 {Source: "README.*"}, 888 {Source: "./foo/**/*"}, 889 }, 890 Format: "tar.gz", 891 }, 892 }, 893 }, 894 ) 895 ctx.Artifacts.Add(&artifact.Artifact{ 896 Goos: "darwin", 897 Goarch: "amd64", 898 Name: "mybin", 899 Path: filepath.Join(dist, "darwinamd64", "mybin"), 900 Type: artifact.Binary, 901 Extra: map[string]interface{}{ 902 artifact.ExtraBinary: "mybin", 903 artifact.ExtraID: "default", 904 }, 905 }) 906 ctx.Artifacts.Add(&artifact.Artifact{ 907 Goos: "windows", 908 Goarch: "amd64", 909 Name: "mybin.exe", 910 Path: filepath.Join(dist, "windowsamd64", "mybin.exe"), 911 Type: artifact.Binary, 912 Extra: map[string]interface{}{ 913 artifact.ExtraBinary: "mybin", 914 artifact.ExtraExt: ".exe", 915 artifact.ExtraID: "default", 916 }, 917 }) 918 ctx.Version = "0.0.1" 919 ctx.Git.CurrentTag = "v0.0.1" 920 err = Pipe{}.Run(ctx) 921 require.Error(t, err) 922 require.Contains(t, err.Error(), "same-filename.tar.gz already exists. Check your archive name template") 923 } 924 925 func TestDuplicateFilesInsideArchive(t *testing.T) { 926 folder := t.TempDir() 927 928 f, err := os.CreateTemp(folder, "") 929 require.NoError(t, err) 930 t.Cleanup(func() { 931 require.NoError(t, f.Close()) 932 }) 933 934 ff, err := os.CreateTemp(folder, "") 935 require.NoError(t, err) 936 require.NoError(t, ff.Close()) 937 a, err := archive.New(f, "tar.gz") 938 require.NoError(t, err) 939 a = NewEnhancedArchive(a, "") 940 t.Cleanup(func() { 941 require.NoError(t, a.Close()) 942 }) 943 944 require.NoError(t, a.Add(config.File{ 945 Source: ff.Name(), 946 Destination: "foo", 947 })) 948 require.ErrorIs(t, a.Add(config.File{ 949 Source: ff.Name(), 950 Destination: "foo", 951 }), fs.ErrExist) 952 } 953 954 func TestWrapInDirectory(t *testing.T) { 955 t.Run("false", func(t *testing.T) { 956 require.Equal(t, "", wrapFolder(config.Archive{ 957 WrapInDirectory: "false", 958 })) 959 }) 960 t.Run("true", func(t *testing.T) { 961 require.Equal(t, "foo", wrapFolder(config.Archive{ 962 WrapInDirectory: "true", 963 NameTemplate: "foo", 964 })) 965 }) 966 t.Run("custom", func(t *testing.T) { 967 require.Equal(t, "foobar", wrapFolder(config.Archive{ 968 WrapInDirectory: "foobar", 969 })) 970 }) 971 } 972 973 func TestSeveralArchivesWithTheSameID(t *testing.T) { 974 ctx := testctx.NewWithCfg(config.Project{ 975 Archives: []config.Archive{ 976 { 977 ID: "a", 978 }, 979 { 980 ID: "a", 981 }, 982 }, 983 }) 984 require.EqualError(t, Pipe{}.Default(ctx), "found 2 archives with the ID 'a', please fix your config") 985 } 986 987 func TestArchive_globbing(t *testing.T) { 988 assertGlob := func(t *testing.T, files []config.File, expected []string) { 989 t.Helper() 990 bin, err := os.CreateTemp(t.TempDir(), "binary") 991 require.NoError(t, err) 992 dist := t.TempDir() 993 ctx := testctx.NewWithCfg(config.Project{ 994 Dist: dist, 995 Archives: []config.Archive{ 996 { 997 Builds: []string{"default"}, 998 Format: "tar.gz", 999 NameTemplate: "foo", 1000 Files: files, 1001 }, 1002 }, 1003 }) 1004 1005 ctx.Artifacts.Add(&artifact.Artifact{ 1006 Goos: "darwin", 1007 Goarch: "amd64", 1008 Name: "foobin", 1009 Path: bin.Name(), 1010 Type: artifact.Binary, 1011 Extra: map[string]interface{}{ 1012 artifact.ExtraID: "default", 1013 }, 1014 }) 1015 1016 require.NoError(t, Pipe{}.Run(ctx)) 1017 require.Equal(t, append(expected, "foobin"), testlib.LsArchive(t, filepath.Join(dist, "foo.tar.gz"), "tar.gz")) 1018 } 1019 1020 t.Run("exact src file", func(t *testing.T) { 1021 assertGlob(t, []config.File{{Source: "testdata/a/a.txt"}}, []string{"testdata/a/a.txt"}) 1022 }) 1023 1024 t.Run("exact src file with dst", func(t *testing.T) { 1025 assertGlob(t, []config.File{ 1026 { 1027 Source: "testdata/a/a.txt", 1028 Destination: "foo/", 1029 }, 1030 }, []string{"foo"}) 1031 }) 1032 1033 t.Run("glob src", func(t *testing.T) { 1034 assertGlob(t, []config.File{ 1035 {Source: "testdata/**/*.txt"}, 1036 }, []string{ 1037 "testdata/a/a.txt", 1038 "testdata/a/b/a.txt", 1039 "testdata/a/b/c/d.txt", 1040 }) 1041 }) 1042 1043 t.Run("glob src with dst", func(t *testing.T) { 1044 assertGlob(t, []config.File{ 1045 { 1046 Source: "testdata/**/*.txt", 1047 Destination: "var/yada", 1048 }, 1049 }, []string{ 1050 "var/yada/a.txt", 1051 "var/yada/b/a.txt", 1052 "var/yada/b/c/d.txt", 1053 }) 1054 }) 1055 1056 t.Run("glob src with dst stripping parent", func(t *testing.T) { 1057 assertGlob(t, []config.File{ 1058 { 1059 Source: "testdata/**/*.txt", 1060 Destination: "var/yada", 1061 StripParent: true, 1062 }, 1063 }, []string{ 1064 "var/yada/a.txt", 1065 "var/yada/d.txt", 1066 }) 1067 }) 1068 } 1069 1070 func TestInvalidFormat(t *testing.T) { 1071 ctx := testctx.NewWithCfg(config.Project{ 1072 Dist: t.TempDir(), 1073 Archives: []config.Archive{ 1074 { 1075 ID: "foo", 1076 NameTemplate: "foo", 1077 Meta: true, 1078 Format: "7z", 1079 }, 1080 }, 1081 }) 1082 require.EqualError(t, Pipe{}.Run(ctx), "invalid archive format: 7z") 1083 } 1084 1085 func TestIssue3803(t *testing.T) { 1086 ctx := testctx.NewWithCfg(config.Project{ 1087 Dist: t.TempDir(), 1088 Archives: []config.Archive{ 1089 { 1090 ID: "foo", 1091 NameTemplate: "foo", 1092 Meta: true, 1093 Format: "zip", 1094 Files: []config.File{ 1095 {Source: "./testdata/a/a.txt"}, 1096 }, 1097 }, 1098 { 1099 ID: "foobar", 1100 NameTemplate: "foobar", 1101 Meta: true, 1102 Format: "zip", 1103 Files: []config.File{ 1104 {Source: "./testdata/a/b/a.txt"}, 1105 }, 1106 }, 1107 }, 1108 }) 1109 require.NoError(t, Pipe{}.Run(ctx)) 1110 archives := ctx.Artifacts.List() 1111 require.Len(t, archives, 2) 1112 } 1113 1114 func TestExtraFormatWhenOverride(t *testing.T) { 1115 ctx := testctx.NewWithCfg(config.Project{ 1116 Dist: t.TempDir(), 1117 Archives: []config.Archive{ 1118 { 1119 ID: "foo", 1120 NameTemplate: "foo", 1121 Format: "tar.gz", 1122 FormatOverrides: []config.FormatOverride{{ 1123 Goos: "windows", 1124 Format: "zip", 1125 }}, 1126 Files: []config.File{ 1127 {Source: "./testdata/a/a.txt"}, 1128 }, 1129 }, 1130 }, 1131 }) 1132 windowsBuild := &artifact.Artifact{ 1133 Goos: "windows", 1134 Goarch: "amd64", 1135 Goamd64: "v1", 1136 Name: "bin/mybin.exe", 1137 Path: filepath.Join(ctx.Config.Dist, "windowsamd64", "bin", "mybin.exe"), 1138 Type: artifact.Binary, 1139 Extra: map[string]interface{}{ 1140 artifact.ExtraBinary: "mybin", 1141 artifact.ExtraExt: ".exe", 1142 artifact.ExtraID: "default", 1143 }, 1144 } 1145 require.NoError(t, os.MkdirAll(filepath.Dir(windowsBuild.Path), 0o755)) 1146 f, err := os.Create(windowsBuild.Path) 1147 require.NoError(t, err) 1148 require.NoError(t, f.Close()) 1149 ctx.Artifacts.Add(windowsBuild) 1150 require.NoError(t, Pipe{}.Run(ctx)) 1151 archives := ctx.Artifacts.Filter(artifact.ByFormats("zip")).List() 1152 require.Len(t, archives, 1) 1153 }