mirror of
https://github.com/goreleaser/nfpm
synced 2024-11-19 03:25:08 +01:00
e959f1d7c3
* fix: Prevent duplicate folders from being created. * Fix apk filename convention.
1278 lines
32 KiB
Go
1278 lines
32 KiB
Go
package deb
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"compress/gzip"
|
|
"crypto/md5" // nolint: gosec
|
|
"encoding/hex"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/blakesmith/ar"
|
|
"github.com/goreleaser/chglog"
|
|
"github.com/goreleaser/nfpm/v2"
|
|
"github.com/goreleaser/nfpm/v2/files"
|
|
"github.com/goreleaser/nfpm/v2/internal/sign"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/xi2/xz"
|
|
)
|
|
|
|
// nolint: gochecknoglobals
|
|
var update = flag.Bool("update", false, "update .golden files")
|
|
|
|
func exampleInfo() *nfpm.Info {
|
|
return nfpm.WithDefaults(&nfpm.Info{
|
|
Name: "foo",
|
|
Arch: "amd64",
|
|
Description: "Foo does things",
|
|
Priority: "extra",
|
|
Maintainer: "Carlos A Becker <pkg@carlosbecker.com>",
|
|
Version: "v1.0.0",
|
|
Section: "default",
|
|
Homepage: "http://carlosbecker.com",
|
|
Vendor: "nope",
|
|
Overridables: nfpm.Overridables{
|
|
Depends: []string{
|
|
"bash",
|
|
},
|
|
Recommends: []string{
|
|
"git",
|
|
},
|
|
Suggests: []string{
|
|
"bash",
|
|
},
|
|
Replaces: []string{
|
|
"svn",
|
|
},
|
|
Provides: []string{
|
|
"bzr",
|
|
},
|
|
Conflicts: []string{
|
|
"zsh",
|
|
},
|
|
Contents: []*files.Content{
|
|
{
|
|
Source: "../testdata/fake",
|
|
Destination: "/usr/local/bin/fake",
|
|
},
|
|
{
|
|
Source: "../testdata/whatever.conf",
|
|
Destination: "/usr/share/doc/fake/fake.txt",
|
|
},
|
|
{
|
|
Source: "../testdata/whatever.conf",
|
|
Destination: "/etc/fake/fake.conf",
|
|
Type: "config",
|
|
},
|
|
{
|
|
Destination: "/var/log/whatever",
|
|
Type: "dir",
|
|
},
|
|
{
|
|
Destination: "/usr/share/whatever",
|
|
Type: "dir",
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestDeb(t *testing.T) {
|
|
for _, arch := range []string{"386", "amd64"} {
|
|
arch := arch
|
|
t.Run(arch, func(t *testing.T) {
|
|
info := exampleInfo()
|
|
info.Arch = arch
|
|
err := Default.Package(info, ioutil.Discard)
|
|
require.NoError(t, err)
|
|
})
|
|
}
|
|
}
|
|
|
|
func extractDebVersion(deb *bytes.Buffer) string {
|
|
for _, s := range strings.Split(deb.String(), "\n") {
|
|
if strings.Contains(s, "Version: ") {
|
|
return strings.TrimPrefix(s, "Version: ")
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func TestDebVersionWithDash(t *testing.T) {
|
|
info := exampleInfo()
|
|
info.Version = "1.0.0-beta"
|
|
err := Default.Package(info, ioutil.Discard)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestDebVersion(t *testing.T) {
|
|
info := exampleInfo()
|
|
info.Version = "1.0.0" //nolint:golint,goconst
|
|
var buf bytes.Buffer
|
|
err := writeControl(&buf, controlData{info, 0})
|
|
require.NoError(t, err)
|
|
v := extractDebVersion(&buf)
|
|
require.Equal(t, "1.0.0", v)
|
|
}
|
|
|
|
func TestDebVersionWithRelease(t *testing.T) {
|
|
info := exampleInfo()
|
|
info.Version = "1.0.0" //nolint:golint,goconst
|
|
info.Release = "1"
|
|
var buf bytes.Buffer
|
|
err := writeControl(&buf, controlData{info, 0})
|
|
require.NoError(t, err)
|
|
v := extractDebVersion(&buf)
|
|
require.Equal(t, "1.0.0-1", v)
|
|
}
|
|
|
|
func TestDebVersionWithPrerelease(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
|
|
info := exampleInfo()
|
|
info.Version = "1.0.0" //nolint:golint,goconst
|
|
info.Prerelease = "1"
|
|
err := writeControl(&buf, controlData{info, 0})
|
|
require.NoError(t, err)
|
|
v := extractDebVersion(&buf)
|
|
require.Equal(t, "1.0.0~1", v)
|
|
}
|
|
|
|
func TestDebVersionWithReleaseAndPrerelease(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
|
|
info := exampleInfo()
|
|
info.Version = "1.0.0" //nolint:golint,goconst
|
|
info.Release = "2"
|
|
info.Prerelease = "rc1" //nolint:golint,goconst
|
|
err := writeControl(&buf, controlData{info, 0})
|
|
require.NoError(t, err)
|
|
v := extractDebVersion(&buf)
|
|
require.Equal(t, "1.0.0-2~rc1", v)
|
|
}
|
|
|
|
func TestDebVersionWithVersionMetadata(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
|
|
info := exampleInfo()
|
|
info.Version = "1.0.0+meta" //nolint:golint,goconst
|
|
info.VersionMetadata = ""
|
|
err := writeControl(&buf, controlData{info, 0})
|
|
require.NoError(t, err)
|
|
v := extractDebVersion(&buf)
|
|
require.Equal(t, "1.0.0+meta", v)
|
|
|
|
buf.Reset()
|
|
|
|
info.Version = "1.0.0" //nolint:golint,goconst
|
|
info.VersionMetadata = "meta"
|
|
err = writeControl(&buf, controlData{info, 0})
|
|
require.NoError(t, err)
|
|
v = extractDebVersion(&buf)
|
|
require.Equal(t, "1.0.0+meta", v)
|
|
|
|
buf.Reset()
|
|
|
|
info.Version = "1.0.0+foo" //nolint:golint,goconst
|
|
info.Prerelease = "alpha"
|
|
info.VersionMetadata = "meta"
|
|
err = writeControl(&buf, controlData{nfpm.WithDefaults(info), 0})
|
|
require.NoError(t, err)
|
|
v = extractDebVersion(&buf)
|
|
require.Equal(t, "1.0.0~alpha+meta", v)
|
|
}
|
|
|
|
func TestControl(t *testing.T) {
|
|
var w bytes.Buffer
|
|
require.NoError(t, writeControl(&w, controlData{
|
|
Info: exampleInfo(),
|
|
InstalledSize: 10,
|
|
}))
|
|
golden := "testdata/control.golden"
|
|
if *update {
|
|
require.NoError(t, ioutil.WriteFile(golden, w.Bytes(), 0o600))
|
|
}
|
|
bts, err := ioutil.ReadFile(golden) //nolint:gosec
|
|
require.NoError(t, err)
|
|
require.Equal(t, string(bts), w.String())
|
|
}
|
|
|
|
func TestSpecialFiles(t *testing.T) {
|
|
var w bytes.Buffer
|
|
out := tar.NewWriter(&w)
|
|
filePath := "testdata/templates.golden"
|
|
require.Error(t, newFilePathInsideTar(out, "doesnotexit", "templates", 0o644))
|
|
require.NoError(t, newFilePathInsideTar(out, filePath, "templates", 0o644))
|
|
in := tar.NewReader(&w)
|
|
header, err := in.Next()
|
|
require.NoError(t, err)
|
|
require.Equal(t, "templates", header.FileInfo().Name())
|
|
mode, err := strconv.ParseInt("0644", 8, 64)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(header.FileInfo().Mode()), mode)
|
|
data, err := ioutil.ReadAll(in)
|
|
require.NoError(t, err)
|
|
org, err := ioutil.ReadFile(filePath)
|
|
require.NoError(t, err)
|
|
require.Equal(t, data, org)
|
|
}
|
|
|
|
func TestNoJoinsControl(t *testing.T) {
|
|
var w bytes.Buffer
|
|
require.NoError(t, writeControl(&w, controlData{
|
|
Info: nfpm.WithDefaults(&nfpm.Info{
|
|
Name: "foo",
|
|
Arch: "amd64",
|
|
Description: "Foo does things",
|
|
Priority: "extra",
|
|
Maintainer: "Carlos A Becker <pkg@carlosbecker.com>",
|
|
Version: "v1.0.0",
|
|
Section: "default",
|
|
Homepage: "http://carlosbecker.com",
|
|
Vendor: "nope",
|
|
Overridables: nfpm.Overridables{
|
|
Depends: []string{},
|
|
Recommends: []string{},
|
|
Suggests: []string{},
|
|
Replaces: []string{},
|
|
Provides: []string{},
|
|
Conflicts: []string{},
|
|
Contents: []*files.Content{},
|
|
},
|
|
}),
|
|
InstalledSize: 10,
|
|
}))
|
|
golden := "testdata/control2.golden"
|
|
if *update {
|
|
require.NoError(t, ioutil.WriteFile(golden, w.Bytes(), 0o600))
|
|
}
|
|
bts, err := ioutil.ReadFile(golden) //nolint:gosec
|
|
require.NoError(t, err)
|
|
require.Equal(t, string(bts), w.String())
|
|
}
|
|
|
|
func TestDebFileDoesNotExist(t *testing.T) {
|
|
abs, err := filepath.Abs("../testdata/whatever.confzzz")
|
|
require.NoError(t, err)
|
|
err = Default.Package(
|
|
nfpm.WithDefaults(&nfpm.Info{
|
|
Name: "foo",
|
|
Arch: "amd64",
|
|
Description: "Foo does things",
|
|
Priority: "extra",
|
|
Maintainer: "Carlos A Becker <pkg@carlosbecker.com>",
|
|
Version: "1.0.0",
|
|
Section: "default",
|
|
Homepage: "http://carlosbecker.com",
|
|
Vendor: "nope",
|
|
Overridables: nfpm.Overridables{
|
|
Depends: []string{
|
|
"bash",
|
|
},
|
|
Contents: []*files.Content{
|
|
{
|
|
Source: "../testdata/fake",
|
|
Destination: "/usr/local/bin/fake",
|
|
},
|
|
{
|
|
Source: "../testdata/whatever.confzzz",
|
|
Destination: "/etc/fake/fake.conf",
|
|
Type: "config",
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
ioutil.Discard,
|
|
)
|
|
require.EqualError(t, err, fmt.Sprintf("matching \"%s\": file does not exist", filepath.ToSlash(abs)))
|
|
}
|
|
|
|
func TestDebNoFiles(t *testing.T) {
|
|
err := Default.Package(
|
|
nfpm.WithDefaults(&nfpm.Info{
|
|
Name: "foo",
|
|
Arch: "amd64",
|
|
Description: "Foo does things",
|
|
Priority: "extra",
|
|
Maintainer: "Carlos A Becker <pkg@carlosbecker.com>",
|
|
Version: "1.0.0",
|
|
Section: "default",
|
|
Homepage: "http://carlosbecker.com",
|
|
Vendor: "nope",
|
|
Overridables: nfpm.Overridables{
|
|
Depends: []string{
|
|
"bash",
|
|
},
|
|
},
|
|
}),
|
|
ioutil.Discard,
|
|
)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestDebNoInfo(t *testing.T) {
|
|
err := Default.Package(nfpm.WithDefaults(&nfpm.Info{}), ioutil.Discard)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestConffiles(t *testing.T) {
|
|
info := nfpm.WithDefaults(&nfpm.Info{
|
|
Name: "minimal",
|
|
Arch: "arm64",
|
|
Description: "Minimal does nothing",
|
|
Priority: "extra",
|
|
Version: "1.0.0",
|
|
Section: "default",
|
|
Overridables: nfpm.Overridables{
|
|
Contents: []*files.Content{
|
|
{
|
|
Source: "../testdata/fake",
|
|
Destination: "/etc/fake",
|
|
Type: "config",
|
|
},
|
|
},
|
|
},
|
|
})
|
|
err := info.Validate()
|
|
require.NoError(t, err)
|
|
out := conffiles(info)
|
|
require.Equal(t, "/etc/fake\n", string(out), "should have a trailing empty line")
|
|
}
|
|
|
|
func TestPathsToCreate(t *testing.T) {
|
|
for filePath, parts := range map[string][]string{
|
|
"/usr/share/doc/whatever/foo.md": {"usr", "usr/share", "usr/share/doc", "usr/share/doc/whatever"},
|
|
"/var/moises": {"var"},
|
|
"/": {},
|
|
} {
|
|
parts := parts
|
|
filePath := filePath
|
|
t.Run(fmt.Sprintf("path: '%s'", filePath), func(t *testing.T) {
|
|
require.Equal(t, parts, pathsToCreate(filePath))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMinimalFields(t *testing.T) {
|
|
var w bytes.Buffer
|
|
require.NoError(t, writeControl(&w, controlData{
|
|
Info: nfpm.WithDefaults(&nfpm.Info{
|
|
Name: "minimal",
|
|
Arch: "arm64",
|
|
Description: "Minimal does nothing",
|
|
Priority: "extra",
|
|
Version: "1.0.0",
|
|
Section: "default",
|
|
}),
|
|
}))
|
|
golden := "testdata/minimal.golden"
|
|
if *update {
|
|
require.NoError(t, ioutil.WriteFile(golden, w.Bytes(), 0o600))
|
|
}
|
|
bts, err := ioutil.ReadFile(golden) //nolint:gosec
|
|
require.NoError(t, err)
|
|
require.Equal(t, string(bts), w.String())
|
|
}
|
|
|
|
func TestDebEpoch(t *testing.T) {
|
|
var w bytes.Buffer
|
|
require.NoError(t, writeControl(&w, controlData{
|
|
Info: nfpm.WithDefaults(&nfpm.Info{
|
|
Name: "withepoch",
|
|
Arch: "arm64",
|
|
Description: "Has an epoch added to it's version",
|
|
Priority: "extra",
|
|
Epoch: "2",
|
|
Version: "1.0.0",
|
|
Section: "default",
|
|
}),
|
|
}))
|
|
golden := "testdata/withepoch.golden"
|
|
if *update {
|
|
require.NoError(t, ioutil.WriteFile(golden, w.Bytes(), 0o600))
|
|
}
|
|
bts, err := ioutil.ReadFile(golden) //nolint:gosec
|
|
require.NoError(t, err)
|
|
require.Equal(t, string(bts), w.String())
|
|
}
|
|
|
|
func TestDebRules(t *testing.T) {
|
|
var w bytes.Buffer
|
|
require.NoError(t, writeControl(&w, controlData{
|
|
Info: nfpm.WithDefaults(&nfpm.Info{
|
|
Name: "lala",
|
|
Arch: "arm64",
|
|
Description: "Has rules script",
|
|
Priority: "extra",
|
|
Epoch: "2",
|
|
Version: "1.2.0",
|
|
Section: "default",
|
|
Overridables: nfpm.Overridables{
|
|
Deb: nfpm.Deb{
|
|
Scripts: nfpm.DebScripts{
|
|
Rules: "foo.sh",
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
}))
|
|
golden := "testdata/rules.golden"
|
|
if *update {
|
|
require.NoError(t, ioutil.WriteFile(golden, w.Bytes(), 0o600))
|
|
}
|
|
bts, err := ioutil.ReadFile(golden) //nolint:gosec
|
|
require.NoError(t, err)
|
|
require.Equal(t, string(bts), w.String())
|
|
}
|
|
|
|
func TestMultilineFields(t *testing.T) {
|
|
var w bytes.Buffer
|
|
require.NoError(t, writeControl(&w, controlData{
|
|
Info: nfpm.WithDefaults(&nfpm.Info{
|
|
Name: "multiline",
|
|
Arch: "riscv64",
|
|
Description: "This field is a\nmultiline field\nthat should work.",
|
|
Priority: "extra",
|
|
Version: "1.0.0",
|
|
Section: "default",
|
|
}),
|
|
}))
|
|
golden := "testdata/multiline.golden"
|
|
if *update {
|
|
require.NoError(t, ioutil.WriteFile(golden, w.Bytes(), 0o600))
|
|
}
|
|
bts, err := ioutil.ReadFile(golden) //nolint:gosec
|
|
require.NoError(t, err)
|
|
require.Equal(t, string(bts), w.String())
|
|
}
|
|
|
|
func TestDEBConventionalFileName(t *testing.T) {
|
|
info := &nfpm.Info{
|
|
Name: "testpkg",
|
|
Arch: "all",
|
|
}
|
|
|
|
testCases := []struct {
|
|
Version string
|
|
Release string
|
|
Prerelease string
|
|
Expected string
|
|
Metadata string
|
|
}{
|
|
{
|
|
Version: "1.2.3", Release: "", Prerelease: "", Metadata: "",
|
|
Expected: fmt.Sprintf("%s_1.2.3_%s.deb", info.Name, info.Arch),
|
|
},
|
|
{
|
|
Version: "1.2.3", Release: "4", Prerelease: "", Metadata: "",
|
|
Expected: fmt.Sprintf("%s_1.2.3-4_%s.deb", info.Name, info.Arch),
|
|
},
|
|
{
|
|
Version: "1.2.3", Release: "4", Prerelease: "5", Metadata: "",
|
|
Expected: fmt.Sprintf("%s_1.2.3~5-4_%s.deb", info.Name, info.Arch),
|
|
},
|
|
{
|
|
Version: "1.2.3", Release: "", Prerelease: "5", Metadata: "",
|
|
Expected: fmt.Sprintf("%s_1.2.3~5_%s.deb", info.Name, info.Arch),
|
|
},
|
|
{
|
|
Version: "1.2.3", Release: "1", Prerelease: "5", Metadata: "git",
|
|
Expected: fmt.Sprintf("%s_1.2.3~5+git-1_%s.deb", info.Name, info.Arch),
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
info.Version = testCase.Version
|
|
info.Release = testCase.Release
|
|
info.Prerelease = testCase.Prerelease
|
|
info.VersionMetadata = testCase.Metadata
|
|
|
|
require.Equal(t, testCase.Expected, Default.ConventionalFileName(info))
|
|
}
|
|
}
|
|
|
|
func TestDebChangelogControl(t *testing.T) {
|
|
info := &nfpm.Info{
|
|
Name: "changelog-test",
|
|
Arch: "amd64",
|
|
Description: "This package has changelogs.",
|
|
Version: "1.0.0",
|
|
Changelog: "../testdata/changelog.yaml",
|
|
}
|
|
err := info.Validate()
|
|
require.NoError(t, err)
|
|
|
|
controlTarGz, err := createControl(0, []byte{}, info)
|
|
require.NoError(t, err)
|
|
|
|
controlChangelog := extractFileFromTar(t, inflate(t, "gz", controlTarGz), "changelog")
|
|
|
|
goldenChangelog := readAndFormatAsDebChangelog(t, info.Changelog, info.Name)
|
|
|
|
require.Equal(t, goldenChangelog, string(controlChangelog))
|
|
}
|
|
|
|
func TestDebNoChangelogControlWithoutChangelogConfigured(t *testing.T) {
|
|
info := &nfpm.Info{
|
|
Name: "no-changelog-test",
|
|
Arch: "amd64",
|
|
Description: "This package has explicitly no changelog.",
|
|
Version: "1.0.0",
|
|
}
|
|
err := info.Validate()
|
|
require.NoError(t, err)
|
|
|
|
controlTarGz, err := createControl(0, []byte{}, info)
|
|
require.NoError(t, err)
|
|
|
|
require.False(t, tarContains(t, inflate(t, "gz", controlTarGz), "changelog"))
|
|
}
|
|
|
|
func TestDebChangelogData(t *testing.T) {
|
|
info := &nfpm.Info{
|
|
Name: "changelog-test",
|
|
Arch: "amd64",
|
|
Description: "This package has changelogs.",
|
|
Version: "1.0.0",
|
|
Changelog: "../testdata/changelog.yaml",
|
|
}
|
|
err := info.Validate()
|
|
require.NoError(t, err)
|
|
|
|
dataTarball, _, _, dataTarballName, err := createDataTarball(info)
|
|
require.NoError(t, err)
|
|
|
|
changelogName := fmt.Sprintf("/usr/share/doc/%s/changelog.gz", info.Name)
|
|
dataChangelogGz := extractFileFromTar(t,
|
|
inflate(t, dataTarballName, dataTarball), changelogName)
|
|
|
|
dataChangelog := inflate(t, "gz", dataChangelogGz)
|
|
goldenChangelog := readAndFormatAsDebChangelog(t, info.Changelog, info.Name)
|
|
|
|
require.Equal(t, goldenChangelog, string(dataChangelog))
|
|
}
|
|
|
|
func TestDebNoChangelogDataWithoutChangelogConfigured(t *testing.T) {
|
|
info := &nfpm.Info{
|
|
Name: "no-changelog-test",
|
|
Arch: "amd64",
|
|
Description: "This package has explicitly no changelog.",
|
|
Version: "1.0.0",
|
|
}
|
|
err := info.Validate()
|
|
require.NoError(t, err)
|
|
|
|
dataTarball, _, _, dataTarballName, err := createDataTarball(info)
|
|
require.NoError(t, err)
|
|
|
|
changelogName := fmt.Sprintf("/usr/share/doc/%s/changelog.gz", info.Name)
|
|
|
|
require.False(t, tarContains(t, inflate(t, dataTarballName, dataTarball), changelogName))
|
|
}
|
|
|
|
func TestDebTriggers(t *testing.T) {
|
|
info := &nfpm.Info{
|
|
Name: "no-triggers-test",
|
|
Arch: "amd64",
|
|
Description: "This package has multiple triggers.",
|
|
Version: "1.0.0",
|
|
Overridables: nfpm.Overridables{
|
|
Deb: nfpm.Deb{
|
|
Triggers: nfpm.DebTriggers{
|
|
Interest: []string{"trigger1", "trigger2"},
|
|
InterestAwait: []string{"trigger3"},
|
|
// InterestNoAwait omitted
|
|
// Activate omitted
|
|
ActivateAwait: []string{"trigger4"},
|
|
ActivateNoAwait: []string{"trigger5", "trigger6"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
err := info.Validate()
|
|
require.NoError(t, err)
|
|
|
|
controlTarGz, err := createControl(0, []byte{}, info)
|
|
require.NoError(t, err)
|
|
|
|
controlTriggers := extractFileFromTar(t, inflate(t, "gz", controlTarGz), "triggers")
|
|
|
|
goldenTriggers := createTriggers(info)
|
|
|
|
require.Equal(t, string(goldenTriggers), string(controlTriggers))
|
|
|
|
// check if specified triggers are included and also that
|
|
// no remnants of triggers that were not specified are included
|
|
require.True(t, bytes.Contains(controlTriggers,
|
|
[]byte("interest trigger1\n")))
|
|
require.True(t, bytes.Contains(controlTriggers,
|
|
[]byte("interest trigger2\n")))
|
|
require.True(t, bytes.Contains(controlTriggers,
|
|
[]byte("interest-await trigger3\n")))
|
|
require.False(t, bytes.Contains(controlTriggers,
|
|
[]byte("interest-noawait ")))
|
|
require.False(t, bytes.Contains(controlTriggers,
|
|
[]byte("activate ")))
|
|
require.True(t, bytes.Contains(controlTriggers,
|
|
[]byte("activate-await trigger4\n")))
|
|
require.True(t, bytes.Contains(controlTriggers,
|
|
[]byte("activate-noawait trigger5\n")))
|
|
require.True(t, bytes.Contains(controlTriggers,
|
|
[]byte("activate-noawait trigger6\n")))
|
|
}
|
|
|
|
func TestDebNoTriggersInControlIfNoneProvided(t *testing.T) {
|
|
info := &nfpm.Info{
|
|
Name: "no-triggers-test",
|
|
Arch: "amd64",
|
|
Description: "This package has explicitly no triggers.",
|
|
Version: "1.0.0",
|
|
}
|
|
err := info.Validate()
|
|
require.NoError(t, err)
|
|
|
|
controlTarGz, err := createControl(0, []byte{}, info)
|
|
require.NoError(t, err)
|
|
|
|
require.False(t, tarContains(t, inflate(t, "gz", controlTarGz), "triggers"))
|
|
}
|
|
|
|
func TestSymlinkInFiles(t *testing.T) {
|
|
var (
|
|
symlinkTarget = "../testdata/whatever.conf"
|
|
packagedTarget = "/etc/fake/whatever.conf"
|
|
)
|
|
|
|
info := &nfpm.Info{
|
|
Name: "symlink-in-files",
|
|
Arch: "amd64",
|
|
Description: "This package's config references a file via symlink.",
|
|
Version: "1.0.0",
|
|
Overridables: nfpm.Overridables{
|
|
Contents: []*files.Content{
|
|
{
|
|
Source: symlinkTo(t, symlinkTarget),
|
|
Destination: packagedTarget,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
err := info.Validate()
|
|
require.NoError(t, err)
|
|
|
|
realSymlinkTarget, err := ioutil.ReadFile(symlinkTarget)
|
|
require.NoError(t, err)
|
|
|
|
dataTarball, _, _, dataTarballName, err := createDataTarball(info)
|
|
require.NoError(t, err)
|
|
|
|
packagedSymlinkTarget := extractFileFromTar(t,
|
|
inflate(t, dataTarballName, dataTarball), packagedTarget)
|
|
|
|
require.Equal(t, string(realSymlinkTarget), string(packagedSymlinkTarget))
|
|
}
|
|
|
|
func TestSymlink(t *testing.T) {
|
|
var (
|
|
configFilePath = "/usr/share/doc/fake/fake.txt"
|
|
symlink = "/path/to/symlink"
|
|
symlinkTarget = configFilePath
|
|
)
|
|
|
|
info := &nfpm.Info{
|
|
Name: "symlink-in-files",
|
|
Arch: "amd64",
|
|
Description: "This package's config references a file via symlink.",
|
|
Version: "1.0.0",
|
|
Overridables: nfpm.Overridables{
|
|
Contents: []*files.Content{
|
|
{
|
|
Source: "../testdata/whatever.conf",
|
|
Destination: configFilePath,
|
|
},
|
|
{
|
|
Source: symlinkTarget,
|
|
Destination: symlink,
|
|
Type: "symlink",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
err := info.Validate()
|
|
require.NoError(t, err)
|
|
|
|
dataTarball, _, _, dataTarballName, err := createDataTarball(info)
|
|
require.NoError(t, err)
|
|
|
|
packagedSymlinkHeader := extractFileHeaderFromTar(t,
|
|
inflate(t, dataTarballName, dataTarball), symlink)
|
|
|
|
require.Equal(t, symlink, path.Join("/", packagedSymlinkHeader.Name)) // nolint:gosec
|
|
require.Equal(t, uint8(tar.TypeSymlink), packagedSymlinkHeader.Typeflag)
|
|
require.Equal(t, symlinkTarget, packagedSymlinkHeader.Linkname)
|
|
}
|
|
|
|
func TestEnsureRelativePrefixInTarballs(t *testing.T) {
|
|
info := exampleInfo()
|
|
info.Contents = []*files.Content{
|
|
{
|
|
Source: "/symlink/to/fake.txt",
|
|
Destination: "/usr/share/doc/fake/fake.txt",
|
|
Type: "symlink",
|
|
},
|
|
}
|
|
info.Changelog = "../testdata/changelog.yaml"
|
|
err := info.Validate()
|
|
require.NoError(t, err)
|
|
|
|
dataTarball, md5sums, instSize, tarballName, err := createDataTarball(info)
|
|
require.NoError(t, err)
|
|
testRelativePathPrefixInTar(t, inflate(t, tarballName, dataTarball))
|
|
|
|
controlTarGz, err := createControl(instSize, md5sums, info)
|
|
require.NoError(t, err)
|
|
testRelativePathPrefixInTar(t, inflate(t, "gz", controlTarGz))
|
|
}
|
|
|
|
func TestMD5Sums(t *testing.T) {
|
|
info := exampleInfo()
|
|
info.Changelog = "../testdata/changelog.yaml"
|
|
err := info.Validate()
|
|
require.NoError(t, err)
|
|
|
|
nFiles := 1
|
|
for _, f := range info.Contents {
|
|
if (f.Packager == "" || f.Packager == "deb") && f.Type != "dir" {
|
|
nFiles++
|
|
}
|
|
}
|
|
|
|
dataTarball, md5sums, instSize, tarballName, err := createDataTarball(info)
|
|
require.NoError(t, err)
|
|
|
|
controlTarGz, err := createControl(instSize, md5sums, info)
|
|
require.NoError(t, err)
|
|
|
|
md5sumsFile := extractFileFromTar(t, inflate(t, "gz", controlTarGz), "./md5sums")
|
|
|
|
lines := strings.Split(strings.TrimRight(string(md5sumsFile), "\n"), "\n")
|
|
require.Equal(t, nFiles, len(lines))
|
|
|
|
dataTar := inflate(t, tarballName, dataTarball)
|
|
|
|
for _, line := range lines {
|
|
parts := strings.Fields(line)
|
|
require.Equal(t, len(parts), 2)
|
|
|
|
md5sum, fileName := parts[0], parts[1]
|
|
|
|
digest := md5.New() // nolint:gosec
|
|
_, err = digest.Write(extractFileFromTar(t, dataTar, fileName))
|
|
require.NoError(t, err)
|
|
require.Equal(t, md5sum, hex.EncodeToString(digest.Sum(nil)))
|
|
}
|
|
}
|
|
|
|
func TestDirectories(t *testing.T) {
|
|
info := exampleInfo()
|
|
info.Contents = []*files.Content{
|
|
{
|
|
Source: "../testdata/whatever.conf",
|
|
Destination: "/etc/foo/file",
|
|
},
|
|
{
|
|
Source: "../testdata/whatever.conf",
|
|
Destination: "/etc/bar/file",
|
|
},
|
|
{
|
|
Destination: "/etc/bar",
|
|
Type: "dir",
|
|
FileInfo: &files.ContentFileInfo{
|
|
Owner: "test",
|
|
Mode: 0o700,
|
|
},
|
|
},
|
|
{
|
|
Destination: "/etc/baz",
|
|
Type: "dir",
|
|
},
|
|
}
|
|
|
|
require.NoError(t, info.Validate())
|
|
|
|
deflatedDataTarball, _, _, dataTarballName, err := createDataTarball(info)
|
|
require.NoError(t, err)
|
|
dataTarball := inflate(t, dataTarballName, deflatedDataTarball)
|
|
|
|
// for debs all implicit or explicit directories are created in the tarball
|
|
h := extractFileHeaderFromTar(t, dataTarball, "/etc")
|
|
require.NoError(t, err)
|
|
require.Equal(t, h.Typeflag, byte(tar.TypeDir))
|
|
h = extractFileHeaderFromTar(t, dataTarball, "/etc/foo")
|
|
require.NoError(t, err)
|
|
require.Equal(t, h.Typeflag, byte(tar.TypeDir))
|
|
h = extractFileHeaderFromTar(t, dataTarball, "/etc/bar")
|
|
require.NoError(t, err)
|
|
require.Equal(t, h.Typeflag, byte(tar.TypeDir))
|
|
require.Equal(t, h.Mode, int64(0o700))
|
|
require.Equal(t, h.Uname, "test")
|
|
h = extractFileHeaderFromTar(t, dataTarball, "/etc/baz")
|
|
require.NoError(t, err)
|
|
require.Equal(t, h.Typeflag, byte(tar.TypeDir))
|
|
}
|
|
|
|
func TestNoDuplicateContents(t *testing.T) {
|
|
info := exampleInfo()
|
|
info.Contents = []*files.Content{
|
|
{
|
|
Source: "../testdata/whatever.conf",
|
|
Destination: "/etc/foo/file",
|
|
},
|
|
{
|
|
Source: "../testdata/whatever.conf",
|
|
Destination: "/etc/foo/file2",
|
|
},
|
|
{
|
|
Destination: "/etc/foo",
|
|
Type: "dir",
|
|
},
|
|
{
|
|
Destination: "/etc/baz",
|
|
Type: "dir",
|
|
},
|
|
}
|
|
|
|
require.NoError(t, info.Validate())
|
|
|
|
deflatedDataTarball, _, _, dataTarballName, err := createDataTarball(info)
|
|
require.NoError(t, err)
|
|
dataTarball := inflate(t, dataTarballName, deflatedDataTarball)
|
|
|
|
exists := map[string]bool{}
|
|
|
|
tr := tar.NewReader(bytes.NewReader(dataTarball))
|
|
for {
|
|
hdr, err := tr.Next()
|
|
if errors.Is(err, io.EOF) {
|
|
break // End of archive
|
|
}
|
|
require.NoError(t, err)
|
|
|
|
_, ok := exists[hdr.Name]
|
|
if ok {
|
|
t.Fatalf("%s exists more than once in tarball", hdr.Name)
|
|
}
|
|
|
|
exists[hdr.Name] = true
|
|
}
|
|
}
|
|
|
|
func testRelativePathPrefixInTar(tb testing.TB, tarFile []byte) {
|
|
tb.Helper()
|
|
|
|
tr := tar.NewReader(bytes.NewReader(tarFile))
|
|
for {
|
|
hdr, err := tr.Next()
|
|
if errors.Is(err, io.EOF) {
|
|
break // End of archive
|
|
}
|
|
require.NoError(tb, err)
|
|
|
|
require.True(tb, strings.HasPrefix(hdr.Name, "./"), "%s does not start with './'", hdr.Name)
|
|
}
|
|
}
|
|
|
|
func TestDebsigsSignature(t *testing.T) {
|
|
info := exampleInfo()
|
|
info.Deb.Signature.KeyFile = "../internal/sign/testdata/privkey.asc"
|
|
info.Deb.Signature.KeyPassphrase = "hunter2"
|
|
|
|
var deb bytes.Buffer
|
|
err := Default.Package(info, &deb)
|
|
require.NoError(t, err)
|
|
|
|
debBinary := extractFileFromAr(t, deb.Bytes(), "debian-binary")
|
|
controlTarGz := extractFileFromAr(t, deb.Bytes(), "control.tar.gz")
|
|
dataTarball := extractFileFromAr(t, deb.Bytes(), findDataTarball(t, deb.Bytes()))
|
|
signature := extractFileFromAr(t, deb.Bytes(), "_gpgorigin")
|
|
|
|
message := io.MultiReader(bytes.NewReader(debBinary),
|
|
bytes.NewReader(controlTarGz), bytes.NewReader(dataTarball))
|
|
|
|
err = sign.PGPVerify(message, signature, "../internal/sign/testdata/pubkey.asc")
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestDebsigsSignatureError(t *testing.T) {
|
|
info := exampleInfo()
|
|
info.Deb.Signature.KeyFile = "/does/not/exist"
|
|
|
|
var deb bytes.Buffer
|
|
err := Default.Package(info, &deb)
|
|
require.Error(t, err)
|
|
|
|
var expectedError *nfpm.ErrSigningFailure
|
|
require.True(t, errors.As(err, &expectedError))
|
|
}
|
|
|
|
func TestDisableGlobbing(t *testing.T) {
|
|
info := exampleInfo()
|
|
info.DisableGlobbing = true
|
|
info.Contents = []*files.Content{
|
|
{
|
|
Source: "../testdata/{file}[",
|
|
Destination: "/test/{file}[",
|
|
},
|
|
}
|
|
require.NoError(t, info.Validate())
|
|
|
|
dataTarball, _, _, tarballName, err := createDataTarball(info)
|
|
require.NoError(t, err)
|
|
|
|
expectedContent, err := ioutil.ReadFile("../testdata/{file}[")
|
|
require.NoError(t, err)
|
|
|
|
actualContent := extractFileFromTar(t, inflate(t, tarballName, dataTarball), "/test/{file}[")
|
|
|
|
require.Equal(t, expectedContent, actualContent)
|
|
}
|
|
|
|
func TestNoDuplicateAutocreatedDirectories(t *testing.T) {
|
|
info := exampleInfo()
|
|
info.DisableGlobbing = true
|
|
info.Contents = []*files.Content{
|
|
{
|
|
Source: "../testdata/fake",
|
|
Destination: "/etc/foo/bar",
|
|
},
|
|
{
|
|
Type: "dir",
|
|
Destination: "/etc/foo",
|
|
},
|
|
}
|
|
require.NoError(t, info.Validate())
|
|
|
|
expected := map[string]bool{
|
|
"./etc/": true,
|
|
"./etc/foo/": true,
|
|
"./etc/foo/bar": true,
|
|
}
|
|
|
|
dataTarball, _, _, tarballName, err := createDataTarball(info)
|
|
require.NoError(t, err)
|
|
|
|
contents := tarContents(t, inflate(t, tarballName, dataTarball))
|
|
|
|
if len(expected) != len(contents) {
|
|
t.Fatalf("contents has %d entries instead of %d: %#v", len(contents), len(expected), contents)
|
|
}
|
|
|
|
for _, entry := range contents {
|
|
if !expected[entry] {
|
|
t.Fatalf("unexpected content: %q", entry)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNoDuplicateDirectories(t *testing.T) {
|
|
info := exampleInfo()
|
|
info.DisableGlobbing = true
|
|
info.Contents = []*files.Content{
|
|
{
|
|
Type: "dir",
|
|
Destination: "/etc/foo",
|
|
},
|
|
{
|
|
Type: "dir",
|
|
Destination: "/etc/foo/",
|
|
},
|
|
}
|
|
require.NoError(t, info.Validate())
|
|
|
|
_, _, _, _, err := createDataTarball(info)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestCompressionAlgorithms(t *testing.T) {
|
|
testCases := []struct {
|
|
algorithm string
|
|
dataTarballName string
|
|
}{
|
|
{"gzip", "data.tar.gz"},
|
|
{"", "data.tar.gz"}, // test current default
|
|
{"xz", "data.tar.xz"},
|
|
{"none", "data.tar"},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
testCase := testCase
|
|
|
|
t.Run(testCase.algorithm, func(t *testing.T) {
|
|
info := exampleInfo()
|
|
info.Deb.Compression = testCase.algorithm
|
|
|
|
var deb bytes.Buffer
|
|
|
|
err := Default.Package(info, &deb)
|
|
require.NoError(t, err)
|
|
|
|
dataTarballName := findDataTarball(t, deb.Bytes())
|
|
require.Equal(t, dataTarballName, testCase.dataTarballName)
|
|
|
|
dataTarball := extractFileFromAr(t, deb.Bytes(), dataTarballName)
|
|
dataTar := inflate(t, dataTarballName, dataTarball)
|
|
|
|
for _, file := range info.Contents {
|
|
tarContains(t, dataTar, file.Destination)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func extractFileFromTar(tb testing.TB, tarFile []byte, filename string) []byte {
|
|
tb.Helper()
|
|
|
|
tr := tar.NewReader(bytes.NewReader(tarFile))
|
|
for {
|
|
hdr, err := tr.Next()
|
|
if errors.Is(err, io.EOF) {
|
|
break // End of archive
|
|
}
|
|
require.NoError(tb, err)
|
|
|
|
if path.Join("/", hdr.Name) != path.Join("/", filename) {
|
|
continue
|
|
}
|
|
|
|
fileContents, err := ioutil.ReadAll(tr)
|
|
require.NoError(tb, err)
|
|
|
|
return fileContents
|
|
}
|
|
|
|
tb.Fatalf("file %q does not exist in tar", filename)
|
|
|
|
return nil
|
|
}
|
|
|
|
func tarContains(tb testing.TB, tarFile []byte, filename string) bool {
|
|
tb.Helper()
|
|
|
|
tr := tar.NewReader(bytes.NewReader(tarFile))
|
|
for {
|
|
hdr, err := tr.Next()
|
|
if errors.Is(err, io.EOF) {
|
|
break // End of archive
|
|
}
|
|
require.NoError(tb, err)
|
|
|
|
if path.Join("/", hdr.Name) == path.Join("/", filename) { // nolint:gosec
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func tarContents(tb testing.TB, tarFile []byte) []string {
|
|
tb.Helper()
|
|
|
|
contents := []string{}
|
|
|
|
tr := tar.NewReader(bytes.NewReader(tarFile))
|
|
for {
|
|
hdr, err := tr.Next()
|
|
if errors.Is(err, io.EOF) {
|
|
break // End of archive
|
|
}
|
|
require.NoError(tb, err)
|
|
|
|
contents = append(contents, hdr.Name)
|
|
}
|
|
|
|
return contents
|
|
}
|
|
|
|
func extractFileHeaderFromTar(tb testing.TB, tarFile []byte, filename string) *tar.Header {
|
|
tb.Helper()
|
|
|
|
tr := tar.NewReader(bytes.NewReader(tarFile))
|
|
for {
|
|
hdr, err := tr.Next()
|
|
if errors.Is(err, io.EOF) {
|
|
break // End of archive
|
|
}
|
|
require.NoError(tb, err)
|
|
|
|
if path.Join("/", hdr.Name) != path.Join("/", filename) { // nolint:gosec
|
|
continue
|
|
}
|
|
|
|
return hdr
|
|
}
|
|
|
|
tb.Fatalf("file %q does not exist in tar", filename)
|
|
|
|
return nil
|
|
}
|
|
|
|
func inflate(tb testing.TB, nameOrType string, data []byte) []byte {
|
|
tb.Helper()
|
|
|
|
ext := filepath.Ext(nameOrType)
|
|
if ext == "" {
|
|
ext = nameOrType
|
|
} else {
|
|
ext = strings.TrimPrefix(ext, ".")
|
|
}
|
|
|
|
dataReader := bytes.NewReader(data)
|
|
|
|
var (
|
|
inflateReadCloser io.ReadCloser
|
|
err error
|
|
)
|
|
|
|
switch ext {
|
|
case "gz", "gzip":
|
|
inflateReadCloser, err = gzip.NewReader(dataReader)
|
|
require.NoError(tb, err)
|
|
case "xz":
|
|
r, err := xz.NewReader(dataReader, 0)
|
|
require.NoError(tb, err)
|
|
inflateReadCloser = io.NopCloser(r)
|
|
case "tar", "": // no compression
|
|
inflateReadCloser = io.NopCloser(dataReader)
|
|
default:
|
|
tb.Fatalf("invalid inflation type: %s", ext)
|
|
}
|
|
|
|
inflatedData, err := ioutil.ReadAll(inflateReadCloser)
|
|
require.NoError(tb, err)
|
|
|
|
err = inflateReadCloser.Close()
|
|
require.NoError(tb, err)
|
|
|
|
return inflatedData
|
|
}
|
|
|
|
func readAndFormatAsDebChangelog(tb testing.TB, changelogFileName, packageName string) string {
|
|
tb.Helper()
|
|
|
|
changelogEntries, err := chglog.Parse(changelogFileName)
|
|
require.NoError(tb, err)
|
|
|
|
tpl, err := chglog.DebTemplate()
|
|
require.NoError(tb, err)
|
|
|
|
debChangelog, err := chglog.FormatChangelog(&chglog.PackageChangeLog{
|
|
Name: packageName,
|
|
Entries: changelogEntries,
|
|
}, tpl)
|
|
require.NoError(tb, err)
|
|
|
|
return strings.TrimSpace(debChangelog) + "\n"
|
|
}
|
|
|
|
func symlinkTo(tb testing.TB, fileName string) string {
|
|
tb.Helper()
|
|
target, err := filepath.Abs(fileName)
|
|
require.NoError(tb, err)
|
|
|
|
symlinkName := filepath.Join(tb.TempDir(), "symlink")
|
|
err = os.Symlink(target, symlinkName)
|
|
require.NoError(tb, err)
|
|
|
|
return files.ToNixPath(symlinkName)
|
|
}
|
|
|
|
func findDataTarball(tb testing.TB, arFile []byte) string {
|
|
tb.Helper()
|
|
|
|
tr := ar.NewReader(bytes.NewReader(arFile))
|
|
for {
|
|
hdr, err := tr.Next()
|
|
if errors.Is(err, io.EOF) {
|
|
break // End of archive
|
|
}
|
|
require.NoError(tb, err)
|
|
|
|
if strings.HasPrefix(path.Join("/", hdr.Name), "/data.tar") {
|
|
return hdr.Name
|
|
}
|
|
}
|
|
|
|
tb.Fatalf("data taball does not exist in ar")
|
|
|
|
return ""
|
|
}
|
|
|
|
func extractFileFromAr(tb testing.TB, arFile []byte, filename string) []byte {
|
|
tb.Helper()
|
|
|
|
tr := ar.NewReader(bytes.NewReader(arFile))
|
|
for {
|
|
hdr, err := tr.Next()
|
|
if errors.Is(err, io.EOF) {
|
|
break // End of archive
|
|
}
|
|
require.NoError(tb, err)
|
|
|
|
if path.Join("/", hdr.Name) != path.Join("/", filename) {
|
|
continue
|
|
}
|
|
|
|
fileContents, err := ioutil.ReadAll(tr)
|
|
require.NoError(tb, err)
|
|
|
|
return fileContents
|
|
}
|
|
|
|
tb.Fatalf("file %q does not exist in ar", filename)
|
|
|
|
return nil
|
|
}
|
|
|
|
func TestEmptyButRequiredDebFields(t *testing.T) {
|
|
item := nfpm.WithDefaults(&nfpm.Info{
|
|
Name: "foo",
|
|
Version: "v1.0.0",
|
|
})
|
|
Default.SetPackagerDefaults(item)
|
|
|
|
require.Equal(t, "optional", item.Priority)
|
|
require.Equal(t, "Unset Maintainer <unset@localhost>", item.Maintainer)
|
|
|
|
var deb bytes.Buffer
|
|
err := Default.Package(item, &deb)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestArches(t *testing.T) {
|
|
for k := range archToDebian {
|
|
t.Run(k, func(t *testing.T) {
|
|
info := exampleInfo()
|
|
info.Arch = k
|
|
info = ensureValidArch(info)
|
|
require.Equal(t, archToDebian[k], info.Arch)
|
|
})
|
|
}
|
|
|
|
t.Run("override", func(t *testing.T) {
|
|
info := exampleInfo()
|
|
info.Deb.Arch = "foo64"
|
|
info = ensureValidArch(info)
|
|
require.Equal(t, "foo64", info.Arch)
|
|
})
|
|
}
|