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  }