github.com/goreleaser/nfpm/v2@v2.44.0/ipk/ipk_test.go (about) 1 package ipk 2 3 import ( 4 "archive/tar" 5 "bytes" 6 "errors" 7 "flag" 8 "fmt" 9 "io" 10 "os" 11 "path" 12 "path/filepath" 13 "strings" 14 "testing" 15 16 "github.com/goreleaser/nfpm/v2" 17 "github.com/goreleaser/nfpm/v2/files" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/require" 20 ) 21 22 // nolint: gochecknoglobals 23 var update = flag.Bool("update", false, "update .golden files") 24 25 func exampleInfo() *nfpm.Info { 26 return nfpm.WithDefaults(&nfpm.Info{ 27 Name: "foo", 28 Arch: "amd64", 29 Description: "Foo does things", 30 Priority: "extra", 31 Maintainer: "Carlos A Becker <pkg@carlosbecker.com>", 32 Version: "v1.0.0", 33 Section: "default", 34 Homepage: "http://carlosbecker.com", 35 Vendor: "nope", 36 Overridables: nfpm.Overridables{ 37 Depends: []string{ 38 "bash", 39 }, 40 Recommends: []string{ 41 "git", 42 }, 43 Suggests: []string{ 44 "bash", 45 }, 46 Replaces: []string{ 47 "svn", 48 }, 49 Provides: []string{ 50 "bzr", 51 }, 52 Conflicts: []string{ 53 "zsh", 54 }, 55 Contents: []*files.Content{ 56 { 57 Source: "../testdata/fake", 58 Destination: "/usr/bin/fake", 59 }, 60 { 61 Source: "../testdata/whatever.conf", 62 Destination: "/usr/share/doc/fake/fake.txt", 63 }, 64 { 65 Source: "../testdata/whatever.conf", 66 Destination: "/etc/fake/fake.conf", 67 Type: files.TypeConfig, 68 }, 69 { 70 Source: "../testdata/whatever.conf", 71 Destination: "/etc/fake/fake2.conf", 72 Type: files.TypeConfigNoReplace, 73 }, 74 { 75 Source: "../testdata/whatever.conf", 76 Destination: "/etc/fake/fake3.conf", 77 Type: files.TypeConfigNoReplace, 78 }, 79 { 80 Destination: "/var/log/whatever", 81 Type: files.TypeDir, 82 }, 83 { 84 Destination: "/usr/share/whatever", 85 Type: files.TypeDir, 86 }, 87 }, 88 IPK: nfpm.IPK{ 89 Predepends: []string{"less"}, 90 }, 91 }, 92 }) 93 } 94 95 func TestConventionalExtension(t *testing.T) { 96 require.Equal(t, ".ipk", Default.ConventionalExtension()) 97 } 98 99 func TestIPK(t *testing.T) { 100 for _, arch := range []string{"386", "amd64"} { 101 arch := arch 102 t.Run(arch, func(t *testing.T) { 103 info := exampleInfo() 104 info.Arch = arch 105 err := Default.Package(info, io.Discard) 106 require.NoError(t, err) 107 }) 108 } 109 } 110 111 func TestIPKPlatform(t *testing.T) { 112 f, err := os.CreateTemp(t.TempDir(), "test*.deb") 113 require.NoError(t, err) 114 t.Cleanup(func() { require.NoError(t, f.Close()) }) 115 info := exampleInfo() 116 info.Platform = "darwin" 117 err = Default.Package(info, f) 118 require.NoError(t, err) 119 } 120 121 func extractIPKArchitecture(deb *bytes.Buffer) string { 122 for _, s := range strings.Split(deb.String(), "\n") { 123 if strings.Contains(s, "Architecture: ") { 124 return strings.TrimPrefix(s, "Architecture: ") 125 } 126 } 127 return "" 128 } 129 130 func splitIPKArchitecture(deb *bytes.Buffer) (string, string) { 131 a := extractIPKArchitecture(deb) 132 if strings.Contains(a, "-") { 133 f := strings.Split(a, "-") 134 return f[0], f[1] 135 } 136 return "linux", a 137 } 138 139 func TestIPKOS(t *testing.T) { 140 info := exampleInfo() 141 var buf bytes.Buffer 142 err := renderControl(&buf, controlData{info, 0}) 143 require.NoError(t, err) 144 o, _ := splitIPKArchitecture(&buf) 145 require.Equal(t, "linux", o) 146 } 147 148 func TestIPKArch(t *testing.T) { 149 info := exampleInfo() 150 var buf bytes.Buffer 151 err := renderControl(&buf, controlData{info, 0}) 152 require.NoError(t, err) 153 _, a := splitIPKArchitecture(&buf) 154 require.Equal(t, "amd64", a) 155 } 156 157 func extractIPKVersion(deb *bytes.Buffer) string { 158 for _, s := range strings.Split(deb.String(), "\n") { 159 if strings.Contains(s, "Version: ") { 160 return strings.TrimPrefix(s, "Version: ") 161 } 162 } 163 return "" 164 } 165 166 func TestIPKVersionWithDash(t *testing.T) { 167 info := exampleInfo() 168 info.Version = "1.0.0-beta" 169 err := Default.Package(info, io.Discard) 170 require.NoError(t, err) 171 } 172 173 func TestIPKVersion(t *testing.T) { 174 info := exampleInfo() 175 info.Version = "1.0.0" //nolint:golint,goconst 176 var buf bytes.Buffer 177 err := renderControl(&buf, controlData{info, 0}) 178 require.NoError(t, err) 179 v := extractIPKVersion(&buf) 180 require.Equal(t, "1.0.0", v) 181 } 182 183 func TestIPKVersionWithRelease(t *testing.T) { 184 info := exampleInfo() 185 info.Version = "1.0.0" //nolint:golint,goconst 186 info.Release = "1" 187 var buf bytes.Buffer 188 err := renderControl(&buf, controlData{info, 0}) 189 require.NoError(t, err) 190 v := extractIPKVersion(&buf) 191 require.Equal(t, "1.0.0-1", v) 192 } 193 194 func TestIPKVersionWithPrerelease(t *testing.T) { 195 var buf bytes.Buffer 196 197 info := exampleInfo() 198 info.Version = "1.0.0" //nolint:golint,goconst 199 info.Prerelease = "1" 200 err := renderControl(&buf, controlData{info, 0}) 201 require.NoError(t, err) 202 v := extractIPKVersion(&buf) 203 require.Equal(t, "1.0.0~1", v) 204 } 205 206 func TestIPKVersionWithReleaseAndPrerelease(t *testing.T) { 207 var buf bytes.Buffer 208 209 info := exampleInfo() 210 info.Version = "1.0.0" //nolint:golint,goconst 211 info.Release = "2" 212 info.Prerelease = "rc1" //nolint:golint,goconst 213 err := renderControl(&buf, controlData{info, 0}) 214 require.NoError(t, err) 215 v := extractIPKVersion(&buf) 216 require.Equal(t, "1.0.0~rc1-2", v) 217 } 218 219 func TestIPKVersionWithVersionMetadata(t *testing.T) { 220 var buf bytes.Buffer 221 222 info := exampleInfo() 223 info.Version = "1.0.0+meta" //nolint:golint,goconst 224 info.VersionMetadata = "" 225 err := renderControl(&buf, controlData{info, 0}) 226 require.NoError(t, err) 227 v := extractIPKVersion(&buf) 228 require.Equal(t, "1.0.0+meta", v) 229 230 buf.Reset() 231 232 info.Version = "1.0.0" //nolint:golint,goconst 233 info.VersionMetadata = "meta" 234 err = renderControl(&buf, controlData{info, 0}) 235 require.NoError(t, err) 236 v = extractIPKVersion(&buf) 237 require.Equal(t, "1.0.0+meta", v) 238 239 buf.Reset() 240 241 info.Version = "1.0.0+foo" //nolint:golint,goconst 242 info.Prerelease = "alpha" 243 info.VersionMetadata = "meta" 244 err = renderControl(&buf, controlData{nfpm.WithDefaults(info), 0}) 245 require.NoError(t, err) 246 v = extractIPKVersion(&buf) 247 require.Equal(t, "1.0.0~alpha+meta", v) 248 } 249 250 func TestControl(t *testing.T) { 251 var w bytes.Buffer 252 require.NoError(t, renderControl(&w, controlData{ 253 Info: exampleInfo(), 254 InstalledSize: 10, 255 })) 256 golden := "testdata/control.golden" 257 if *update { 258 require.NoError(t, os.WriteFile(golden, w.Bytes(), 0o600)) 259 } 260 bts, err := os.ReadFile(golden) //nolint:gosec 261 require.NoError(t, err) 262 require.Equal(t, string(bts), w.String()) 263 } 264 265 func TestNoJoinsControl(t *testing.T) { 266 var w bytes.Buffer 267 require.NoError(t, renderControl(&w, controlData{ 268 Info: nfpm.WithDefaults(&nfpm.Info{ 269 Name: "foo", 270 Arch: "amd64", 271 Description: "Foo does things", 272 Priority: "extra", 273 Maintainer: "Carlos A Becker <pkg@carlosbecker.com>", 274 Version: "v1.0.0", 275 Section: "default", 276 Homepage: "http://carlosbecker.com", 277 Vendor: "nope", 278 Overridables: nfpm.Overridables{ 279 Depends: []string{}, 280 Recommends: []string{}, 281 Suggests: []string{}, 282 Replaces: []string{}, 283 Provides: []string{}, 284 Conflicts: []string{}, 285 Contents: []*files.Content{}, 286 }, 287 }), 288 InstalledSize: 10, 289 })) 290 golden := "testdata/control2.golden" 291 if *update { 292 require.NoError(t, os.WriteFile(golden, w.Bytes(), 0o600)) 293 } 294 bts, err := os.ReadFile(golden) //nolint:gosec 295 require.NoError(t, err) 296 require.Equal(t, string(bts), w.String()) 297 } 298 299 func TestVersionControl(t *testing.T) { 300 var w bytes.Buffer 301 require.NoError(t, renderControl(&w, controlData{ 302 Info: nfpm.WithDefaults(&nfpm.Info{ 303 Name: "foo", 304 Arch: "amd64", 305 Description: "Foo does things", 306 Priority: "extra", 307 Maintainer: "Carlos A Becker <pkg@carlosbecker.com>", 308 Version: "v1.0.0-beta+meta", 309 Release: "2", 310 Section: "default", 311 Homepage: "http://carlosbecker.com", 312 Vendor: "nope", 313 Overridables: nfpm.Overridables{ 314 Depends: []string{}, 315 Recommends: []string{}, 316 Suggests: []string{}, 317 Replaces: []string{}, 318 Provides: []string{}, 319 Conflicts: []string{}, 320 Contents: []*files.Content{}, 321 }, 322 }), 323 InstalledSize: 10, 324 })) 325 golden := "testdata/control4.golden" 326 if *update { 327 require.NoError(t, os.WriteFile(golden, w.Bytes(), 0o600)) 328 } 329 bts, err := os.ReadFile(golden) //nolint:gosec 330 require.NoError(t, err) 331 require.Equal(t, string(bts), w.String()) 332 } 333 334 func TestIPKFileDoesNotExist(t *testing.T) { 335 abs, err := filepath.Abs("../testdata/whatever.confzzz") 336 require.NoError(t, err) 337 err = Default.Package( 338 nfpm.WithDefaults(&nfpm.Info{ 339 Name: "foo", 340 Arch: "amd64", 341 Description: "Foo does things", 342 Priority: "extra", 343 Maintainer: "Carlos A Becker <pkg@carlosbecker.com>", 344 Version: "1.0.0", 345 Section: "default", 346 Homepage: "http://carlosbecker.com", 347 Vendor: "nope", 348 Overridables: nfpm.Overridables{ 349 Depends: []string{ 350 "bash", 351 }, 352 Contents: []*files.Content{ 353 { 354 Source: "../testdata/fake", 355 Destination: "/usr/bin/fake", 356 }, 357 { 358 Source: "../testdata/whatever.confzzz", 359 Destination: "/etc/fake/fake.conf", 360 Type: files.TypeConfig, 361 }, 362 }, 363 }, 364 }), 365 io.Discard, 366 ) 367 require.EqualError(t, err, fmt.Sprintf("matching \"%s\": file does not exist", filepath.ToSlash(abs))) 368 } 369 370 func TestIPKNoFiles(t *testing.T) { 371 err := Default.Package( 372 nfpm.WithDefaults(&nfpm.Info{ 373 Name: "foo", 374 Arch: "amd64", 375 Description: "Foo does things", 376 Priority: "extra", 377 Maintainer: "Carlos A Becker <pkg@carlosbecker.com>", 378 Version: "1.0.0", 379 Section: "default", 380 Homepage: "http://carlosbecker.com", 381 Vendor: "nope", 382 Overridables: nfpm.Overridables{ 383 Depends: []string{ 384 "bash", 385 }, 386 }, 387 }), 388 io.Discard, 389 ) 390 require.NoError(t, err) 391 } 392 393 func TestIPKNoInfo(t *testing.T) { 394 err := Default.Package(nfpm.WithDefaults(&nfpm.Info{}), io.Discard) 395 require.Error(t, err) 396 } 397 398 func TestConffiles(t *testing.T) { 399 info := nfpm.WithDefaults(&nfpm.Info{ 400 Name: "minimal", 401 Arch: "arm64", 402 Description: "Minimal does nothing", 403 Priority: "extra", 404 Version: "1.0.0", 405 Section: "default", 406 Maintainer: "maintainer", 407 Overridables: nfpm.Overridables{ 408 Contents: []*files.Content{ 409 { 410 Source: "../testdata/fake", 411 Destination: "/etc/fake", 412 Type: files.TypeConfig, 413 }, 414 }, 415 }, 416 }) 417 err := nfpm.PrepareForPackager(info, packagerName) 418 require.NoError(t, err) 419 out := conffiles(info) 420 require.Equal(t, "/etc/fake\n", string(out), "should have a trailing empty line") 421 } 422 423 func TestMinimalFields(t *testing.T) { 424 var w bytes.Buffer 425 require.NoError(t, renderControl(&w, controlData{ 426 Info: nfpm.WithDefaults(&nfpm.Info{ 427 Name: "minimal", 428 Arch: "arm64", 429 Description: "Minimal does nothing", 430 Priority: "extra", 431 Version: "1.0.0", 432 Maintainer: "maintainer", 433 }), 434 })) 435 golden := "testdata/minimal.golden" 436 if *update { 437 require.NoError(t, os.WriteFile(golden, w.Bytes(), 0o600)) 438 } 439 bts, err := os.ReadFile(golden) //nolint:gosec 440 require.NoError(t, err) 441 require.Equal(t, string(bts), w.String()) 442 } 443 444 func TestIPKEpoch(t *testing.T) { 445 var w bytes.Buffer 446 require.NoError(t, renderControl(&w, controlData{ 447 Info: nfpm.WithDefaults(&nfpm.Info{ 448 Name: "withepoch", 449 Arch: "arm64", 450 Description: "Has an epoch added to it's version", 451 Priority: "extra", 452 Epoch: "2", 453 Version: "1.0.0", 454 Section: "default", 455 }), 456 })) 457 golden := "testdata/withepoch.golden" 458 if *update { 459 require.NoError(t, os.WriteFile(golden, w.Bytes(), 0o600)) 460 } 461 bts, err := os.ReadFile(golden) //nolint:gosec 462 require.NoError(t, err) 463 require.Equal(t, string(bts), w.String()) 464 } 465 466 func TestMultilineFields(t *testing.T) { 467 var w bytes.Buffer 468 require.NoError(t, renderControl(&w, controlData{ 469 Info: nfpm.WithDefaults(&nfpm.Info{ 470 Name: "multiline", 471 Arch: "riscv64", 472 Description: "This field is a\nmultiline field\n\nthat should work.", 473 Priority: "extra", 474 Version: "1.0.0", 475 Maintainer: "someone", 476 }), 477 })) 478 golden := "testdata/multiline.golden" 479 if *update { 480 require.NoError(t, os.WriteFile(golden, w.Bytes(), 0o600)) 481 } 482 bts, err := os.ReadFile(golden) //nolint:gosec 483 require.NoError(t, err) 484 require.Equal(t, string(bts), w.String()) 485 } 486 487 func TestIPKConventionalFileName(t *testing.T) { 488 info := &nfpm.Info{ 489 Name: "testpkg", 490 Arch: "all", 491 Maintainer: "maintainer", 492 } 493 494 testCases := []struct { 495 Version string 496 Release string 497 Prerelease string 498 Expected string 499 Metadata string 500 }{ 501 { 502 Version: "1.2.3", Release: "", Prerelease: "", Metadata: "", 503 Expected: fmt.Sprintf("%s_1.2.3_%s.ipk", info.Name, info.Arch), 504 }, 505 { 506 Version: "1.2.3", Release: "4", Prerelease: "", Metadata: "", 507 Expected: fmt.Sprintf("%s_1.2.3-4_%s.ipk", info.Name, info.Arch), 508 }, 509 { 510 Version: "1.2.3", Release: "4", Prerelease: "5", Metadata: "", 511 Expected: fmt.Sprintf("%s_1.2.3~5-4_%s.ipk", info.Name, info.Arch), 512 }, 513 { 514 Version: "1.2.3", Release: "", Prerelease: "5", Metadata: "", 515 Expected: fmt.Sprintf("%s_1.2.3~5_%s.ipk", info.Name, info.Arch), 516 }, 517 { 518 Version: "1.2.3", Release: "1", Prerelease: "5", Metadata: "git", 519 Expected: fmt.Sprintf("%s_1.2.3~5+git-1_%s.ipk", info.Name, info.Arch), 520 }, 521 } 522 523 for _, testCase := range testCases { 524 info.Version = testCase.Version 525 info.Release = testCase.Release 526 info.Prerelease = testCase.Prerelease 527 info.VersionMetadata = testCase.Metadata 528 529 require.Equal(t, testCase.Expected, Default.ConventionalFileName(info)) 530 } 531 } 532 533 func TestSymlink(t *testing.T) { 534 var ( 535 configFilePath = "/usr/share/doc/fake/fake.txt" 536 symlink = "/path/to/symlink" 537 symlinkTarget = configFilePath 538 ) 539 540 info := &nfpm.Info{ 541 Name: "symlink-in-files", 542 Arch: "amd64", 543 Description: "This package's config references a file via symlink.", 544 Version: "1.0.0", 545 Maintainer: "maintainer", 546 Overridables: nfpm.Overridables{ 547 Contents: []*files.Content{ 548 { 549 Source: "../testdata/whatever.conf", 550 Destination: configFilePath, 551 }, 552 { 553 Source: symlinkTarget, 554 Destination: symlink, 555 Type: files.TypeSymlink, 556 }, 557 }, 558 }, 559 } 560 err := nfpm.PrepareForPackager(info, packagerName) 561 require.NoError(t, err) 562 563 var buf bytes.Buffer 564 tarball := tar.NewWriter(&buf) 565 _, err = populateDataTar(info, tarball) 566 require.NoError(t, err) 567 require.NoError(t, tarball.Close()) 568 569 packagedSymlinkHeader := extractFileHeaderFromTar(t, buf.Bytes(), symlink) 570 571 require.Equal(t, symlink, path.Join("/", packagedSymlinkHeader.Name)) // nolint:gosec 572 require.Equal(t, uint8(tar.TypeSymlink), packagedSymlinkHeader.Typeflag) 573 require.Equal(t, symlinkTarget, packagedSymlinkHeader.Linkname) 574 } 575 576 func TestEnsureRelativePrefixInTarballs(t *testing.T) { 577 info := exampleInfo() 578 info.Contents = []*files.Content{ 579 { 580 Source: "/symlink/to/fake.txt", 581 Destination: "/usr/share/doc/fake/fake.txt", 582 Type: files.TypeSymlink, 583 }, 584 } 585 info.Changelog = "../testdata/changelog.yaml" 586 err := nfpm.PrepareForPackager(info, packagerName) 587 require.NoError(t, err) 588 589 var dataBuf bytes.Buffer 590 dataTarball := tar.NewWriter(&dataBuf) 591 instSize, err := populateDataTar(info, dataTarball) 592 require.NoError(t, err) 593 require.NoError(t, dataTarball.Close()) 594 testRelativePathPrefixInTar(t, dataBuf.Bytes()) 595 596 var controlBuf bytes.Buffer 597 controlTarball := tar.NewWriter(&controlBuf) 598 err = populateControlTar(info, controlTarball, instSize) 599 require.NoError(t, err) 600 require.NoError(t, controlTarball.Close()) 601 testRelativePathPrefixInTar(t, controlBuf.Bytes()) 602 } 603 604 func TestDirectories(t *testing.T) { 605 info := exampleInfo() 606 info.Contents = []*files.Content{ 607 { 608 Source: "../testdata/whatever.conf", 609 Destination: "/etc/foo/file", 610 }, 611 { 612 Source: "../testdata/whatever.conf", 613 Destination: "/etc/bar/file", 614 }, 615 { 616 Destination: "/etc/bar", 617 Type: files.TypeDir, 618 FileInfo: &files.ContentFileInfo{ 619 Owner: "test", 620 Mode: 0o700, 621 }, 622 }, 623 { 624 Destination: "/etc/baz", 625 Type: files.TypeDir, 626 }, 627 { 628 Destination: "/usr/lib/something/somethingelse", 629 Type: files.TypeDir, 630 }, 631 } 632 633 require.NoError(t, nfpm.PrepareForPackager(info, packagerName)) 634 635 var dataBuf bytes.Buffer 636 tarball := tar.NewWriter(&dataBuf) 637 _, err := populateDataTar(info, tarball) 638 require.NoError(t, err) 639 require.NoError(t, tarball.Close()) 640 641 dataTarball := dataBuf.Bytes() 642 643 require.Equal(t, []string{ 644 "./etc/", 645 "./etc/bar/", 646 "./etc/bar/file", 647 "./etc/baz/", 648 "./etc/foo/", 649 "./etc/foo/file", 650 "./usr/", 651 "./usr/lib/", 652 "./usr/lib/something/", 653 "./usr/lib/something/somethingelse/", 654 }, getTree(t, dataBuf.Bytes())) 655 656 // for ipk all implicit or explicit directories are created in the tarball 657 h := extractFileHeaderFromTar(t, dataTarball, "/etc") 658 require.Equal(t, h.Typeflag, byte(tar.TypeDir)) 659 h = extractFileHeaderFromTar(t, dataTarball, "/etc/foo") 660 require.Equal(t, h.Typeflag, byte(tar.TypeDir)) 661 h = extractFileHeaderFromTar(t, dataTarball, "/etc/bar") 662 require.Equal(t, h.Typeflag, byte(tar.TypeDir)) 663 require.Equal(t, int64(0o700), h.Mode) 664 require.Equal(t, "test", h.Uname) 665 h = extractFileHeaderFromTar(t, dataTarball, "/etc/baz") 666 require.Equal(t, h.Typeflag, byte(tar.TypeDir)) 667 668 h = extractFileHeaderFromTar(t, dataTarball, "/usr") 669 require.Equal(t, h.Typeflag, byte(tar.TypeDir)) 670 h = extractFileHeaderFromTar(t, dataTarball, "/usr/lib") 671 require.Equal(t, h.Typeflag, byte(tar.TypeDir)) 672 h = extractFileHeaderFromTar(t, dataTarball, "/usr/lib/something") 673 require.Equal(t, h.Typeflag, byte(tar.TypeDir)) 674 h = extractFileHeaderFromTar(t, dataTarball, "/usr/lib/something/somethingelse") 675 require.Equal(t, h.Typeflag, byte(tar.TypeDir)) 676 } 677 678 func TestNoDuplicateContents(t *testing.T) { 679 info := exampleInfo() 680 info.Contents = []*files.Content{ 681 { 682 Source: "../testdata/whatever.conf", 683 Destination: "/etc/foo/file", 684 }, 685 { 686 Source: "../testdata/whatever.conf", 687 Destination: "/etc/foo/file2", 688 }, 689 { 690 Destination: "/etc/foo", 691 Type: files.TypeDir, 692 }, 693 { 694 Destination: "/etc/baz", 695 Type: files.TypeDir, 696 }, 697 } 698 699 require.NoError(t, nfpm.PrepareForPackager(info, packagerName)) 700 701 var dataBuf bytes.Buffer 702 tarball := tar.NewWriter(&dataBuf) 703 _, err := populateDataTar(info, tarball) 704 require.NoError(t, err) 705 require.NoError(t, tarball.Close()) 706 707 exists := map[string]bool{} 708 709 tr := tar.NewReader(bytes.NewReader(dataBuf.Bytes())) 710 for { 711 hdr, err := tr.Next() 712 if errors.Is(err, io.EOF) { 713 break // End of archive 714 } 715 require.NoError(t, err) 716 717 _, ok := exists[hdr.Name] 718 if ok { 719 t.Fatalf("%s exists more than once in tarball", hdr.Name) 720 } 721 722 exists[hdr.Name] = true 723 } 724 } 725 726 func testRelativePathPrefixInTar(tb testing.TB, tarFile []byte) { 727 tb.Helper() 728 729 tr := tar.NewReader(bytes.NewReader(tarFile)) 730 for { 731 hdr, err := tr.Next() 732 if errors.Is(err, io.EOF) { 733 break // End of archive 734 } 735 require.NoError(tb, err) 736 require.True(tb, strings.HasPrefix(hdr.Name, "./"), "%s does not start with './'", hdr.Name) 737 } 738 } 739 740 func TestDisableGlobbing(t *testing.T) { 741 info := exampleInfo() 742 info.DisableGlobbing = true 743 info.Contents = []*files.Content{ 744 { 745 Source: "../testdata/{file}[", 746 Destination: "/test/{file}[", 747 }, 748 } 749 require.NoError(t, nfpm.PrepareForPackager(info, packagerName)) 750 751 var dataBuf bytes.Buffer 752 tarball := tar.NewWriter(&dataBuf) 753 _, err := populateDataTar(info, tarball) 754 require.NoError(t, err) 755 require.NoError(t, tarball.Close()) 756 757 expectedContent, err := os.ReadFile("../testdata/{file}[") 758 require.NoError(t, err) 759 760 actualContent := extractFileFromTar(t, dataBuf.Bytes(), "/test/{file}[") 761 762 require.Equal(t, expectedContent, actualContent) 763 } 764 765 func TestNoDuplicateAutocreatedDirectories(t *testing.T) { 766 info := exampleInfo() 767 info.DisableGlobbing = true 768 info.Contents = []*files.Content{ 769 { 770 Source: "../testdata/fake", 771 Destination: "/etc/foo/bar", 772 }, 773 { 774 Type: files.TypeDir, 775 Destination: "/etc/foo", 776 }, 777 } 778 require.NoError(t, nfpm.PrepareForPackager(info, packagerName)) 779 780 expected := map[string]bool{ 781 "./etc/": true, 782 "./etc/foo/": true, 783 "./etc/foo/bar": true, 784 } 785 786 var dataBuf bytes.Buffer 787 tarball := tar.NewWriter(&dataBuf) 788 _, err := populateDataTar(info, tarball) 789 require.NoError(t, err) 790 require.NoError(t, tarball.Close()) 791 792 contents := tarContents(t, dataBuf.Bytes()) 793 794 if len(expected) != len(contents) { 795 t.Fatalf("contents has %d entries instead of %d: %#v", len(contents), len(expected), contents) 796 } 797 798 for _, entry := range contents { 799 if !expected[entry] { 800 t.Fatalf("unexpected content: %q", entry) 801 } 802 } 803 } 804 805 func TestNoDuplicateDirectories(t *testing.T) { 806 info := exampleInfo() 807 info.DisableGlobbing = true 808 info.Contents = []*files.Content{ 809 { 810 Type: files.TypeDir, 811 Destination: "/etc/foo", 812 }, 813 { 814 Type: files.TypeDir, 815 Destination: "/etc/foo/", 816 }, 817 } 818 require.Error(t, nfpm.PrepareForPackager(info, packagerName)) 819 } 820 821 func TestIgnoreUnrelatedFiles(t *testing.T) { 822 info := exampleInfo() 823 info.Contents = files.Contents{ 824 { 825 Source: "../testdata/fake", 826 Destination: "/usr/bin/fake", 827 Packager: "rpm", 828 }, 829 { 830 Source: "../testdata/whatever.conf", 831 Destination: "/usr/share/doc/fake/fake.txt", 832 Type: files.TypeRPMLicence, 833 }, 834 { 835 Source: "../testdata/whatever.conf", 836 Destination: "/etc/fake/fake.conf", 837 Type: files.TypeRPMLicense, 838 }, 839 { 840 Source: "../testdata/whatever.conf", 841 Destination: "/etc/fake/fake2.conf", 842 Type: files.TypeRPMReadme, 843 }, 844 { 845 Destination: "/var/log/whatever", 846 Type: files.TypeRPMDoc, 847 }, 848 } 849 850 require.NoError(t, nfpm.PrepareForPackager(info, packagerName)) 851 852 var dataBuf bytes.Buffer 853 tarball := tar.NewWriter(&dataBuf) 854 _, err := populateDataTar(info, tarball) 855 require.NoError(t, err) 856 require.NoError(t, tarball.Close()) 857 858 contents := tarContents(t, dataBuf.Bytes()) 859 require.Empty(t, contents) 860 } 861 862 func TestEmptyButRequiredIPKFields(t *testing.T) { 863 item := nfpm.WithDefaults(&nfpm.Info{ 864 Name: "foo", 865 Version: "v1.0.0", 866 }) 867 Default.SetPackagerDefaults(item) 868 869 require.Equal(t, "optional", item.Priority) 870 require.Equal(t, "Unset Maintainer <unset@localhost>", item.Maintainer) 871 872 var deb bytes.Buffer 873 err := Default.Package(item, &deb) 874 require.NoError(t, err) 875 } 876 877 func TestArches(t *testing.T) { 878 for k := range archToIPK { 879 t.Run(k, func(t *testing.T) { 880 info := exampleInfo() 881 info.Arch = k 882 info = ensureValidArch(info) 883 require.Equal(t, archToIPK[k], info.Arch) 884 }) 885 } 886 887 t.Run("override", func(t *testing.T) { 888 info := exampleInfo() 889 info.IPK.Arch = "foo64" 890 info = ensureValidArch(info) 891 require.Equal(t, "foo64", info.Arch) 892 }) 893 } 894 895 func TestFields(t *testing.T) { 896 var w bytes.Buffer 897 require.NoError(t, renderControl(&w, controlData{ 898 Info: nfpm.WithDefaults(&nfpm.Info{ 899 Name: "foo", 900 Description: "Foo does things", 901 Priority: "extra", 902 Maintainer: "Carlos A Becker <pkg@carlosbecker.com>", 903 Version: "v1.0.0", 904 Section: "default", 905 Homepage: "http://carlosbecker.com", 906 Overridables: nfpm.Overridables{ 907 IPK: nfpm.IPK{ 908 Fields: map[string]string{ 909 "Bugs": "https://github.com/goreleaser/nfpm/issues", 910 "Empty": "", 911 }, 912 }, 913 }, 914 }), 915 InstalledSize: 10, 916 })) 917 golden := "testdata/control3.golden" 918 if *update { 919 require.NoError(t, os.WriteFile(golden, w.Bytes(), 0o600)) 920 } 921 bts, err := os.ReadFile(golden) //nolint:gosec 922 require.NoError(t, err) 923 require.Equal(t, string(bts), w.String()) 924 } 925 926 func TestMost(t *testing.T) { 927 var w bytes.Buffer 928 require.NoError(t, renderControl(&w, controlData{ 929 Info: nfpm.WithDefaults(&nfpm.Info{ 930 Name: "foo", 931 Description: "Foo does things", 932 Priority: "extra", 933 Maintainer: "Carlos A Becker <pkg@carlosbecker.com>", 934 Version: "v1.0.0", 935 Section: "default", 936 License: "MIT", 937 Homepage: "http://carlosbecker.com", 938 Overridables: nfpm.Overridables{ 939 IPK: nfpm.IPK{ 940 ABIVersion: "1", 941 AutoInstalled: true, 942 Essential: true, 943 Fields: map[string]string{ 944 "Bugs": "https://github.com/goreleaser/nfpm/issues", 945 "Empty": "", 946 }, 947 }, 948 }, 949 }), 950 InstalledSize: 10, 951 })) 952 golden := "testdata/control_most.golden" 953 if *update { 954 require.NoError(t, os.WriteFile(golden, w.Bytes(), 0o600)) 955 } 956 bts, err := os.ReadFile(golden) //nolint:gosec 957 require.NoError(t, err) 958 require.Equal(t, string(bts), w.String()) 959 } 960 961 func TestGlob(t *testing.T) { 962 require.NoError(t, Default.Package(nfpm.WithDefaults(&nfpm.Info{ 963 Name: "nfpm-repro", 964 Version: "1.0.0", 965 Maintainer: "asdfasdf", 966 967 Overridables: nfpm.Overridables{ 968 Contents: files.Contents{ 969 { 970 Destination: "/usr/share/nfpm-repro", 971 Source: "../files/*", 972 }, 973 }, 974 }, 975 }), io.Discard)) 976 } 977 978 func TestBadProvides(t *testing.T) { 979 var w bytes.Buffer 980 info := exampleInfo() 981 info.Provides = []string{" "} 982 require.NoError(t, renderControl(&w, controlData{ 983 Info: nfpm.WithDefaults(info), 984 })) 985 golden := "testdata/bad_provides.golden" 986 if *update { 987 require.NoError(t, os.WriteFile(golden, w.Bytes(), 0o600)) 988 } 989 bts, err := os.ReadFile(golden) //nolint:gosec 990 require.NoError(t, err) 991 require.Equal(t, string(bts), w.String()) 992 } 993 994 func Test_stripDisallowedFields(t *testing.T) { 995 tests := []struct { 996 description string 997 info *nfpm.Info 998 expect map[string]string 999 }{ 1000 { 1001 description: "", 1002 info: &nfpm.Info{ 1003 Overridables: nfpm.Overridables{ 1004 IPK: nfpm.IPK{ 1005 ABIVersion: "1", 1006 AutoInstalled: true, 1007 Essential: true, 1008 Fields: map[string]string{ 1009 "Bugs": "https://github.com/goreleaser/nfpm/issues", 1010 "Empty": "", 1011 "Conffiles": "removed", 1012 "Filename": "removed", 1013 "Installed-Time": "removed", 1014 "MD5sum": "removed", 1015 "SHA256sum": "removed", 1016 "Size": "removed", 1017 "size": "removed", 1018 "Status": "removed", 1019 "Source": "ok", 1020 }, 1021 }, 1022 }, 1023 }, 1024 expect: map[string]string{ 1025 "Bugs": "https://github.com/goreleaser/nfpm/issues", 1026 "Empty": "", 1027 "Source": "ok", 1028 }, 1029 }, 1030 } 1031 for _, tc := range tests { 1032 t.Run(tc.description, func(t *testing.T) { 1033 assert := assert.New(t) 1034 1035 stripDisallowedFields(tc.info) 1036 1037 assert.Equal(tc.expect, tc.info.IPK.Fields) 1038 }) 1039 } 1040 }