github.com/goreleaser/nfpm/v2@v2.44.0/nfpm_test.go (about) 1 package nfpm_test 2 3 import ( 4 "fmt" 5 "io" 6 "net/mail" 7 "os" 8 "strconv" 9 "strings" 10 "testing" 11 "time" 12 13 "github.com/goreleaser/nfpm/v2" 14 "github.com/goreleaser/nfpm/v2/files" 15 "github.com/stretchr/testify/require" 16 ) 17 18 var mtime = time.Date(2023, 11, 5, 23, 15, 17, 0, time.UTC) 19 20 func TestEnumerate(t *testing.T) { 21 nfpm.RegisterPackager("deb", nil) 22 result := nfpm.Enumerate() 23 require.NotEmpty(t, result) 24 } 25 26 func TestRegister(t *testing.T) { 27 format := "TestRegister" 28 pkgr := &fakePackager{} 29 nfpm.RegisterPackager(format, pkgr) 30 got, err := nfpm.Get(format) 31 require.NoError(t, err) 32 require.Equal(t, pkgr, got) 33 } 34 35 func TestGet(t *testing.T) { 36 format := "TestGet" 37 got, err := nfpm.Get(format) 38 require.Error(t, err) 39 require.EqualError(t, err, "no packager registered for the format "+format) 40 require.Nil(t, got) 41 pkgr := &fakePackager{} 42 nfpm.RegisterPackager(format, pkgr) 43 got, err = nfpm.Get(format) 44 require.NoError(t, err) 45 require.Equal(t, pkgr, got) 46 } 47 48 func TestDefaultsVersion(t *testing.T) { 49 info := nfpm.WithDefaults(&nfpm.Info{ 50 Version: "v1.0.0", 51 VersionSchema: "semver", 52 }) 53 require.NotEmpty(t, info.Platform) 54 require.Equal(t, "1.0.0", info.Version) 55 require.Empty(t, info.Release) 56 require.Empty(t, info.Prerelease) 57 58 info = nfpm.WithDefaults(&nfpm.Info{ 59 Version: "v1.0.0-rc1", 60 }) 61 require.Equal(t, "1.0.0", info.Version) 62 require.Empty(t, info.Release) 63 require.Equal(t, "rc1", info.Prerelease) 64 65 info = nfpm.WithDefaults(&nfpm.Info{ 66 Version: "v1.0.0-beta1", 67 }) 68 require.Equal(t, "1.0.0", info.Version) 69 require.Empty(t, info.Release) 70 require.Equal(t, "beta1", info.Prerelease) 71 72 info = nfpm.WithDefaults(&nfpm.Info{ 73 Version: "v1.0.0-1", 74 Release: "2", 75 Prerelease: "beta1", 76 }) 77 require.Equal(t, "1.0.0", info.Version) 78 require.Equal(t, "2", info.Release) 79 require.Equal(t, "beta1", info.Prerelease) 80 81 info = nfpm.WithDefaults(&nfpm.Info{ 82 Version: "v1.0.0-1+xdg2", 83 Release: "2", 84 Prerelease: "beta1", 85 }) 86 require.Equal(t, "1.0.0", info.Version) 87 require.Equal(t, "2", info.Release) 88 require.Equal(t, "beta1", info.Prerelease) 89 90 info = nfpm.WithDefaults(&nfpm.Info{ 91 Version: "this.is.my.version", 92 VersionSchema: "none", 93 Release: "2", 94 Prerelease: "beta1", 95 }) 96 require.Equal(t, "this.is.my.version", info.Version) 97 require.Equal(t, "2", info.Release) 98 require.Equal(t, "beta1", info.Prerelease) 99 } 100 101 func TestDefaults(t *testing.T) { 102 t.Run("all given", func(t *testing.T) { 103 makeinfo := func() nfpm.Info { 104 return nfpm.Info{ 105 Platform: "darwin", 106 Version: "2.4.1", 107 Description: "no description given", 108 Arch: "arm64", 109 MTime: mtime, 110 Overridables: nfpm.Overridables{ 111 Umask: 0o112, 112 }, 113 } 114 } 115 info := makeinfo() 116 nfpm.WithDefaults(&info) 117 require.Equal(t, makeinfo(), info) 118 }) 119 t.Run("none given", func(t *testing.T) { 120 t.Setenv("SOURCE_DATE_EPOCH", strconv.FormatInt(mtime.Unix(), 10)) 121 got := nfpm.WithDefaults(&nfpm.Info{}) 122 require.Equal(t, nfpm.Info{ 123 Platform: "linux", 124 Arch: "amd64", 125 Version: "0.0.0", 126 Prerelease: "rc0", 127 Description: "no description given", 128 MTime: mtime, 129 Overridables: nfpm.Overridables{ 130 Umask: 0o002, 131 }, 132 }, *got) 133 }) 134 t.Run("mips softfloat", func(t *testing.T) { 135 makeinfo := func() nfpm.Info { 136 return nfpm.Info{ 137 Platform: "linux", 138 Arch: "mips64softfloat", 139 } 140 } 141 info := makeinfo() 142 nfpm.WithDefaults(&info) 143 require.Equal(t, "mips64", info.Arch) 144 }) 145 t.Run("mips softfloat", func(t *testing.T) { 146 makeinfo := func() nfpm.Info { 147 return nfpm.Info{ 148 Platform: "linux", 149 Arch: "mips64hardfloat", 150 } 151 } 152 info := makeinfo() 153 nfpm.WithDefaults(&info) 154 require.Equal(t, "mips64", info.Arch) 155 }) 156 } 157 158 func TestPrepareForPackager(t *testing.T) { 159 t.Run("dirs", func(t *testing.T) { 160 info := nfpm.WithDefaults(&nfpm.Info{ 161 Name: "as", 162 Arch: "asd", 163 Version: "1.2.3", 164 Overridables: nfpm.Overridables{ 165 Umask: 0o032, 166 Contents: []*files.Content{ 167 { 168 Destination: "/usr/share/test", 169 Type: files.TypeDir, 170 }, 171 { 172 Source: "./testdata/contents.yaml", 173 Destination: "asd", 174 }, 175 { 176 Destination: "/usr/a", 177 Type: files.TypeDir, 178 }, 179 }, 180 }, 181 }) 182 require.NoError(t, nfpm.PrepareForPackager(info, "")) 183 require.Len(t, info.Contents, 5) 184 asdFile := info.Contents[0] 185 require.Equal(t, "/asd", asdFile.Destination) 186 require.Equal(t, files.TypeFile, asdFile.Type) 187 require.Equal(t, "-rw-r--r--", asdFile.FileInfo.Mode.String()) 188 require.Equal(t, "root", asdFile.FileInfo.Owner) 189 require.Equal(t, "root", asdFile.FileInfo.Group) 190 usrDir := info.Contents[1] 191 require.Equal(t, "/usr/", usrDir.Destination) 192 require.Equal(t, files.TypeImplicitDir, usrDir.Type) 193 require.Equal(t, "-rwxr-xr-x", usrDir.FileInfo.Mode.String()) 194 require.Equal(t, "root", usrDir.FileInfo.Owner) 195 require.Equal(t, "root", usrDir.FileInfo.Group) 196 aDir := info.Contents[2] 197 require.Equal(t, "/usr/a/", aDir.Destination) 198 require.Equal(t, files.TypeDir, aDir.Type) 199 require.Equal(t, "-rwxr-xr-x", aDir.FileInfo.Mode.String()) 200 require.Equal(t, "root", aDir.FileInfo.Owner) 201 require.Equal(t, "root", aDir.FileInfo.Group) 202 }) 203 204 t.Run("config", func(t *testing.T) { 205 require.NoError(t, nfpm.PrepareForPackager(&nfpm.Info{ 206 Name: "as", 207 Arch: "asd", 208 Version: "1.2.3", 209 Overridables: nfpm.Overridables{ 210 Contents: []*files.Content{ 211 { 212 Source: "./testdata/contents.yaml", 213 Destination: "asd", 214 Type: files.TypeConfig, 215 }, 216 }, 217 }, 218 }, "")) 219 }) 220 } 221 222 func TestValidate(t *testing.T) { 223 t.Run("dirs", func(t *testing.T) { 224 info := nfpm.Info{ 225 Name: "as", 226 Arch: "asd", 227 Version: "1.2.3", 228 Overridables: nfpm.Overridables{ 229 Contents: []*files.Content{ 230 { 231 Destination: "/usr/share/test", 232 Type: files.TypeDir, 233 }, 234 { 235 Source: "./testdata/contents.yaml", 236 Destination: "asd", 237 }, 238 }, 239 }, 240 } 241 require.NoError(t, nfpm.Validate(&info)) 242 require.Len(t, info.Contents, 2) 243 }) 244 245 t.Run("config", func(t *testing.T) { 246 require.NoError(t, nfpm.Validate(&nfpm.Info{ 247 Name: "as", 248 Arch: "asd", 249 Version: "1.2.3", 250 Overridables: nfpm.Overridables{ 251 Contents: []*files.Content{ 252 { 253 Source: "./testdata/contents.yaml", 254 Destination: "asd", 255 Type: files.TypeConfig, 256 }, 257 }, 258 }, 259 })) 260 }) 261 } 262 263 func TestValidateError(t *testing.T) { 264 for err, info := range map[string]*nfpm.Info{ 265 "package name must be provided": {}, 266 "package arch must be provided": { 267 Name: "fo", 268 }, 269 "package version must be provided": { 270 Name: "as", 271 Arch: "asd", 272 }, 273 } { 274 func(inf *nfpm.Info, e string) { 275 t.Run(e, func(t *testing.T) { 276 require.EqualError(t, nfpm.Validate(inf), e) 277 }) 278 }(info, err) 279 } 280 } 281 282 func parseAndValidate(filename string) (nfpm.Config, error) { 283 config, err := nfpm.ParseFile(filename) 284 if err != nil { 285 return config, fmt.Errorf("parse file: %w", err) 286 } 287 288 err = config.Validate() 289 if err != nil { 290 return config, fmt.Errorf("validate: %w", err) 291 } 292 293 err = nfpm.PrepareForPackager(&config.Info, "") 294 if err != nil { 295 return config, fmt.Errorf("prepare for packager: %w", err) 296 } 297 298 return config, nil 299 } 300 301 func TestParseFile(t *testing.T) { 302 nfpm.ClearPackagers() 303 _, err := parseAndValidate("./testdata/overrides.yaml") 304 require.Error(t, err) 305 nfpm.RegisterPackager("deb", &fakePackager{}) 306 nfpm.RegisterPackager("rpm", &fakePackager{}) 307 nfpm.RegisterPackager("apk", &fakePackager{}) 308 nfpm.RegisterPackager("ipk", &fakePackager{}) 309 _, err = parseAndValidate("./testdata/overrides.yaml") 310 require.NoError(t, err) 311 _, err = parseAndValidate("./testdata/doesnotexist.yaml") 312 require.Error(t, err) 313 t.Setenv("RPM_KEY_FILE", "my/rpm/key/file") 314 t.Setenv("TEST_RELEASE_ENV_VAR", "1234") 315 t.Setenv("TEST_PRERELEASE_ENV_VAR", "beta1") 316 t.Setenv("TEST_DESCRIPTION_ENV_VAR", "description") 317 config, err := parseAndValidate("./testdata/env-fields.yaml") 318 require.NoError(t, err) 319 require.Equal(t, fmt.Sprintf("v%s", os.Getenv("GOROOT")), config.Version) 320 require.Equal(t, "1234", config.Release) 321 require.Equal(t, "beta1", config.Prerelease) 322 require.Equal(t, "My description", config.Description) 323 require.Equal(t, "my/rpm/key/file", config.RPM.Signature.KeyFile) 324 require.Equal(t, "hard/coded/file", config.Deb.Signature.KeyFile) 325 require.Empty(t, config.APK.Signature.KeyFile) 326 } 327 328 func TestParseEnhancedFile(t *testing.T) { 329 config, err := parseAndValidate("./testdata/contents.yaml") 330 require.NoError(t, err) 331 require.Equal(t, "contents foo", config.Name) 332 shouldFind := 10 333 require.Len(t, config.Contents, shouldFind) 334 } 335 336 func TestParseEnhancedNestedGlobFile(t *testing.T) { 337 config, err := parseAndValidate("./testdata/contents_glob.yaml") 338 require.NoError(t, err) 339 shouldFind := 5 340 require.Len(t, config.Contents, shouldFind) 341 } 342 343 func TestParseEnhancedNestedNoGlob(t *testing.T) { 344 config, err := parseAndValidate("./testdata/contents_directory.yaml") 345 require.NoError(t, err) 346 shouldFind := 8 347 require.Len(t, config.Contents, shouldFind) 348 tested := 0 349 for _, f := range config.Contents { 350 if f.Type == files.TypeImplicitDir { 351 continue 352 } 353 354 switch f.Source { 355 case "testdata/globtest/nested/b.txt": 356 tested++ 357 require.Equal(t, "/etc/foo/nested/b.txt", f.Destination) 358 case "testdata/globtest/multi-nested/subdir/c.txt": 359 tested++ 360 require.Equal(t, "/etc/foo/multi-nested/subdir/c.txt", f.Destination) 361 case "testdata/globtest/a.txt": 362 tested++ 363 require.Equal(t, "/etc/foo/a.txt", f.Destination) 364 default: 365 t.Errorf("unknown source %q", f.Source) 366 } 367 } 368 require.Equal(t, 3, tested) 369 } 370 371 func TestOptionsFromEnvironment(t *testing.T) { 372 const ( 373 globalPass = "hunter2" 374 debPass = "password123" 375 rpmPass = "secret" 376 apkPass = "foobar" 377 platform = "linux" 378 arch = "amd64" 379 release = "3" 380 version = "1.0.0" 381 vendor = "GoReleaser" 382 packager = "nope" 383 maintainerEmail = "nope@example.com" 384 homepage = "https://nfpm.goreleaser.com" 385 vcsBrowser = "https://github.com/goreleaser/nfpm" 386 description = "barfoo" 387 ) 388 389 t.Run("platform", func(t *testing.T) { 390 t.Setenv("OS", platform) 391 info, err := nfpm.Parse(strings.NewReader("name: foo\nplatform: $OS")) 392 require.NoError(t, err) 393 require.Equal(t, platform, info.Platform) 394 }) 395 396 t.Run("arch", func(t *testing.T) { 397 t.Setenv("ARCH", arch) 398 info, err := nfpm.Parse(strings.NewReader("name: foo\narch: $ARCH")) 399 require.NoError(t, err) 400 require.Equal(t, arch, info.Arch) 401 }) 402 403 t.Run("version", func(t *testing.T) { 404 t.Setenv("VERSION", version) 405 info, err := nfpm.Parse(strings.NewReader("name: foo\nversion: $VERSION")) 406 require.NoError(t, err) 407 require.Equal(t, version, info.Version) 408 }) 409 410 t.Run("release", func(t *testing.T) { 411 t.Setenv("RELEASE", release) 412 info, err := nfpm.Parse(strings.NewReader("name: foo\nrelease: $RELEASE")) 413 require.NoError(t, err) 414 require.Equal(t, release, info.Release) 415 }) 416 417 t.Run("maintainer", func(t *testing.T) { 418 t.Setenv("GIT_COMMITTER_NAME", packager) 419 t.Setenv("GIT_COMMITTER_EMAIL", maintainerEmail) 420 info, err := nfpm.Parse(strings.NewReader(` 421 name: foo 422 maintainer: '"$GIT_COMMITTER_NAME" <$GIT_COMMITTER_EMAIL>' 423 `)) 424 require.NoError(t, err) 425 addr := mail.Address{ 426 Name: packager, 427 Address: maintainerEmail, 428 } 429 require.Equal(t, addr.String(), info.Maintainer) 430 }) 431 432 t.Run("vendor", func(t *testing.T) { 433 t.Setenv("VENDOR", vendor) 434 info, err := nfpm.Parse(strings.NewReader("name: foo\nvendor: $VENDOR")) 435 require.NoError(t, err) 436 require.Equal(t, vendor, info.Vendor) 437 }) 438 439 t.Run("homepage", func(t *testing.T) { 440 t.Setenv("CI_PROJECT_URL", homepage) 441 info, err := nfpm.Parse(strings.NewReader("name: foo\nhomepage: $CI_PROJECT_URL")) 442 require.NoError(t, err) 443 require.Equal(t, homepage, info.Homepage) 444 }) 445 446 t.Run("description", func(t *testing.T) { 447 t.Setenv("DESCRIPTION", description) 448 info, err := nfpm.Parse(strings.NewReader("name: foo\ndescription: $DESCRIPTION")) 449 require.NoError(t, err) 450 require.Equal(t, description, info.Description) 451 }) 452 453 t.Run("global passphrase", func(t *testing.T) { 454 t.Setenv("NFPM_PASSPHRASE", globalPass) 455 info, err := nfpm.Parse(strings.NewReader("name: foo")) 456 require.NoError(t, err) 457 require.Equal(t, globalPass, info.Deb.Signature.KeyPassphrase) 458 require.Equal(t, globalPass, info.RPM.Signature.KeyPassphrase) 459 require.Equal(t, globalPass, info.APK.Signature.KeyPassphrase) 460 }) 461 462 t.Run("specific passphrases", func(t *testing.T) { 463 t.Setenv("NFPM_PASSPHRASE", globalPass) 464 t.Setenv("NFPM_DEB_PASSPHRASE", debPass) 465 t.Setenv("NFPM_RPM_PASSPHRASE", rpmPass) 466 t.Setenv("NFPM_APK_PASSPHRASE", apkPass) 467 info, err := nfpm.Parse(strings.NewReader("name: foo")) 468 require.NoError(t, err) 469 require.Equal(t, debPass, info.Deb.Signature.KeyPassphrase) 470 require.Equal(t, rpmPass, info.RPM.Signature.KeyPassphrase) 471 require.Equal(t, apkPass, info.APK.Signature.KeyPassphrase) 472 }) 473 474 t.Run("packager", func(t *testing.T) { 475 t.Setenv("PACKAGER", packager) 476 info, err := nfpm.Parse(strings.NewReader("name: foo\nrpm:\n packager: $PACKAGER")) 477 require.NoError(t, err) 478 require.Equal(t, packager, info.RPM.Packager) 479 }) 480 481 t.Run("depends", func(t *testing.T) { 482 t.Setenv("VERSION", version) 483 info, err := nfpm.Parse(strings.NewReader(`--- 484 name: foo 485 overrides: 486 deb: 487 depends: 488 - package (= ${VERSION}) 489 rpm: 490 depends: 491 - package = ${VERSION}`)) 492 require.NoError(t, err) 493 require.Len(t, info.Overrides["deb"].Depends, 1) 494 require.Equal(t, "package (= 1.0.0)", info.Overrides["deb"].Depends[0]) 495 require.Len(t, info.Overrides["rpm"].Depends, 1) 496 require.Equal(t, "package = 1.0.0", info.Overrides["rpm"].Depends[0]) 497 }) 498 499 t.Run("depends-strips-empty", func(t *testing.T) { 500 t.Setenv("VERSION", version) 501 t.Setenv("PKG", "") 502 info, err := nfpm.Parse(strings.NewReader(`--- 503 name: foo 504 overrides: 505 deb: 506 depends: 507 - ${PKG} 508 - package (= ${VERSION}) 509 - ${PKG} 510 - ${PKG} 511 rpm: 512 depends: 513 - package = ${VERSION} 514 - ${PKG}`)) 515 require.NoError(t, err) 516 require.Len(t, info.Overrides["deb"].Depends, 1) 517 require.Equal(t, "package (= 1.0.0)", info.Overrides["deb"].Depends[0]) 518 require.Len(t, info.Overrides["rpm"].Depends, 1) 519 require.Equal(t, "package = 1.0.0", info.Overrides["rpm"].Depends[0]) 520 }) 521 522 t.Run("deb fields", func(t *testing.T) { 523 t.Setenv("CI_PROJECT_URL", vcsBrowser) 524 info, err := nfpm.Parse(strings.NewReader(` 525 name: foo 526 deb: 527 fields: 528 Vcs-Browser: ${CI_PROJECT_URL} 529 `)) 530 require.NoError(t, err) 531 require.Equal(t, vcsBrowser, info.Deb.Fields["Vcs-Browser"]) 532 }) 533 534 t.Run("contents", func(t *testing.T) { 535 t.Setenv("ARCH", "amd64") 536 t.Setenv("NAME", "foo") 537 info, err := nfpm.Parse(strings.NewReader(` 538 name: foo 539 contents: 540 - src: '${NAME}_${ARCH}' 541 dst: /usr/bin/${NAME} 542 expand: true 543 - src: '${NAME}' 544 dst: /usr/bin/bar 545 546 overrides: 547 deb: 548 contents: 549 - src: '${NAME}_${ARCH}' 550 dst: /debian/usr/bin/${NAME} 551 expand: true 552 `)) 553 require.NoError(t, err) 554 require.Equal(t, 2, info.Contents.Len()) 555 content1 := info.Contents[0] 556 require.Equal(t, "/usr/bin/foo", content1.Destination) 557 require.Equal(t, "foo_amd64", content1.Source) 558 content2 := info.Contents[1] 559 require.Equal(t, "/usr/bin/bar", content2.Destination) 560 require.Equal(t, "${NAME}", content2.Source) 561 content3 := info.Overrides["deb"].Contents[0] 562 require.Equal(t, "/debian/usr/bin/foo", content3.Destination) 563 require.Equal(t, "foo_amd64", content3.Source) 564 }) 565 } 566 567 func TestOverrides(t *testing.T) { 568 nfpm.RegisterPackager("deb", &fakePackager{}) 569 nfpm.RegisterPackager("rpm", &fakePackager{}) 570 nfpm.RegisterPackager("apk", &fakePackager{}) 571 572 file := "./testdata/overrides.yaml" 573 config, err := nfpm.ParseFile(file) 574 require.NoError(t, err) 575 require.Equal(t, "foo", config.Name) 576 require.Equal(t, "amd64", config.Arch) 577 578 for _, format := range []string{"apk", "deb", "rpm"} { 579 format := format 580 t.Run(format, func(t *testing.T) { 581 pkg, err := config.Get(format) 582 require.NoError(t, err) 583 require.Equal(t, []string{format + "_depend"}, pkg.Depends) 584 for _, f := range pkg.Contents { 585 switch f.Packager { 586 case format: 587 require.Contains(t, f.Destination, "/"+format) 588 case "": 589 require.Equal(t, "/etc/foo/whatever.conf", f.Destination) 590 default: 591 t.Fatalf("invalid packager: %s", f.Packager) 592 } 593 } 594 require.Equal(t, "amd64", pkg.Arch) 595 require.Equal(t, time.Date(2023, 0o1, 0o2, 0, 0, 0, 0, time.UTC), pkg.MTime) 596 }) 597 } 598 599 t.Run("no_overrides", func(t *testing.T) { 600 pkg, err := config.Get("doesnotexist") 601 require.NoError(t, err) 602 require.Empty(t, pkg.Depends) 603 }) 604 } 605 606 type fakePackager struct{} 607 608 func (*fakePackager) ConventionalFileName(_ *nfpm.Info) string { 609 return "" 610 } 611 612 func (*fakePackager) Package(_ *nfpm.Info, _ io.Writer) error { 613 return nil 614 }