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  }