github.com/goreleaser/nfpm/v2@v2.44.0/arch/arch_test.go (about) 1 package arch 2 3 import ( 4 "archive/tar" 5 "bytes" 6 "fmt" 7 "io" 8 "os" 9 "strings" 10 "testing" 11 "time" 12 13 "github.com/goreleaser/nfpm/v2" 14 "github.com/goreleaser/nfpm/v2/files" 15 "github.com/klauspost/compress/zstd" 16 "github.com/klauspost/pgzip" 17 "github.com/stretchr/testify/require" 18 ) 19 20 var mtime = time.Date(2023, 11, 5, 23, 15, 17, 0, time.UTC) 21 22 func exampleInfo() *nfpm.Info { 23 return nfpm.WithDefaults(&nfpm.Info{ 24 Name: "foo-test", 25 Arch: "amd64", 26 Description: "Foo does things", 27 Priority: "extra", 28 Maintainer: "Carlos A Becker <pkg@carlosbecker.com>", 29 Version: "1.0.0", 30 Prerelease: "beta-1", 31 Section: "default", 32 Homepage: "http://carlosbecker.com", 33 Vendor: "nope", 34 License: "MIT", 35 Overridables: nfpm.Overridables{ 36 Depends: []string{ 37 "bash", 38 }, 39 Replaces: []string{ 40 "svn", 41 }, 42 Provides: []string{ 43 "bzr", 44 }, 45 Conflicts: []string{ 46 "zsh", 47 }, 48 Contents: []*files.Content{ 49 { 50 Source: "../testdata/fake", 51 Destination: "/usr/bin/fake", 52 }, 53 { 54 Source: "../testdata/whatever.conf", 55 Destination: "/etc/fake/fake.conf", 56 Type: files.TypeConfig, 57 }, 58 { 59 Destination: "/var/log/whatever", 60 Type: files.TypeDir, 61 }, 62 { 63 Destination: "/usr/share/whatever", 64 Type: files.TypeDir, 65 }, 66 { 67 Source: "/etc/fake/fake.conf", 68 Destination: "/etc/fake/fake-link.conf", 69 Type: files.TypeSymlink, 70 }, 71 { 72 Source: "../testdata/something", 73 Destination: "/etc/something", 74 }, 75 }, 76 Scripts: nfpm.Scripts{ 77 PreInstall: "../testdata/scripts/preinstall.sh", 78 PostInstall: "../testdata/scripts/postinstall.sh", 79 PreRemove: "../testdata/scripts/preremove.sh", 80 PostRemove: "../testdata/scripts/postremove.sh", 81 }, 82 ArchLinux: nfpm.ArchLinux{ 83 Scripts: nfpm.ArchLinuxScripts{ 84 PreUpgrade: "../testdata/scripts/preupgrade.sh", 85 PostUpgrade: "../testdata/scripts/postupgrade.sh", 86 }, 87 }, 88 }, 89 }) 90 } 91 92 func TestConventionalExtension(t *testing.T) { 93 require.Equal(t, ".pkg.tar.zst", Default.ConventionalExtension()) 94 } 95 96 func TestArch(t *testing.T) { 97 for _, arch := range []string{"386", "amd64", "arm64"} { 98 arch := arch 99 t.Run(arch, func(t *testing.T) { 100 info := exampleInfo() 101 info.Arch = arch 102 err := Default.Package(info, io.Discard) 103 require.NoError(t, err) 104 }) 105 } 106 } 107 108 func TestArchPlatform(t *testing.T) { 109 f, err := os.CreateTemp(t.TempDir(), "test*.pkg.tar.zstd") 110 require.NoError(t, err) 111 t.Cleanup(func() { require.NoError(t, f.Close()) }) 112 info := exampleInfo() 113 info.Platform = "darwin" 114 err = Default.Package(info, f) 115 require.Error(t, err) 116 } 117 118 func TestArchNoFiles(t *testing.T) { 119 info := exampleInfo() 120 info.Contents = nil 121 info.Scripts = nfpm.Scripts{} 122 info.ArchLinux = nfpm.ArchLinux{} 123 err := Default.Package(info, io.Discard) 124 require.NoError(t, err) 125 } 126 127 func TestArchNoInfo(t *testing.T) { 128 err := Default.Package(nfpm.WithDefaults(&nfpm.Info{}), io.Discard) 129 require.Error(t, err) 130 } 131 132 func TestArchConventionalFileName(t *testing.T) { 133 for _, arch := range []string{"386", "amd64", "arm64"} { 134 arch := arch 135 t.Run(arch, func(t *testing.T) { 136 info := exampleInfo() 137 info.Arch = arch 138 name := Default.ConventionalFileName(info) 139 require.Equal(t, 140 "foo-test-1.0.0beta_1-1-"+archToArchLinux[arch]+".pkg.tar.zst", 141 name, 142 ) 143 }) 144 } 145 } 146 147 func TestArchPkginfo(t *testing.T) { 148 info := exampleInfo() 149 pkginfoData, err := makeTestPkginfo(t, info) 150 require.NoError(t, err) 151 fields := extractPkginfoFields(pkginfoData) 152 require.Equal(t, "foo-test", fields["pkgname"]) 153 require.Equal(t, "foo-test", fields["pkgbase"]) 154 require.Equal(t, "1.0.0-1", fields["pkgver"]) 155 require.Equal(t, "Foo does things", fields["pkgdesc"]) 156 require.Equal(t, "http://carlosbecker.com", fields["url"]) 157 require.Equal(t, "Unknown Packager", fields["packager"]) 158 require.Equal(t, "x86_64", fields["arch"]) 159 require.Equal(t, "MIT", fields["license"]) 160 require.Equal(t, "1234", fields["size"]) 161 require.Equal(t, "svn", fields["replaces"]) 162 require.Equal(t, "zsh", fields["conflict"]) 163 require.Equal(t, "bzr", fields["provides"]) 164 require.Equal(t, "bash", fields["depend"]) 165 require.Equal(t, "etc/fake/fake.conf", fields["backup"]) 166 } 167 168 func TestArchPkgbase(t *testing.T) { 169 info := exampleInfo() 170 info.ArchLinux.Pkgbase = "foo" 171 pkginfoData, err := makeTestPkginfo(t, info) 172 require.NoError(t, err) 173 fields := extractPkginfoFields(pkginfoData) 174 require.Equal(t, "foo", fields["pkgbase"]) 175 } 176 177 func TestArchInvalidName(t *testing.T) { 178 info := exampleInfo() 179 info.Name = "#" 180 _, err := makeTestPkginfo(t, info) 181 require.ErrorIs(t, err, ErrInvalidPkgName) 182 } 183 184 func TestArchVersionWithRelease(t *testing.T) { 185 info := exampleInfo() 186 info.Version = "0.0.1" 187 info.Release = "4" 188 pkginfoData, err := makeTestPkginfo(t, info) 189 require.NoError(t, err) 190 fields := extractPkginfoFields(pkginfoData) 191 require.Equal(t, "0.0.1-4", fields["pkgver"]) 192 } 193 194 func TestArchVersionWithEpoch(t *testing.T) { 195 info := exampleInfo() 196 info.Version = "0.0.1" 197 info.Epoch = "2" 198 pkginfoData, err := makeTestPkginfo(t, info) 199 require.NoError(t, err) 200 fields := extractPkginfoFields(pkginfoData) 201 require.Equal(t, "2:0.0.1beta_1-1", fields["pkgver"]) 202 } 203 204 func TestArchOverrideArchitecture(t *testing.T) { 205 info := exampleInfo() 206 info.ArchLinux.Arch = "randomarch" 207 pkginfoData, err := makeTestPkginfo(t, info) 208 require.NoError(t, err) 209 fields := extractPkginfoFields(pkginfoData) 210 require.Equal(t, "randomarch", fields["arch"]) 211 } 212 213 func makeTestPkginfo(t *testing.T, info *nfpm.Info) ([]byte, error) { 214 t.Helper() 215 216 require.NoError(t, nfpm.PrepareForPackager(info, packagerName)) 217 218 buf := &bytes.Buffer{} 219 tw := tar.NewWriter(buf) 220 221 entry, err := createPkginfo(info, tw, 1234) 222 if err != nil { 223 return nil, err 224 } 225 226 tw.Close() 227 228 tr := tar.NewReader(buf) 229 _, err = tr.Next() 230 require.NoError(t, err) 231 232 pkginfoData := make([]byte, entry.Size) 233 _, err = io.ReadFull(tr, pkginfoData) 234 if err != nil { 235 return nil, err 236 } 237 238 return pkginfoData, nil 239 } 240 241 func extractPkginfoFields(data []byte) map[string]string { 242 strData := string(data) 243 strData = strings.TrimPrefix(strData, "# Generated by nfpm\n") 244 strData = strings.TrimSpace(strData) 245 246 splitData := strings.Split(strData, "\n") 247 out := map[string]string{} 248 249 for _, kvPair := range splitData { 250 splitPair := strings.Split(kvPair, " = ") 251 out[splitPair[0]] = splitPair[1] 252 } 253 254 return out 255 } 256 257 const correctMtree = `#mtree 258 ./foo/bar time=1234.0 mode=755 type=dir 259 ./foo/bar/file time=1234.0 mode=600 size=143 type=file md5digest=abcd sha256digest=ef12 260 ./3 time=12345.0 mode=644 size=100 type=file md5digest=abcd sha256digest=ef12 261 ./sh time=123456.0 mode=777 type=link link=/bin/bash 262 ` 263 264 func TestArchMtree(t *testing.T) { 265 info := exampleInfo() 266 require.NoError(t, nfpm.PrepareForPackager(info, packagerName)) 267 268 buf := &bytes.Buffer{} 269 tw := tar.NewWriter(buf) 270 271 err := createMtree(tw, []MtreeEntry{ 272 { 273 Destination: "foo/bar", 274 Time: 1234, 275 Type: files.TypeDir, 276 Mode: 0o755, 277 }, 278 { 279 Destination: "foo/bar/file", 280 Time: 1234, 281 Type: files.TypeFile, 282 Mode: 0o600, 283 Size: 143, 284 MD5: []byte{0xAB, 0xCD}, 285 SHA256: []byte{0xEF, 0x12}, 286 }, 287 { 288 Destination: "3", 289 Time: 12345, 290 Mode: 0o644, 291 Size: 100, 292 Type: files.TypeFile, 293 MD5: []byte{0xAB, 0xCD}, 294 SHA256: []byte{0xEF, 0x12}, 295 }, 296 { 297 LinkSource: "/bin/bash", 298 Destination: "sh", 299 Time: 123456, 300 Mode: 0o777, 301 Type: files.TypeSymlink, 302 }, 303 }, mtime) 304 require.NoError(t, err) 305 306 tw.Close() 307 308 tr := tar.NewReader(buf) 309 _, err = tr.Next() 310 require.NoError(t, err) 311 312 gr, err := pgzip.NewReader(tr) 313 require.NoError(t, err) 314 defer gr.Close() 315 316 mtree, err := io.ReadAll(gr) 317 require.NoError(t, err) 318 319 require.Equal(t, correctMtree, string(mtree)) 320 } 321 322 func TestGlob(t *testing.T) { 323 var pkg bytes.Buffer 324 require.NoError(t, Default.Package(nfpm.WithDefaults(&nfpm.Info{ 325 Name: "nfpm-repro", 326 Version: "1.0.0", 327 Maintainer: "asdfasdf", 328 MTime: mtime, 329 330 Overridables: nfpm.Overridables{ 331 Contents: files.Contents{ 332 { 333 Destination: "/usr/share/nfpm-repro", 334 Source: "../files/testdata/globtest/different-sizes/*/*.txt", 335 FileInfo: &files.ContentFileInfo{ 336 Mode: 0o644, 337 MTime: mtime, 338 }, 339 }, 340 }, 341 }, 342 }), &pkg)) 343 344 pkgZstd, err := zstd.NewReader(&pkg) 345 require.NoError(t, err) 346 t.Cleanup(func() { pkgZstd.Close() }) 347 pkgTar := tar.NewReader(pkgZstd) 348 for { 349 f, err := pkgTar.Next() 350 if err == io.EOF || f == nil { 351 break 352 } 353 354 if f.Name == ".MTREE" { 355 break 356 } 357 } 358 359 mtreeTarBts, err := io.ReadAll(pkgTar) 360 require.NoError(t, err) 361 362 mtreeGzip, err := pgzip.NewReader(bytes.NewReader(mtreeTarBts)) 363 require.NoError(t, err) 364 t.Cleanup(func() { require.NoError(t, mtreeGzip.Close()) }) 365 366 mtreeContentBts, err := io.ReadAll(mtreeGzip) 367 require.NoError(t, err) 368 369 expectedTime := fmt.Sprintf("time=%d.0", mtime.Unix()) 370 expected := map[string][]string{ 371 "./.PKGINFO": {expectedTime, "mode=644", "size=185", "type=file", "md5digest=408daafbd01f6622f0bfd6ccdf96735f", "sha256digest=98468a4b87a677958f872662f476b14ff28cc1f8c6bd0029869e21946b4cd8d2"}, 372 "./usr/": {expectedTime, "mode=755", "type=dir"}, 373 "./usr/share/": {expectedTime, "mode=755", "type=dir"}, 374 "./usr/share/nfpm-repro/": {expectedTime, "mode=755", "type=dir"}, 375 "./usr/share/nfpm-repro/a/": {expectedTime, "mode=755", "type=dir"}, 376 "./usr/share/nfpm-repro/a/a.txt": {expectedTime, "mode=644", "size=4", "type=file", "md5digest=d3b07384d113edec49eaa6238ad5ff00", "sha256digest=b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"}, 377 "./usr/share/nfpm-repro/b/": {expectedTime, "mode=755", "type=dir"}, 378 "./usr/share/nfpm-repro/b/b.txt": {expectedTime, "mode=644", "size=7", "type=file", "md5digest=551a67cc6e06de1910061fe318d28f72", "sha256digest=73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f"}, 379 } 380 381 for _, line := range strings.Split(string(mtreeContentBts), "\n") { 382 if line == "#mtree" || line == "" { 383 continue 384 } 385 parts := strings.Fields(line) 386 filename := parts[0] 387 expect := expected[filename] 388 require.Equal(t, expect, strings.Split(line, " ")[1:], filename) 389 } 390 }