github.com/goreleaser/nfpm/v2@v2.44.0/apk/apk_test.go (about)

     1  package apk
     2  
     3  import (
     4  	"archive/tar"
     5  	"bytes"
     6  	"compress/gzip"
     7  	"crypto/sha1" // nolint:gosec
     8  	"crypto/sha256"
     9  	"errors"
    10  	"flag"
    11  	"fmt"
    12  	"io"
    13  	"os"
    14  	"path"
    15  	"path/filepath"
    16  	"testing"
    17  
    18  	"github.com/goreleaser/nfpm/v2"
    19  	"github.com/goreleaser/nfpm/v2/files"
    20  	"github.com/goreleaser/nfpm/v2/internal/sign"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  // nolint: gochecknoglobals
    25  var update = flag.Bool("update", false, "update apk .golden files")
    26  
    27  func exampleInfo() *nfpm.Info {
    28  	return nfpm.WithDefaults(&nfpm.Info{
    29  		Name:        "foo",
    30  		Arch:        "amd64",
    31  		Description: "Foo does things",
    32  		Priority:    "extra",
    33  		Maintainer:  "Carlos A Becker <pkg@carlosbecker.com>",
    34  		Version:     "v1.0.0",
    35  		Prerelease:  "beta1",
    36  		Release:     "r1",
    37  		Section:     "default",
    38  		Homepage:    "http://carlosbecker.com",
    39  		Vendor:      "nope",
    40  		Overridables: nfpm.Overridables{
    41  			Depends: []string{
    42  				"bash",
    43  				"foo",
    44  			},
    45  			Recommends: []string{
    46  				"git",
    47  				"bar",
    48  			},
    49  			Suggests: []string{
    50  				"bash",
    51  				"lala",
    52  			},
    53  			Replaces: []string{
    54  				"svn",
    55  				"subversion",
    56  			},
    57  			Provides: []string{
    58  				"bzr",
    59  				"zzz",
    60  			},
    61  			Conflicts: []string{
    62  				"zsh",
    63  				"foobarsh",
    64  			},
    65  			Contents: []*files.Content{
    66  				{
    67  					Source:      "../testdata/fake",
    68  					Destination: "/usr/bin/fake",
    69  				},
    70  				{
    71  					Source:      "../testdata/whatever.conf",
    72  					Destination: "/usr/share/doc/fake/fake.txt",
    73  				},
    74  				{
    75  					Source:      "../testdata/whatever.conf",
    76  					Destination: "/etc/fake/fake.conf",
    77  					Type:        files.TypeConfig,
    78  				},
    79  				{
    80  					Source:      "../testdata/whatever.conf",
    81  					Destination: "/etc/fake/fake2.conf",
    82  					Type:        files.TypeConfigNoReplace,
    83  				},
    84  				{
    85  					Source:      "../testdata/whatever.conf",
    86  					Destination: "/etc/fake/fake3.conf",
    87  					Type:        files.TypeConfigMissingOK,
    88  				},
    89  				{
    90  					Destination: "/var/log/whatever",
    91  					Type:        files.TypeDir,
    92  				},
    93  				{
    94  					Destination: "/usr/share/whatever",
    95  					Type:        files.TypeDir,
    96  				},
    97  			},
    98  		},
    99  	})
   100  }
   101  
   102  func TestConventionalExtension(t *testing.T) {
   103  	require.Equal(t, ".apk", Default.ConventionalExtension())
   104  }
   105  
   106  func TestCreateBuilderData(t *testing.T) {
   107  	info := exampleInfo()
   108  	require.NoError(t, nfpm.PrepareForPackager(info, "apk"))
   109  	size := int64(0)
   110  	builderData := createBuilderData(info, &size)
   111  
   112  	var buf bytes.Buffer
   113  	tw := tar.NewWriter(&buf)
   114  
   115  	require.NoError(t, builderData(tw))
   116  
   117  	require.Equal(t, 15872, buf.Len(), buf.String())
   118  }
   119  
   120  func TestCombineToApk(t *testing.T) {
   121  	var bufData bytes.Buffer
   122  	bufData.Write([]byte{1})
   123  
   124  	var bufControl bytes.Buffer
   125  	bufControl.Write([]byte{2})
   126  
   127  	var bufTarget bytes.Buffer
   128  
   129  	require.NoError(t, combineToApk(&bufTarget, &bufData, &bufControl))
   130  	require.Equal(t, 2, bufTarget.Len())
   131  }
   132  
   133  func TestDefaultWithArch(t *testing.T) {
   134  	expectedChecksums := map[string]string{
   135  		"usr/share/doc/fake/fake.txt": "96c335dc28122b5f09a4cef74b156cd24c23784c",
   136  		"usr/bin/fake":                "f46cece3eeb7d9ed5cb244d902775427be71492d",
   137  		"etc/fake/fake.conf":          "96c335dc28122b5f09a4cef74b156cd24c23784c",
   138  		"etc/fake/fake2.conf":         "96c335dc28122b5f09a4cef74b156cd24c23784c",
   139  		"etc/fake/fake3.conf":         "96c335dc28122b5f09a4cef74b156cd24c23784c",
   140  	}
   141  	for _, arch := range []string{"386", "amd64"} {
   142  		arch := arch
   143  		t.Run(arch, func(t *testing.T) {
   144  			info := exampleInfo()
   145  			info.Arch = arch
   146  
   147  			var f bytes.Buffer
   148  			require.NoError(t, Default.Package(info, &f))
   149  
   150  			gz, err := gzip.NewReader(&f)
   151  			require.NoError(t, err)
   152  			defer gz.Close()
   153  			tr := tar.NewReader(gz)
   154  
   155  			for {
   156  				hdr, err := tr.Next()
   157  				if errors.Is(err, io.EOF) {
   158  					break // End of archive
   159  				}
   160  				require.NoError(t, err)
   161  
   162  				require.Equal(t, expectedChecksums[hdr.Name], hdr.PAXRecords["APK-TOOLS.checksum.SHA1"], hdr.Name)
   163  			}
   164  		})
   165  	}
   166  }
   167  
   168  func TestApkPlatform(t *testing.T) {
   169  	f, err := os.CreateTemp(t.TempDir(), "test*.apk")
   170  	require.NoError(t, err)
   171  	t.Cleanup(func() { require.NoError(t, f.Close()) })
   172  	info := exampleInfo()
   173  	info.Platform = "darwin"
   174  	err = Default.Package(info, f)
   175  	require.Error(t, err)
   176  }
   177  
   178  func TestNoInfo(t *testing.T) {
   179  	err := Default.Package(nfpm.WithDefaults(&nfpm.Info{}), io.Discard)
   180  	require.Error(t, err)
   181  }
   182  
   183  func TestFileDoesNotExist(t *testing.T) {
   184  	abs, err := filepath.Abs("../testdata/whatever.confzzz")
   185  	require.NoError(t, err)
   186  	err = Default.Package(
   187  		nfpm.WithDefaults(&nfpm.Info{
   188  			Name:        "foo",
   189  			Arch:        "amd64",
   190  			Description: "Foo does things",
   191  			Priority:    "extra",
   192  			Maintainer:  "Carlos A Becker <pkg@carlosbecker.com>",
   193  			Version:     "1.0.0",
   194  			Section:     "default",
   195  			Homepage:    "http://carlosbecker.com",
   196  			Vendor:      "nope",
   197  			Overridables: nfpm.Overridables{
   198  				Depends: []string{
   199  					"bash",
   200  				},
   201  				Contents: []*files.Content{
   202  					{
   203  						Source:      "../testdata/fake",
   204  						Destination: "/usr/bin/fake",
   205  					},
   206  					{
   207  						Source:      "../testdata/whatever.confzzz",
   208  						Destination: "/etc/fake/fake.conf",
   209  						Type:        files.TypeConfig,
   210  					},
   211  				},
   212  			},
   213  		}),
   214  		io.Discard,
   215  	)
   216  	require.EqualError(t, err, fmt.Sprintf("matching \"%s\": file does not exist", filepath.ToSlash(abs)))
   217  }
   218  
   219  func TestNoFiles(t *testing.T) {
   220  	err := Default.Package(
   221  		nfpm.WithDefaults(&nfpm.Info{
   222  			Name:        "foo",
   223  			Arch:        "amd64",
   224  			Description: "Foo does things",
   225  			Priority:    "extra",
   226  			Maintainer:  "Carlos A Becker <pkg@carlosbecker.com>",
   227  			Version:     "1.0.0",
   228  			Section:     "default",
   229  			Homepage:    "http://carlosbecker.com",
   230  			Vendor:      "nope",
   231  			Overridables: nfpm.Overridables{
   232  				Depends: []string{
   233  					"bash",
   234  				},
   235  			},
   236  		}),
   237  		io.Discard,
   238  	)
   239  	require.NoError(t, err)
   240  }
   241  
   242  func TestCreateBuilderControl(t *testing.T) {
   243  	info := exampleInfo()
   244  	size := int64(12345)
   245  	err := nfpm.PrepareForPackager(info, "apk")
   246  	require.NoError(t, err)
   247  	builderControl := createBuilderControl(info, size, sha256.New().Sum(nil))
   248  
   249  	var w bytes.Buffer
   250  	tw := tar.NewWriter(&w)
   251  	require.NoError(t, builderControl(tw))
   252  
   253  	control := string(extractFromTar(t, w.Bytes(), ".PKGINFO"))
   254  	golden := "testdata/TestCreateBuilderControl.golden"
   255  	if *update {
   256  		require.NoError(t, os.WriteFile(golden, []byte(control), 0o655)) // nolint: gosec
   257  	}
   258  	bts, err := os.ReadFile(golden) //nolint:gosec
   259  	require.NoError(t, err)
   260  	require.Equal(t, string(bts), control)
   261  }
   262  
   263  func TestCreateBuilderControlScripts(t *testing.T) {
   264  	info := exampleInfo()
   265  	info.Scripts = nfpm.Scripts{
   266  		PreInstall:  "../testdata/scripts/preinstall.sh",
   267  		PostInstall: "../testdata/scripts/postinstall.sh",
   268  		PreRemove:   "../testdata/scripts/preremove.sh",
   269  		PostRemove:  "../testdata/scripts/postremove.sh",
   270  	}
   271  	info.APK.Scripts = nfpm.APKScripts{
   272  		PreUpgrade:  "../testdata/scripts/preupgrade.sh",
   273  		PostUpgrade: "../testdata/scripts/postupgrade.sh",
   274  	}
   275  	err := nfpm.PrepareForPackager(info, "apk")
   276  	require.NoError(t, err)
   277  
   278  	size := int64(12345)
   279  	builderControl := createBuilderControl(info, size, sha256.New().Sum(nil))
   280  
   281  	var w bytes.Buffer
   282  	tw := tar.NewWriter(&w)
   283  	require.NoError(t, builderControl(tw))
   284  
   285  	control := string(extractFromTar(t, w.Bytes(), ".PKGINFO"))
   286  	golden := "testdata/TestCreateBuilderControlScripts.golden"
   287  	if *update {
   288  		require.NoError(t, os.WriteFile(golden, []byte(control), 0o655)) // nolint: gosec
   289  	}
   290  	bts, err := os.ReadFile(golden) //nolint:gosec
   291  	require.NoError(t, err)
   292  	require.Equal(t, string(bts), control)
   293  
   294  	// Validate scripts are correct
   295  	script := string(extractFromTar(t, w.Bytes(), ".pre-install"))
   296  	require.Contains(t, script, `echo "Preinstall" > /dev/null`)
   297  	script = string(extractFromTar(t, w.Bytes(), ".post-install"))
   298  	require.Contains(t, script, `echo "Postinstall" > /dev/null`)
   299  	script = string(extractFromTar(t, w.Bytes(), ".pre-upgrade"))
   300  	require.Contains(t, script, `echo "PreUpgrade" > /dev/null`)
   301  	script = string(extractFromTar(t, w.Bytes(), ".post-upgrade"))
   302  	require.Contains(t, script, `echo "PostUpgrade" > /dev/null`)
   303  	script = string(extractFromTar(t, w.Bytes(), ".pre-deinstall"))
   304  	require.Contains(t, script, `echo "Preremove" > /dev/null`)
   305  	script = string(extractFromTar(t, w.Bytes(), ".post-deinstall"))
   306  	require.Contains(t, script, `echo "Postremove" > /dev/null`)
   307  }
   308  
   309  func TestControl(t *testing.T) {
   310  	var w bytes.Buffer
   311  	require.NoError(t, writeControl(&w, controlData{
   312  		Info:          exampleInfo(),
   313  		InstalledSize: 10,
   314  	}))
   315  	golden := "testdata/TestControl.golden"
   316  	if *update {
   317  		require.NoError(t, os.WriteFile(golden, w.Bytes(), 0o655)) // nolint: gosec
   318  	}
   319  	bts, err := os.ReadFile(golden) //nolint:gosec
   320  	require.NoError(t, err)
   321  	require.Equal(t, string(bts), w.String())
   322  }
   323  
   324  func TestSignatureName(t *testing.T) {
   325  	info := exampleInfo()
   326  	info.APK.Signature.KeyFile = "../internal/sign/testdata/rsa.priv"
   327  	info.APK.Signature.KeyName = "testkey"
   328  	info.APK.Signature.KeyPassphrase = "hunter2"
   329  	err := nfpm.PrepareForPackager(info, "apk")
   330  	require.NoError(t, err)
   331  
   332  	digest := sha1.New().Sum(nil) // nolint:gosec
   333  
   334  	var signatureTarGz bytes.Buffer
   335  	tw := tar.NewWriter(&signatureTarGz)
   336  	require.NoError(t, createSignatureBuilder(digest, info)(tw))
   337  
   338  	signature := extractFromTar(t, signatureTarGz.Bytes(), ".SIGN.RSA.testkey.rsa.pub")
   339  	err = sign.RSAVerifySHA1Digest(digest, signature, "../internal/sign/testdata/rsa.pub")
   340  	require.NoError(t, err)
   341  
   342  	err = Default.Package(info, io.Discard)
   343  	require.NoError(t, err)
   344  }
   345  
   346  func TestSignature(t *testing.T) {
   347  	info := exampleInfo()
   348  	info.APK.Signature.KeyFile = "../internal/sign/testdata/rsa.priv"
   349  	info.APK.Signature.KeyName = "testkey.rsa.pub"
   350  	info.APK.Signature.KeyPassphrase = "hunter2"
   351  	err := nfpm.PrepareForPackager(info, "apk")
   352  	require.NoError(t, err)
   353  
   354  	digest := sha1.New().Sum(nil) // nolint:gosec
   355  
   356  	var signatureTarGz bytes.Buffer
   357  	tw := tar.NewWriter(&signatureTarGz)
   358  	require.NoError(t, createSignatureBuilder(digest, info)(tw))
   359  
   360  	signature := extractFromTar(t, signatureTarGz.Bytes(), ".SIGN.RSA.testkey.rsa.pub")
   361  	err = sign.RSAVerifySHA1Digest(digest, signature, "../internal/sign/testdata/rsa.pub")
   362  	require.NoError(t, err)
   363  
   364  	err = Default.Package(info, io.Discard)
   365  	require.NoError(t, err)
   366  }
   367  
   368  func TestSignatureError(t *testing.T) {
   369  	info := exampleInfo()
   370  	info.APK.Signature.KeyFile = "../internal/sign/testdata/rsa.priv"
   371  	info.APK.Signature.KeyName = "testkey.rsa.pub"
   372  	info.APK.Signature.KeyPassphrase = "hunter2"
   373  	err := nfpm.PrepareForPackager(info, "apk")
   374  	require.NoError(t, err)
   375  
   376  	// wrong hash format
   377  	digest := sha256.New().Sum(nil)
   378  
   379  	var signatureTarGz bytes.Buffer
   380  
   381  	err = createSignature(&signatureTarGz, info, digest)
   382  	require.Error(t, err)
   383  
   384  	var expectedError *nfpm.ErrSigningFailure
   385  	require.ErrorAs(t, err, &expectedError)
   386  
   387  	info.APK.Signature.KeyName = ""
   388  	info.Maintainer = ""
   389  	digest = sha1.New().Sum(nil) // nolint:gosec
   390  	err = createSignature(&signatureTarGz, info, digest)
   391  	require.ErrorAs(t, err, &expectedError)
   392  }
   393  
   394  func TestSignatureCallback(t *testing.T) {
   395  	info := exampleInfo()
   396  	info.APK.Signature.SignFn = func(r io.Reader) ([]byte, error) {
   397  		digest, err := io.ReadAll(r)
   398  		if err != nil {
   399  			return nil, err
   400  		}
   401  		return sign.RSASignSHA1Digest(digest, "../internal/sign/testdata/rsa.priv", "hunter2")
   402  	}
   403  	info.APK.Signature.KeyName = "testkey.rsa.pub"
   404  	err := nfpm.PrepareForPackager(info, "apk")
   405  	require.NoError(t, err)
   406  
   407  	digest := sha1.New().Sum(nil) // nolint:gosec
   408  
   409  	var signatureTarGz bytes.Buffer
   410  	tw := tar.NewWriter(&signatureTarGz)
   411  	require.NoError(t, createSignatureBuilder(digest, info)(tw))
   412  
   413  	signature := extractFromTar(t, signatureTarGz.Bytes(), ".SIGN.RSA.testkey.rsa.pub")
   414  	err = sign.RSAVerifySHA1Digest(digest, signature, "../internal/sign/testdata/rsa.pub")
   415  	require.NoError(t, err)
   416  
   417  	err = Default.Package(info, io.Discard)
   418  	require.NoError(t, err)
   419  }
   420  
   421  func TestDisableGlobbing(t *testing.T) {
   422  	info := exampleInfo()
   423  	info.DisableGlobbing = true
   424  	info.Contents = []*files.Content{
   425  		{
   426  			Source:      "../testdata/{file}[",
   427  			Destination: "/test/{file}[",
   428  		},
   429  	}
   430  	err := nfpm.PrepareForPackager(info, "apk")
   431  	require.NoError(t, err)
   432  
   433  	size := int64(0)
   434  	var dataTarGz bytes.Buffer
   435  	_, err = createData(&dataTarGz, info, &size)
   436  	require.NoError(t, err)
   437  
   438  	gzr, err := gzip.NewReader(&dataTarGz)
   439  	require.NoError(t, err)
   440  	dataTar, err := io.ReadAll(gzr)
   441  	require.NoError(t, err)
   442  
   443  	extractedContent := extractFromTar(t, dataTar, "test/{file}[")
   444  	actualContent, err := os.ReadFile("../testdata/{file}[")
   445  	require.NoError(t, err)
   446  	require.Equal(t, actualContent, extractedContent)
   447  }
   448  
   449  func extractFromTar(t *testing.T, tarFile []byte, fileName string) []byte {
   450  	t.Helper()
   451  
   452  	tr := tar.NewReader(bytes.NewReader(tarFile))
   453  
   454  	for {
   455  		hdr, err := tr.Next()
   456  		if errors.Is(err, io.EOF) {
   457  			break
   458  		}
   459  		require.NoError(t, err)
   460  
   461  		if hdr.Name != fileName {
   462  			continue
   463  		}
   464  
   465  		data, err := io.ReadAll(tr)
   466  		require.NoError(t, err)
   467  		return data
   468  	}
   469  
   470  	t.Fatalf("file %q not found in tar file", fileName)
   471  	return nil
   472  }
   473  
   474  func tarContents(tb testing.TB, tarFile []byte) []string {
   475  	tb.Helper()
   476  
   477  	contents := []string{}
   478  
   479  	tr := tar.NewReader(bytes.NewReader(tarFile))
   480  	for {
   481  		hdr, err := tr.Next()
   482  		if errors.Is(err, io.EOF) {
   483  			break // End of archive
   484  		}
   485  		require.NoError(tb, err)
   486  
   487  		contents = append(contents, hdr.Name)
   488  	}
   489  
   490  	return contents
   491  }
   492  
   493  func TestAPKConventionalFileName(t *testing.T) {
   494  	apkName := "default"
   495  	testCases := []struct {
   496  		Arch       string
   497  		Version    string
   498  		Meta       string
   499  		Release    string
   500  		Prerelease string
   501  		Expect     string
   502  	}{
   503  		{
   504  			Arch: "amd64", Version: "1.2.3",
   505  			Expect: "default_1.2.3_x86_64.apk",
   506  		},
   507  		{
   508  			Arch: "386", Version: "1.2.3", Meta: "git",
   509  			Expect: "default_1.2.3-git_x86.apk",
   510  		},
   511  		{
   512  			Arch: "386", Version: "1.2.3", Meta: "1", Release: "10",
   513  			Expect: "default_1.2.3-r10-p1_x86.apk",
   514  		},
   515  		{
   516  			Arch: "386", Version: "1.2.3", Prerelease: "git", Release: "1",
   517  			Expect: "default_1.2.3_git-r1_x86.apk",
   518  		},
   519  		{
   520  			Arch: "all", Version: "1.2.3",
   521  			Expect: "default_1.2.3_all.apk",
   522  		},
   523  		{
   524  			Arch: "386", Version: "1.2.3", Release: "1", Prerelease: "beta1",
   525  			Expect: "default_1.2.3_beta1-r1_x86.apk",
   526  		},
   527  		{
   528  			Arch: "amd64", Version: "1.2.3a", Prerelease: "alpha1", Release: "47", Meta: "git-aaaccc",
   529  			Expect: "default_1.2.3a_alpha1-r47-git-aaaccc_x86_64.apk",
   530  		},
   531  	}
   532  
   533  	for _, testCase := range testCases {
   534  		info := &nfpm.Info{
   535  			Name:            apkName,
   536  			Arch:            testCase.Arch,
   537  			Version:         testCase.Version,
   538  			VersionMetadata: testCase.Meta,
   539  			Release:         testCase.Release,
   540  			Prerelease:      testCase.Prerelease,
   541  		}
   542  		require.Equal(t, testCase.Expect, Default.ConventionalFileName(info))
   543  	}
   544  }
   545  
   546  func TestPackageSymlinks(t *testing.T) {
   547  	info := exampleInfo()
   548  	info.Contents = []*files.Content{
   549  		{
   550  			Source:      "../testdata/fake",
   551  			Destination: "fake",
   552  			Type:        files.TypeSymlink,
   553  		},
   554  	}
   555  	require.NoError(t, Default.Package(info, io.Discard))
   556  }
   557  
   558  func TestDirectories(t *testing.T) {
   559  	info := exampleInfo()
   560  	info.Contents = []*files.Content{
   561  		{
   562  			Source:      "../testdata/whatever.conf",
   563  			Destination: "/etc/foo/file",
   564  		},
   565  		{
   566  			Source:      "../testdata/whatever.conf",
   567  			Destination: "/etc/bar/file",
   568  		},
   569  		{
   570  			Destination: "/etc/bar",
   571  			Type:        files.TypeDir,
   572  			FileInfo: &files.ContentFileInfo{
   573  				Owner: "test",
   574  				Mode:  0o700,
   575  			},
   576  		},
   577  		{
   578  			Destination: "/etc/baz",
   579  			Type:        files.TypeDir,
   580  		},
   581  		{
   582  			Destination: "/usr/lib/something/somethingelse",
   583  			Type:        files.TypeDir,
   584  		},
   585  	}
   586  
   587  	require.NoError(t, nfpm.PrepareForPackager(info, "apk"))
   588  
   589  	var buf bytes.Buffer
   590  	size := int64(0)
   591  	err := createFilesInsideTarGz(info, tar.NewWriter(&buf), &size)
   592  	require.NoError(t, err)
   593  
   594  	require.Equal(t, []string{
   595  		"etc/",
   596  		"etc/bar/",
   597  		"etc/bar/file",
   598  		"etc/baz/",
   599  		"etc/foo/",
   600  		"etc/foo/file",
   601  		"usr/",
   602  		"usr/lib/",
   603  		"usr/lib/something/",
   604  		"usr/lib/something/somethingelse/",
   605  	}, getTree(t, buf.Bytes()))
   606  
   607  	// for apks all implicit or explicit directories are created in the tarball
   608  	h := extractFileHeaderFromTar(t, buf.Bytes(), "/etc")
   609  	require.Equal(t, h.Typeflag, byte(tar.TypeDir))
   610  	h = extractFileHeaderFromTar(t, buf.Bytes(), "/etc/foo")
   611  	require.Equal(t, h.Typeflag, byte(tar.TypeDir))
   612  	h = extractFileHeaderFromTar(t, buf.Bytes(), "/etc/bar")
   613  	require.Equal(t, h.Typeflag, byte(tar.TypeDir))
   614  	require.Equal(t, int64(0o700), h.Mode)
   615  	require.Equal(t, "test", h.Uname)
   616  	h = extractFileHeaderFromTar(t, buf.Bytes(), "/etc/baz")
   617  	require.Equal(t, h.Typeflag, byte(tar.TypeDir))
   618  }
   619  
   620  func TestNoDuplicateAutocreatedDirectories(t *testing.T) {
   621  	info := exampleInfo()
   622  	info.DisableGlobbing = true
   623  	info.Contents = []*files.Content{
   624  		{
   625  			Source:      "../testdata/fake",
   626  			Destination: "/etc/foo/bar",
   627  		},
   628  		{
   629  			Type:        files.TypeDir,
   630  			Destination: "/etc/foo",
   631  		},
   632  	}
   633  	require.NoError(t, nfpm.PrepareForPackager(info, "apk"))
   634  
   635  	expected := map[string]bool{
   636  		"etc/":        true,
   637  		"etc/foo/":    true,
   638  		"etc/foo/bar": true,
   639  	}
   640  
   641  	var buf bytes.Buffer
   642  	size := int64(0)
   643  	err := createFilesInsideTarGz(info, tar.NewWriter(&buf), &size)
   644  	require.NoError(t, err)
   645  
   646  	contents := tarContents(t, buf.Bytes())
   647  
   648  	if len(expected) != len(contents) {
   649  		t.Fatalf("contents has %d entries instead of %d: %#v", len(contents), len(expected), contents)
   650  	}
   651  
   652  	for _, entry := range contents {
   653  		if !expected[entry] {
   654  			t.Fatalf("unexpected content: %q", entry)
   655  		}
   656  	}
   657  }
   658  
   659  func TestNoDuplicateDirectories(t *testing.T) {
   660  	info := exampleInfo()
   661  	info.DisableGlobbing = true
   662  	info.Contents = []*files.Content{
   663  		{
   664  			Type:        files.TypeDir,
   665  			Destination: "/etc/foo",
   666  		},
   667  		{
   668  			Type:        files.TypeDir,
   669  			Destination: "/etc/foo/",
   670  		},
   671  	}
   672  	require.Error(t, nfpm.PrepareForPackager(info, "apk"))
   673  }
   674  
   675  func TestNoDuplicateContents(t *testing.T) {
   676  	info := exampleInfo()
   677  	info.Contents = []*files.Content{
   678  		{
   679  			Source:      "../testdata/whatever.conf",
   680  			Destination: "/etc/foo/file",
   681  		},
   682  		{
   683  			Source:      "../testdata/whatever.conf",
   684  			Destination: "/etc/bar/file",
   685  		},
   686  		{
   687  			Destination: "/etc/bar",
   688  			Type:        files.TypeDir,
   689  			FileInfo: &files.ContentFileInfo{
   690  				Owner: "test",
   691  				Mode:  0o700,
   692  			},
   693  		},
   694  		{
   695  			Destination: "/etc/baz",
   696  			Type:        files.TypeDir,
   697  		},
   698  	}
   699  
   700  	require.NoError(t, nfpm.PrepareForPackager(info, "apk"))
   701  
   702  	var buf bytes.Buffer
   703  	size := int64(0)
   704  	err := createFilesInsideTarGz(info, tar.NewWriter(&buf), &size)
   705  	require.NoError(t, err)
   706  
   707  	exists := map[string]bool{}
   708  
   709  	tr := tar.NewReader(bytes.NewReader(buf.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 extractFileHeaderFromTar(tb testing.TB, tarFile []byte, filename string) *tar.Header {
   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  
   737  		if path.Join("/", hdr.Name) != path.Join("/", filename) { // nolint:gosec
   738  			continue
   739  		}
   740  
   741  		return hdr
   742  	}
   743  
   744  	tb.Fatalf("file %q does not exist in tar", filename)
   745  
   746  	return nil
   747  }
   748  
   749  func getTree(tb testing.TB, tarFile []byte) []string {
   750  	tb.Helper()
   751  
   752  	var result []string
   753  	tr := tar.NewReader(bytes.NewReader(tarFile))
   754  	for {
   755  		hdr, err := tr.Next()
   756  		if errors.Is(err, io.EOF) {
   757  			break // End of archive
   758  		}
   759  		require.NoError(tb, err)
   760  
   761  		result = append(result, hdr.Name)
   762  	}
   763  
   764  	return result
   765  }
   766  
   767  func TestArches(t *testing.T) {
   768  	for k := range archToAlpine {
   769  		t.Run(k, func(t *testing.T) {
   770  			info := exampleInfo()
   771  			info.Arch = k
   772  			info = ensureValidArch(info)
   773  			require.Equal(t, archToAlpine[k], info.Arch)
   774  		})
   775  	}
   776  
   777  	t.Run("override", func(t *testing.T) {
   778  		info := exampleInfo()
   779  		info.APK.Arch = "foo64"
   780  		info = ensureValidArch(info)
   781  		require.Equal(t, "foo64", info.Arch)
   782  	})
   783  }
   784  
   785  func TestGlob(t *testing.T) {
   786  	require.NoError(t, Default.Package(nfpm.WithDefaults(&nfpm.Info{
   787  		Name:       "nfpm-repro",
   788  		Version:    "1.0.0",
   789  		Maintainer: "asdfasdf",
   790  
   791  		Overridables: nfpm.Overridables{
   792  			Contents: files.Contents{
   793  				{
   794  					Destination: "/usr/share/nfpm-repro",
   795  					Source:      "../files/*",
   796  				},
   797  			},
   798  		},
   799  	}), io.Discard))
   800  }