github.com/goreleaser/nfpm/v2@v2.44.0/files/files_test.go (about) 1 package files_test 2 3 import ( 4 "fmt" 5 "io/fs" 6 "os" 7 "path/filepath" 8 "runtime" 9 "strconv" 10 "strings" 11 "sync" 12 "testing" 13 "time" 14 15 "github.com/goreleaser/nfpm/v2/files" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 "gopkg.in/yaml.v3" 19 ) 20 21 var mtime = time.Date(2023, 11, 5, 23, 15, 17, 0, time.UTC) 22 23 type testStruct struct { 24 Contents files.Contents `yaml:"contents"` 25 } 26 27 func TestBasicDecode(t *testing.T) { 28 var config testStruct 29 dec := yaml.NewDecoder(strings.NewReader(`--- 30 contents: 31 - src: a 32 dst: b 33 - src: a 34 dst: b 35 type: "config|noreplace" 36 packager: "rpm" 37 file_info: 38 mode: 0644 39 mtime: 2008-01-02T15:04:05Z 40 `)) 41 dec.KnownFields(true) 42 err := dec.Decode(&config) 43 require.NoError(t, err) 44 require.Len(t, config.Contents, 2) 45 for _, f := range config.Contents { 46 t.Logf("%+#v\n", f) 47 require.Equal(t, "a", f.Source) 48 require.Equal(t, "b", f.Destination) 49 } 50 } 51 52 func TestDeepPathsWithGlobAndUmask(t *testing.T) { 53 path := filepath.Join(t.TempDir(), "foo", "bar", "zaz", "file.txt") 54 // create a bunch of files with bad permissions 55 require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o777)) 56 require.NoError(t, os.WriteFile(path, nil, 0o777)) 57 var config testStruct 58 dec := yaml.NewDecoder(strings.NewReader(`--- 59 contents: 60 - src: testdata/globtest/**/* 61 dst: /bla 62 file_info: 63 mode: 0644 64 mtime: 2008-01-02T15:04:05Z 65 - src: testdata/deep-paths/ 66 dst: /bar 67 - src: ` + path + ` 68 dst: /foo/file.txt 69 `)) 70 dec.KnownFields(true) 71 err := dec.Decode(&config) 72 require.NoError(t, err) 73 require.Len(t, config.Contents, 3) 74 parsedContents, err := files.PrepareForPackager( 75 config.Contents, 76 0o133, 77 "", 78 false, 79 mtime, 80 ) 81 require.NoError(t, err) 82 for _, c := range parsedContents { 83 switch c.Source { 84 case "testdata/globtest/nested/b.txt": 85 require.Equal(t, "/bla/nested/b.txt", c.Destination) 86 require.Equal(t, "-rw-r--r--", c.Mode().String()) 87 case "testdata/globtest/multi-nested/subdir/c.txt": 88 require.Equal(t, "/bla/multi-nested/subdir/c.txt", c.Destination) 89 require.Equal(t, "-rw-r--r--", c.Mode().String()) 90 case "testdata/deep-paths/nested1/nested2/a.txt": 91 require.Equal(t, "/bar/nested1/nested2/a.txt", c.Destination) 92 require.Equal(t, "-rw-r--r--", c.Mode().String()) 93 case path: 94 require.Equal(t, "/foo/file.txt", c.Destination) 95 require.Equal(t, "-rw-r--r--", c.Mode().String()) 96 } 97 } 98 } 99 100 func TestDeepPathsWithoutGlob(t *testing.T) { 101 var config testStruct 102 dec := yaml.NewDecoder(strings.NewReader(`--- 103 contents: 104 - src: testdata/deep-paths/ 105 dst: /bla 106 `)) 107 dec.KnownFields(true) 108 err := dec.Decode(&config) 109 require.NoError(t, err) 110 require.Len(t, config.Contents, 1) 111 parsedContents, err := files.PrepareForPackager( 112 config.Contents, 113 0, 114 "", 115 true, 116 mtime, 117 ) 118 require.NoError(t, err) 119 present := false 120 121 for _, f := range parsedContents { 122 switch f.Source { 123 case "testdata/deep-paths/nested1/nested2/a.txt": 124 present = true 125 require.Equal(t, "/bla/nested1/nested2/a.txt", f.Destination) 126 case "": 127 continue 128 default: 129 t.Errorf("unknown source %s for content %#v", f.Source, f) 130 } 131 } 132 133 require.True(t, present) 134 } 135 136 func TestFileInfoDefault(t *testing.T) { 137 var config testStruct 138 dec := yaml.NewDecoder(strings.NewReader(`--- 139 contents: 140 - src: files_test.go 141 dst: b 142 `)) 143 dec.KnownFields(true) 144 err := dec.Decode(&config) 145 require.NoError(t, err) 146 147 config.Contents, err = files.PrepareForPackager( 148 config.Contents, 149 0, 150 "", 151 true, 152 mtime, 153 ) 154 require.NoError(t, err) 155 require.Len(t, config.Contents, 1) 156 157 fi, err := os.Stat("files_test.go") 158 require.NoError(t, err) 159 160 f := config.Contents[0] 161 require.Equal(t, "files_test.go", f.Source) 162 require.Equal(t, "/b", f.Destination) 163 require.Equal(t, f.FileInfo.Mode, fi.Mode()) 164 require.Equal(t, f.FileInfo.MTime, mtime) 165 } 166 167 func TestFileInfo(t *testing.T) { 168 var config testStruct 169 dec := yaml.NewDecoder(strings.NewReader(`--- 170 contents: 171 - src: files_test.go 172 dst: b 173 type: "config|noreplace" 174 packager: "rpm" 175 file_info: 176 mode: 0123 177 mtime: 2008-01-02T15:04:05Z 178 `)) 179 dec.KnownFields(true) 180 err := dec.Decode(&config) 181 require.NoError(t, err) 182 183 config.Contents, err = files.PrepareForPackager( 184 config.Contents, 185 0, 186 "rpm", 187 true, 188 mtime, 189 ) 190 require.NoError(t, err) 191 require.Len(t, config.Contents, 1) 192 193 ct, err := time.Parse(time.RFC3339, "2008-01-02T15:04:05Z") 194 require.NoError(t, err) 195 196 f := config.Contents[0] 197 require.Equal(t, "files_test.go", f.Source) 198 require.Equal(t, "/b", f.Destination) 199 require.Equal(t, f.FileInfo.Mode, os.FileMode(0o123)) 200 require.Equal(t, f.FileInfo.MTime, ct) 201 } 202 203 func TestSymlinksInDirectory(t *testing.T) { 204 var config testStruct 205 dec := yaml.NewDecoder(strings.NewReader(`--- 206 contents: 207 - src: testdata/symlinks/subdir 208 dst: /bla 209 - src: testdata/symlinks/link-1 210 dst: / 211 - src: testdata/symlinks/link-2 212 dst: / 213 - src: existent 214 dst: /bla/link-3 215 type: symlink 216 `)) 217 dec.KnownFields(true) 218 err := dec.Decode(&config) 219 require.NoError(t, err) 220 221 config.Contents, err = files.PrepareForPackager( 222 config.Contents, 223 0, 224 "", 225 true, 226 mtime, 227 ) 228 require.NoError(t, err) 229 config.Contents = withoutFileInfo(config.Contents) 230 231 expected := files.Contents{ 232 { 233 Source: "", 234 Destination: "/bla/", 235 Type: files.TypeImplicitDir, 236 }, 237 { 238 Source: "testdata/symlinks/subdir/existent", 239 Destination: "/bla/existent", 240 Type: files.TypeFile, 241 }, 242 { 243 Source: "non-existent", 244 Destination: "/bla/link-1", 245 Type: files.TypeSymlink, 246 }, 247 { 248 Source: "existent", 249 Destination: "/bla/link-2", 250 Type: files.TypeSymlink, 251 }, 252 { 253 Source: "existent", 254 Destination: "/bla/link-3", 255 Type: files.TypeSymlink, 256 }, 257 { 258 Source: "broken", 259 Destination: "/link-1", 260 Type: files.TypeSymlink, 261 }, 262 { 263 Source: "bla", 264 Destination: "/link-2", 265 Type: files.TypeSymlink, 266 }, 267 } 268 269 require.Equal(t, expected, config.Contents) 270 } 271 272 func TestRace(t *testing.T) { 273 var config testStruct 274 dec := yaml.NewDecoder(strings.NewReader(`--- 275 contents: 276 - src: a 277 dst: b 278 type: symlink 279 `)) 280 err := dec.Decode(&config) 281 require.NoError(t, err) 282 errs := make(chan error, 10) 283 t.Cleanup(func() { close(errs) }) 284 var wg sync.WaitGroup 285 for range 10 { 286 wg.Add(1) 287 go func() { 288 defer wg.Done() 289 _, err := files.PrepareForPackager( 290 config.Contents, 291 0, 292 "", 293 false, 294 mtime, 295 ) 296 errs <- err 297 }() 298 } 299 wg.Wait() 300 301 for range 10 { 302 require.NoError(t, <-errs) 303 } 304 } 305 306 func TestCollision(t *testing.T) { 307 t.Run("collision between files for all packagers", func(t *testing.T) { 308 configuredFiles := []*files.Content{ 309 {Source: "../testdata/whatever.conf", Destination: "/samedestination"}, 310 {Source: "../testdata/whatever2.conf", Destination: "/samedestination"}, 311 } 312 313 _, err := files.PrepareForPackager( 314 configuredFiles, 315 0, 316 "", 317 true, 318 mtime, 319 ) 320 require.ErrorIs(t, err, files.ErrContentCollision) 321 }) 322 323 t.Run("no collision due to different per-file packagers", func(t *testing.T) { 324 configuredFiles := []*files.Content{ 325 {Source: "../testdata/whatever.conf", Destination: "/samedestination", Packager: "foo"}, 326 {Source: "../testdata/whatever2.conf", Destination: "/samedestination", Packager: "bar"}, 327 } 328 329 _, err := files.PrepareForPackager( 330 configuredFiles, 331 0, 332 "foo", 333 true, 334 mtime, 335 ) 336 require.NoError(t, err) 337 }) 338 339 t.Run("collision between file for all packagers and file for specific packager", func(t *testing.T) { 340 configuredFiles := []*files.Content{ 341 {Source: "../testdata/whatever.conf", Destination: "/samedestination", Packager: "foo"}, 342 {Source: "../testdata/whatever2.conf", Destination: "/samedestination", Packager: ""}, 343 } 344 345 _, err := files.PrepareForPackager( 346 configuredFiles, 347 0, 348 "foo", 349 true, 350 mtime, 351 ) 352 require.ErrorIs(t, err, files.ErrContentCollision) 353 }) 354 } 355 356 func TestDisableGlobbing(t *testing.T) { 357 testCases := []files.Content{ 358 { 359 Source: "testdata/{test}/bar", 360 Destination: "/etc/{test}/bar", 361 }, 362 { 363 Source: "testdata/{test}/[f]oo", 364 Destination: "testdata/{test}/[f]oo", 365 }, 366 { 367 Source: "testdata/globtest/a.txt", 368 Destination: "testdata/globtest/a.txt", 369 }, 370 { 371 Source: "testdata/globtest/a.txt", 372 Destination: "/etc/a.txt", 373 }, 374 } 375 376 disableGlobbing := true 377 378 for i, testCase := range testCases { 379 content := testCase 380 381 t.Run(strconv.Itoa(i), func(t *testing.T) { 382 result, err := files.PrepareForPackager( 383 files.Contents{&content}, 384 0, 385 "", 386 disableGlobbing, 387 mtime, 388 ) 389 if err != nil { 390 t.Fatalf("expand content globs: %v", err) 391 } 392 393 result = withoutImplicitDirs(result) 394 395 if len(result) != 1 { 396 t.Fatalf("unexpected result length: %d, expected one", len(result)) 397 } 398 399 actualContent := result[0] 400 401 // we expect the result content to be identical to the input content 402 if actualContent.Source != content.Source { 403 t.Fatalf("unexpected content source: %q, expected %q", actualContent.Source, content.Source) 404 } 405 406 if strings.TrimLeft(actualContent.Destination, "./") != strings.TrimLeft(content.Destination, "/") { 407 t.Fatalf("unexpected content destination: %q, expected %q", 408 strings.TrimLeft(actualContent.Destination, "./"), strings.TrimLeft(content.Destination, "/")) 409 } 410 }) 411 } 412 } 413 414 func withoutImplicitDirs(contents files.Contents) files.Contents { 415 filtered := make(files.Contents, 0, len(contents)) 416 417 for _, c := range contents { 418 if c.Type != files.TypeImplicitDir { 419 filtered = append(filtered, c) 420 } 421 } 422 423 return filtered 424 } 425 426 func TestGlobbingWhenFilesHaveBrackets(t *testing.T) { 427 if runtime.GOOS == "windows" { 428 t.Skip("doesn't work on windows") 429 } 430 result, err := files.PrepareForPackager( 431 files.Contents{ 432 { 433 Source: "./testdata/\\{test\\}/", 434 Destination: ".", 435 }, 436 }, 437 0, 438 "", 439 false, 440 mtime, 441 ) 442 if err != nil { 443 t.Fatalf("expand content globs: %v", err) 444 } 445 446 expected := files.Contents{ 447 { 448 Source: "testdata/{test}/[f]oo", 449 Destination: "/[f]oo", 450 }, 451 { 452 Source: "testdata/{test}/bar", 453 Destination: "/bar", 454 }, 455 } 456 457 if len(result) != 2 { 458 t.Fatalf("unexpected result length: %d, expected one", len(result)) 459 } 460 461 for i, r := range result { 462 ex := expected[i] 463 if ex.Source != r.Source { 464 t.Fatalf("unexpected content source: %q, expected %q", r.Source, ex.Source) 465 } 466 if ex.Destination != r.Destination { 467 t.Fatalf("unexpected content destination: %q, expected %q", 468 ex.Destination, r.Destination) 469 } 470 } 471 } 472 473 func TestGlobbingFilesWithDifferentSizesWithFileInfo(t *testing.T) { 474 result, err := files.PrepareForPackager( 475 files.Contents{ 476 { 477 Source: "./testdata/globtest/different-sizes/**/*", 478 Destination: ".", 479 FileInfo: &files.ContentFileInfo{ 480 Mode: 0o777, 481 }, 482 }, 483 }, 484 0, 485 "", 486 false, 487 mtime, 488 ) 489 if err != nil { 490 t.Fatalf("expand content globs: %v", err) 491 } 492 493 result = withoutImplicitDirs(result) 494 495 if len(result) != 2 { 496 t.Fatalf("unexpected result length: %d, expected 2", len(result)) 497 } 498 499 if result[0].FileInfo.Size == result[1].FileInfo.Size { 500 t.Fatal("test FileInfos have the same size, expected different") 501 } 502 } 503 504 func TestDestEndsWithSlash(t *testing.T) { 505 result, err := files.PrepareForPackager( 506 files.Contents{ 507 { 508 Source: "./testdata/globtest/a.txt", 509 Destination: "./foo/", 510 }, 511 }, 512 0, 513 "", 514 false, 515 mtime, 516 ) 517 result = withoutImplicitDirs(result) 518 require.NoError(t, err) 519 require.Len(t, result, 1) 520 require.Equal(t, "/foo/a.txt", result[0].Destination) 521 } 522 523 func TestInvalidFileType(t *testing.T) { 524 var config testStruct 525 dec := yaml.NewDecoder(strings.NewReader(`--- 526 contents: 527 - src: testdata/globtest/**/* 528 dst: /bla 529 type: filr 530 `)) 531 dec.KnownFields(true) 532 require.NoError(t, dec.Decode(&config)) 533 _, err := files.PrepareForPackager( 534 config.Contents, 535 0, 536 "", 537 false, 538 mtime, 539 ) 540 require.EqualError(t, err, "invalid content type: filr") 541 } 542 543 func TestValidFileTypes(t *testing.T) { 544 var config testStruct 545 dec := yaml.NewDecoder(strings.NewReader(`--- 546 contents: 547 - src: testdata/globtest/a.txt 548 dst: /f1.txt 549 - src: testdata/globtest/a.txt 550 dst: /f2.txt 551 type: file 552 - src: testdata/globtest/a.txt 553 dst: /f3.txt 554 type: config 555 - src: testdata/globtest/a.txt 556 dst: /f4.txt 557 type: config|noreplace 558 - src: testdata/globtest/a.txt 559 dst: /f5.txt 560 type: symlink 561 - src: testdata/globtest/a.txt 562 dst: /f6.txt 563 type: dir 564 - src: testdata/globtest/a.txt 565 dst: /f7.txt 566 type: ghost 567 `)) 568 dec.KnownFields(true) 569 require.NoError(t, dec.Decode(&config)) 570 _, err := files.PrepareForPackager( 571 config.Contents, 572 0, 573 "", 574 false, 575 mtime, 576 ) 577 require.NoError(t, err) 578 } 579 580 func TestImplicitDirectories(t *testing.T) { 581 results, err := files.PrepareForPackager( 582 files.Contents{ 583 { 584 Source: "./testdata/globtest/a.txt", 585 Destination: "./foo/bar/baz", 586 }, 587 }, 588 0, 589 "", 590 false, 591 mtime, 592 ) 593 require.NoError(t, err) 594 595 expected := files.Contents{ 596 { 597 Source: "", 598 Destination: "/foo/", 599 Type: files.TypeImplicitDir, 600 }, 601 { 602 Source: "", 603 Destination: "/foo/bar/", 604 Type: files.TypeImplicitDir, 605 }, 606 { 607 Source: "testdata/globtest/a.txt", 608 Destination: "/foo/bar/baz", 609 Type: files.TypeFile, 610 }, 611 } 612 613 require.Equal(t, expected, withoutFileInfo(results)) 614 } 615 616 func TestRelevantFiles(t *testing.T) { 617 contents := files.Contents{ 618 { 619 Source: "./testdata/globtest/a.txt", 620 Destination: "/1allpackagers", 621 }, 622 { 623 Source: "./testdata/globtest/a.txt", 624 Destination: "/2onlyrpm", 625 Packager: "rpm", 626 }, 627 { 628 Source: "./testdata/globtest/a.txt", 629 Destination: "/3onlydeb", 630 Packager: "deb", 631 }, 632 { 633 Source: "./testdata/globtest/a.txt", 634 Destination: "/4debchangelog", 635 Type: files.TypeDebChangelog, 636 }, 637 { 638 Source: "./testdata/globtest/a.txt", 639 Destination: "/5ghost", 640 Type: files.TypeRPMGhost, 641 }, 642 { 643 Source: "./testdata/globtest/a.txt", 644 Destination: "/6doc", 645 Type: files.TypeRPMDoc, 646 }, 647 { 648 Source: "./testdata/globtest/a.txt", 649 Destination: "/7licence", 650 Type: files.TypeRPMLicence, 651 }, 652 { 653 Source: "./testdata/globtest/a.txt", 654 Destination: "/8license", 655 Type: files.TypeRPMLicense, 656 }, 657 { 658 Source: "./testdata/globtest/a.txt", 659 Destination: "/9readme", 660 Type: files.TypeRPMReadme, 661 }, 662 } 663 664 t.Run("deb", func(t *testing.T) { 665 results, err := files.PrepareForPackager( 666 contents, 667 0, 668 "deb", 669 false, 670 mtime, 671 ) 672 require.NoError(t, err) 673 require.Equal(t, files.Contents{ 674 { 675 Source: "testdata/globtest/a.txt", 676 Destination: "/1allpackagers", 677 Type: files.TypeFile, 678 }, 679 { 680 Source: "testdata/globtest/a.txt", 681 Destination: "/3onlydeb", 682 Packager: "deb", 683 Type: files.TypeFile, 684 }, 685 { 686 Source: "testdata/globtest/a.txt", 687 Destination: "/4debchangelog", 688 Type: files.TypeDebChangelog, 689 }, 690 }, withoutFileInfo(results)) 691 }) 692 693 t.Run("rpm", func(t *testing.T) { 694 results, err := files.PrepareForPackager( 695 contents, 696 0, 697 "rpm", 698 false, 699 mtime, 700 ) 701 require.NoError(t, err) 702 require.Equal(t, files.Contents{ 703 { 704 Source: "testdata/globtest/a.txt", 705 Destination: "/1allpackagers", 706 Type: files.TypeFile, 707 }, 708 { 709 Source: "testdata/globtest/a.txt", 710 Destination: "/2onlyrpm", 711 Packager: "rpm", 712 Type: files.TypeFile, 713 }, 714 { 715 Source: "testdata/globtest/a.txt", 716 Destination: "/5ghost", 717 Type: files.TypeRPMGhost, 718 }, 719 { 720 Source: "testdata/globtest/a.txt", 721 Destination: "/6doc", 722 Type: files.TypeRPMDoc, 723 }, 724 { 725 Source: "testdata/globtest/a.txt", 726 Destination: "/7licence", 727 Type: files.TypeRPMLicence, 728 }, 729 { 730 Source: "testdata/globtest/a.txt", 731 Destination: "/8license", 732 Type: files.TypeRPMLicense, 733 }, 734 { 735 Source: "testdata/globtest/a.txt", 736 Destination: "/9readme", 737 Type: files.TypeRPMReadme, 738 }, 739 }, withoutFileInfo(results)) 740 }) 741 742 t.Run("apk", func(t *testing.T) { 743 results, err := files.PrepareForPackager( 744 contents, 745 0, 746 "apk", 747 false, 748 mtime, 749 ) 750 require.NoError(t, err) 751 require.Equal(t, files.Contents{ 752 { 753 Source: "testdata/globtest/a.txt", 754 Destination: "/1allpackagers", 755 Type: files.TypeFile, 756 }, 757 }, withoutFileInfo(results)) 758 }) 759 } 760 761 func TestTreeOwner(t *testing.T) { 762 results, err := files.PrepareForPackager( 763 files.Contents{ 764 { 765 Source: filepath.Join("testdata", "tree"), 766 Destination: "/usr/share/foo", 767 Type: files.TypeTree, 768 FileInfo: &files.ContentFileInfo{ 769 Owner: "goreleaser", 770 Group: "goreleaser", 771 MTime: mtime, 772 }, 773 }, 774 }, 775 0, 776 "", 777 false, 778 mtime, 779 ) 780 require.NoError(t, err) 781 782 for _, f := range results { 783 if f.Destination == "/usr/" || f.Destination == "/usr/share/" { 784 require.Equal(t, "root", f.FileInfo.Owner, f.Destination) 785 require.Equal(t, "root", f.FileInfo.Group, f.Destination) 786 continue 787 } 788 require.Equal(t, "goreleaser", f.FileInfo.Owner, f.Destination) 789 require.Equal(t, "goreleaser", f.FileInfo.Group, f.Destination) 790 } 791 792 require.Equal(t, files.Contents{ 793 { 794 Source: "", 795 Destination: "/usr/", 796 Type: files.TypeImplicitDir, 797 }, 798 { 799 Source: "", 800 Destination: "/usr/share/", 801 Type: files.TypeImplicitDir, 802 }, 803 { 804 Source: "", 805 Destination: "/usr/share/foo/", 806 Type: files.TypeDir, 807 }, 808 { 809 Source: "", 810 Destination: "/usr/share/foo/files/", 811 Type: files.TypeDir, 812 }, 813 { 814 Source: filepath.Join("testdata", "tree", "files", "a"), 815 Destination: "/usr/share/foo/files/a", 816 Type: files.TypeFile, 817 }, 818 { 819 Source: "", 820 Destination: "/usr/share/foo/files/b/", 821 Type: files.TypeDir, 822 }, 823 { 824 Source: filepath.Join("testdata", "tree", "files", "b", "c"), 825 Destination: "/usr/share/foo/files/b/c", 826 Type: files.TypeFile, 827 }, 828 { 829 Source: "", 830 Destination: "/usr/share/foo/symlinks/", 831 Type: files.TypeDir, 832 }, 833 { 834 Source: "/etc/foo", 835 Destination: "/usr/share/foo/symlinks/link1", 836 Type: files.TypeSymlink, 837 }, 838 { 839 Source: "../files/a", 840 Destination: "/usr/share/foo/symlinks/link2", 841 Type: files.TypeSymlink, 842 }, 843 }, withoutFileInfo(results)) 844 } 845 846 func TestTree(t *testing.T) { 847 results, err := files.PrepareForPackager( 848 files.Contents{ 849 { 850 Source: filepath.Join("testdata", "tree"), 851 Destination: "/base", 852 Type: files.TypeTree, 853 }, 854 }, 855 0, 856 "", 857 false, 858 mtime, 859 ) 860 require.NoError(t, err) 861 862 require.Equal(t, files.Contents{ 863 { 864 Source: "", 865 Destination: "/base/", 866 Type: files.TypeDir, 867 }, 868 { 869 Source: "", 870 Destination: "/base/files/", 871 Type: files.TypeDir, 872 }, 873 { 874 Source: filepath.Join("testdata", "tree", "files", "a"), 875 Destination: "/base/files/a", 876 Type: files.TypeFile, 877 }, 878 { 879 Source: "", 880 Destination: "/base/files/b/", 881 Type: files.TypeDir, 882 }, 883 { 884 Source: filepath.Join("testdata", "tree", "files", "b", "c"), 885 Destination: "/base/files/b/c", 886 Type: files.TypeFile, 887 }, 888 { 889 Source: "", 890 Destination: "/base/symlinks/", 891 Type: files.TypeDir, 892 }, 893 { 894 Source: "/etc/foo", 895 Destination: "/base/symlinks/link1", 896 Type: files.TypeSymlink, 897 }, 898 { 899 Source: "../files/a", 900 Destination: "/base/symlinks/link2", 901 Type: files.TypeSymlink, 902 }, 903 }, withoutFileInfo(results)) 904 } 905 906 func TestTreeMode(t *testing.T) { 907 results, err := files.PrepareForPackager( 908 files.Contents{ 909 { 910 Source: filepath.Join("testdata", "tree"), 911 Destination: "/usr/share/foo", 912 Type: files.TypeTree, 913 FileInfo: &files.ContentFileInfo{ 914 Mode: 0o777, 915 }, 916 }, 917 }, 918 0, 919 "", 920 false, 921 mtime, 922 ) 923 require.NoError(t, err) 924 925 for _, f := range results { 926 if f.Destination == "/usr/" || f.Destination == "/usr/share/" || f.Type == files.TypeSymlink { 927 require.NotEqual(t, fs.FileMode(0o777).String(), f.FileInfo.Mode.String(), f.Destination) 928 continue 929 } 930 require.Equal(t, fs.FileMode(0o777).String(), f.FileInfo.Mode.String(), "%s: %s", f.Destination, f.Type) 931 } 932 } 933 934 func withoutFileInfo(contents files.Contents) files.Contents { 935 filtered := make(files.Contents, 0, len(contents)) 936 937 for _, c := range contents { 938 cc := *c 939 cc.FileInfo = nil 940 filtered = append(filtered, &cc) 941 } 942 943 return filtered 944 } 945 946 func TestAsRelativePath(t *testing.T) { 947 sep := fmt.Sprintf("%c", filepath.Separator) 948 testCases := map[string]string{ 949 "/etc/foo/": "etc/foo/", 950 "./etc/foo": "etc/foo", 951 "./././foo/../bar/": "bar/", 952 sep: "", 953 sep + sep: "", 954 sep + strings.Join([]string{"foo", "bar", "zazz"}, sep): "foo/bar/zazz", 955 } 956 957 for input, expected := range testCases { 958 assert.Equal(t, expected, files.AsRelativePath(input)) 959 } 960 } 961 962 func TestAsExplicitRelativePath(t *testing.T) { 963 sep := fmt.Sprintf("%c", filepath.Separator) 964 testCases := map[string]string{ 965 "/etc/foo/": "./etc/foo/", 966 "./etc/foo": "./etc/foo", 967 "./././foo/../bar/": "./bar/", 968 sep: "./", 969 sep: "./", 970 sep + strings.Join([]string{"foo", "bar", "zazz"}, sep): "./foo/bar/zazz", 971 } 972 973 for input, expected := range testCases { 974 assert.Equal(t, expected, files.AsExplicitRelativePath(input)) 975 } 976 } 977 978 func TestIssue954(t *testing.T) { 979 tmpDir := t.TempDir() 980 contentDir := filepath.Join(tmpDir, "content") 981 require.NoError(t, os.MkdirAll(filepath.Join(contentDir, "subdir1"), 0o755)) 982 require.NoError(t, os.MkdirAll(filepath.Join(contentDir, "subdir2"), 0o755)) 983 require.NoError(t, os.WriteFile(filepath.Join(contentDir, "subdir1", "file1.txt"), []byte("test"), 0o644)) 984 require.NoError(t, os.WriteFile(filepath.Join(contentDir, "subdir2", "file2.txt"), []byte("test"), 0o644)) 985 986 results, err := files.PrepareForPackager( 987 files.Contents{ 988 { 989 Source: contentDir + "/", 990 Destination: "/srv/www", 991 FileInfo: &files.ContentFileInfo{ 992 Owner: "www-data", 993 Group: "www-data", 994 Mode: 0o775, 995 }, 996 }, 997 }, 998 0, 999 "", 1000 false, 1001 mtime, 1002 ) 1003 require.NoError(t, err) 1004 1005 found := []string{} 1006 for _, f := range results { 1007 found = append(found, f.Destination) 1008 if f.Destination == "/srv/" { 1009 require.Equal(t, "root", f.FileInfo.Owner, f.Destination) 1010 require.Equal(t, "root", f.FileInfo.Group, f.Destination) 1011 continue 1012 } 1013 require.Contains(t, f.Destination, "/srv/www") 1014 require.Equal(t, "www-data", f.FileInfo.Owner, f.Destination) 1015 require.Equal(t, "www-data", f.FileInfo.Group, f.Destination) 1016 1017 } 1018 require.ElementsMatch(t, []string{ 1019 "/srv/", 1020 "/srv/www/", 1021 "/srv/www/subdir1/", 1022 "/srv/www/subdir1/file1.txt", 1023 "/srv/www/subdir2/", 1024 "/srv/www/subdir2/file2.txt", 1025 }, found) 1026 } 1027 1028 func TestIssue952(t *testing.T) { 1029 contents := files.Contents{ 1030 { 1031 Source: "/source", 1032 Destination: "/dest", 1033 Type: files.TypeSymlink, 1034 }, 1035 } 1036 1037 result, err := files.PrepareForPackager(contents, 0, "", false, mtime) 1038 require.NoError(t, err) 1039 require.Len(t, result, 1) 1040 1041 symlink := result[0] 1042 require.Equal(t, files.TypeSymlink, symlink.Type) 1043 require.Equal(t, "/dest", symlink.Destination) 1044 require.Equal(t, mtime, symlink.FileInfo.MTime) 1045 } 1046 1047 func TestIssue829(t *testing.T) { 1048 var config testStruct 1049 dec := yaml.NewDecoder(strings.NewReader(`--- 1050 contents: 1051 # Implementation. 1052 - src: ./testdata/issue829/lib/ 1053 dst: /usr/lib/ 1054 type: tree 1055 - src: ./testdata/issue829/usr/ 1056 dst: /usr/ 1057 type: tree 1058 - src: ./testdata/issue829/var/ 1059 dst: /var/ 1060 type: tree 1061 1062 # Configuration. 1063 - src: ./testdata/issue829/etc/logrotate.d/acme 1064 dst: /etc/logrotate.d/acme 1065 type: config|noreplace 1066 - src: ./testdata/issue829/etc/sysconfig/acme 1067 dst: /etc/sysconfig/acme 1068 type: config|noreplace 1069 `)) 1070 dec.KnownFields(true) 1071 require.NoError(t, dec.Decode(&config)) 1072 files, err := files.PrepareForPackager( 1073 config.Contents, 1074 0, 1075 "", 1076 false, 1077 mtime, 1078 ) 1079 require.NoError(t, err) 1080 1081 expect := map[string]string{ 1082 "/etc/": "implicit dir", 1083 "/etc/logrotate.d/": "implicit dir", 1084 "/etc/logrotate.d/acme": "config|noreplace", 1085 "/etc/sysconfig/": "implicit dir", 1086 "/etc/sysconfig/acme": "config|noreplace", 1087 "/usr/lib/": "implicit dir", 1088 "/usr/lib/systemd/": "implicit dir", 1089 "/usr/lib/systemd/system/": "implicit dir", 1090 "/usr/lib/systemd/system/acme.service": "file", 1091 "/usr/": "implicit dir", 1092 "/usr/bin/": "implicit dir", 1093 "/usr/bin/acme": "file", 1094 "/usr/share/": "implicit dir", 1095 "/usr/share/doc/": "implicit dir", 1096 "/usr/share/doc/acme/": "dir", 1097 "/usr/share/doc/acme/readme.md": "file", 1098 "/usr/share/man/": "implicit dir", 1099 "/usr/share/man/man1/": "implicit dir", 1100 "/usr/share/man/man1/acme.1.gz": "file", 1101 "/var/": "implicit dir", 1102 "/var/log/": "implicit dir", 1103 "/var/log/acme/": "dir", 1104 "/var/log/acme/.gitkeep": "file", 1105 } 1106 1107 for _, file := range files { 1108 require.Equal(t, expect[file.Destination], file.Type, "invalid type for %s", file.Destination) 1109 } 1110 }