mirror of
https://github.com/goreleaser/nfpm
synced 2024-05-07 01:26:08 +02:00
feat: reproducible packages (#748)
* feat: allow to set a build date defaults to $SOURCE_DATE_EPOCH closes #744 closes #734 Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: rename to mtime * docs: fix systemd note closes #739 * fix: improve arch packager * fix: arch test Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: improve apk packager * fix: improve deb special files * fix: reuse keys func * fix: deps Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> --------- Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
This commit is contained in:
parent
c3142513c9
commit
9c4fc0e886
28
apk/apk.go
28
apk/apk.go
|
@ -45,6 +45,7 @@ import (
|
|||
|
||||
"github.com/goreleaser/nfpm/v2"
|
||||
"github.com/goreleaser/nfpm/v2/files"
|
||||
"github.com/goreleaser/nfpm/v2/internal/maps"
|
||||
"github.com/goreleaser/nfpm/v2/internal/sign"
|
||||
gzip "github.com/klauspost/pgzip"
|
||||
)
|
||||
|
@ -343,18 +344,21 @@ func createBuilderControl(info *nfpm.Info, size int64, dataDigest []byte) func(t
|
|||
// bin/echo 'running preinstall.sh' // do stuff here
|
||||
//
|
||||
// exit 0
|
||||
for script, dest := range map[string]string{
|
||||
info.Scripts.PreInstall: ".pre-install",
|
||||
info.APK.Scripts.PreUpgrade: ".pre-upgrade",
|
||||
info.Scripts.PostInstall: ".post-install",
|
||||
info.APK.Scripts.PostUpgrade: ".post-upgrade",
|
||||
info.Scripts.PreRemove: ".pre-deinstall",
|
||||
info.Scripts.PostRemove: ".post-deinstall",
|
||||
} {
|
||||
if script != "" {
|
||||
if err := newScriptInsideTarGz(tw, script, dest); err != nil {
|
||||
return err
|
||||
}
|
||||
scripts := map[string]string{
|
||||
".pre-install": info.Scripts.PreInstall,
|
||||
".pre-upgrade": info.APK.Scripts.PreUpgrade,
|
||||
".post-install": info.Scripts.PostInstall,
|
||||
".post-upgrade": info.APK.Scripts.PostUpgrade,
|
||||
".pre-deinstall": info.Scripts.PreRemove,
|
||||
".post-deinstall": info.Scripts.PostRemove,
|
||||
}
|
||||
for _, name := range maps.Keys(scripts) {
|
||||
path := scripts[name]
|
||||
if path == "" {
|
||||
continue
|
||||
}
|
||||
if err := newScriptInsideTarGz(tw, path, name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
42
arch/arch.go
42
arch/arch.go
|
@ -16,6 +16,7 @@ import (
|
|||
|
||||
"github.com/goreleaser/nfpm/v2"
|
||||
"github.com/goreleaser/nfpm/v2/files"
|
||||
"github.com/goreleaser/nfpm/v2/internal/maps"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/klauspost/pgzip"
|
||||
)
|
||||
|
@ -151,7 +152,7 @@ func (ArchLinux) Package(info *nfpm.Info, w io.Writer) error {
|
|||
// .PKGINFO must be the first entry in .MTREE
|
||||
entries = append([]MtreeEntry{*pkginfoEntry}, entries...)
|
||||
|
||||
err = createMtree(tw, entries)
|
||||
err = createMtree(tw, entries, info.MTime)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create mtree: %w", err)
|
||||
}
|
||||
|
@ -181,25 +182,23 @@ func createFilesInTar(info *nfpm.Info, tw *tar.Writer) ([]MtreeEntry, int64, err
|
|||
Type: files.TypeDir,
|
||||
})
|
||||
|
||||
err := tw.WriteHeader(&tar.Header{
|
||||
if err := tw.WriteHeader(&tar.Header{
|
||||
Name: content.Destination,
|
||||
Mode: int64(content.Mode()),
|
||||
Typeflag: tar.TypeDir,
|
||||
ModTime: content.ModTime(),
|
||||
Uname: content.FileInfo.Owner,
|
||||
Gname: content.FileInfo.Group,
|
||||
})
|
||||
if err != nil {
|
||||
}); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
case files.TypeSymlink:
|
||||
err := tw.WriteHeader(&tar.Header{
|
||||
if err := tw.WriteHeader(&tar.Header{
|
||||
Name: content.Destination,
|
||||
Linkname: content.Source,
|
||||
ModTime: content.ModTime(),
|
||||
Typeflag: tar.TypeSymlink,
|
||||
})
|
||||
if err != nil {
|
||||
}); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
|
@ -311,7 +310,7 @@ func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntr
|
|||
return nil, err
|
||||
}
|
||||
|
||||
builddate := strconv.FormatInt(time.Now().Unix(), 10)
|
||||
builddate := strconv.FormatInt(info.MTime.Unix(), 10)
|
||||
totalSizeStr := strconv.FormatInt(totalSize, 10)
|
||||
|
||||
err = writeKVPairs(buf, map[string]string{
|
||||
|
@ -362,8 +361,7 @@ func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntr
|
|||
if content.Type == files.TypeConfig || content.Type == files.TypeConfigNoReplace {
|
||||
path := files.AsRelativePath(content.Destination)
|
||||
|
||||
err = writeKVPair(buf, "backup", path)
|
||||
if err != nil {
|
||||
if err := writeKVPair(buf, "backup", path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@ -376,7 +374,7 @@ func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntr
|
|||
Mode: 0o644,
|
||||
Name: ".PKGINFO",
|
||||
Size: int64(size),
|
||||
ModTime: time.Now(),
|
||||
ModTime: info.MTime,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -388,14 +386,13 @@ func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntr
|
|||
r := io.TeeReader(buf, md5Hash)
|
||||
r = io.TeeReader(r, sha256Hash)
|
||||
|
||||
_, err = io.Copy(tw, r)
|
||||
if err != nil {
|
||||
if _, err = io.Copy(tw, r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &MtreeEntry{
|
||||
Destination: ".PKGINFO",
|
||||
Time: time.Now().Unix(),
|
||||
Time: info.MTime.Unix(),
|
||||
Mode: 0o644,
|
||||
Size: int64(size),
|
||||
Type: files.TypeFile,
|
||||
|
@ -404,10 +401,9 @@ func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntr
|
|||
}, nil
|
||||
}
|
||||
|
||||
func writeKVPairs(w io.Writer, s map[string]string) error {
|
||||
for key, val := range s {
|
||||
err := writeKVPair(w, key, val)
|
||||
if err != nil {
|
||||
func writeKVPairs(w io.Writer, pairs map[string]string) error {
|
||||
for _, key := range maps.Keys(pairs) {
|
||||
if err := writeKVPair(w, key, pairs[key]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -485,7 +481,7 @@ func (me *MtreeEntry) WriteTo(w io.Writer) (int64, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func createMtree(tw *tar.Writer, entries []MtreeEntry) error {
|
||||
func createMtree(tw *tar.Writer, entries []MtreeEntry, mtime time.Time) error {
|
||||
buf := &bytes.Buffer{}
|
||||
gw := pgzip.NewWriter(buf)
|
||||
defer gw.Close()
|
||||
|
@ -509,7 +505,7 @@ func createMtree(tw *tar.Writer, entries []MtreeEntry) error {
|
|||
Mode: 0o644,
|
||||
Name: ".MTREE",
|
||||
Size: int64(buf.Len()),
|
||||
ModTime: time.Now(),
|
||||
ModTime: mtime,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -562,7 +558,7 @@ func createScripts(info *nfpm.Info, tw *tar.Writer) error {
|
|||
Mode: 0o644,
|
||||
Name: ".INSTALL",
|
||||
Size: int64(buf.Len()),
|
||||
ModTime: time.Now(),
|
||||
ModTime: info.MTime,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -573,10 +569,10 @@ func createScripts(info *nfpm.Info, tw *tar.Writer) error {
|
|||
}
|
||||
|
||||
func writeScripts(w io.Writer, scripts map[string]string) error {
|
||||
for script, path := range scripts {
|
||||
for _, script := range maps.Keys(scripts) {
|
||||
fmt.Fprintf(w, "function %s() {\n", script)
|
||||
|
||||
fl, err := os.Open(path)
|
||||
fl, err := os.Open(scripts[script])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -3,11 +3,12 @@ package arch
|
|||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/goreleaser/nfpm/v2"
|
||||
"github.com/goreleaser/nfpm/v2/files"
|
||||
|
@ -16,6 +17,8 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var mtime = time.Date(2023, 11, 5, 23, 15, 17, 0, time.UTC)
|
||||
|
||||
func exampleInfo() *nfpm.Info {
|
||||
return nfpm.WithDefaults(&nfpm.Info{
|
||||
Name: "foo-test",
|
||||
|
@ -297,7 +300,7 @@ func TestArchMtree(t *testing.T) {
|
|||
Mode: 0o777,
|
||||
Type: files.TypeSymlink,
|
||||
},
|
||||
})
|
||||
}, mtime)
|
||||
require.NoError(t, err)
|
||||
|
||||
tw.Close()
|
||||
|
@ -322,6 +325,7 @@ func TestGlob(t *testing.T) {
|
|||
Name: "nfpm-repro",
|
||||
Version: "1.0.0",
|
||||
Maintainer: "asdfasdf",
|
||||
MTime: mtime,
|
||||
|
||||
Overridables: nfpm.Overridables{
|
||||
Contents: files.Contents{
|
||||
|
@ -329,7 +333,8 @@ func TestGlob(t *testing.T) {
|
|||
Destination: "/usr/share/nfpm-repro",
|
||||
Source: "../files/testdata/globtest/different-sizes/*/*.txt",
|
||||
FileInfo: &files.ContentFileInfo{
|
||||
Mode: 0o644,
|
||||
Mode: 0o644,
|
||||
MTime: mtime,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -361,15 +366,16 @@ func TestGlob(t *testing.T) {
|
|||
mtreeContentBts, err := io.ReadAll(mtreeGzip)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedTime := fmt.Sprintf("time=%d.0", mtime.Unix())
|
||||
expected := map[string][]string{
|
||||
"./.PKGINFO": {"mode=644", "size=185", "type=file"},
|
||||
"./usr/": {"mode=755", "type=dir"},
|
||||
"./usr/share/": {"mode=755", "type=dir"},
|
||||
"./usr/share/nfpm-repro/": {"mode=755", "type=dir"},
|
||||
"./usr/share/nfpm-repro/a/": {"mode=755", "type=dir"},
|
||||
"./usr/share/nfpm-repro/a/a.txt": {"mode=644", "size=4", "type=file", "md5digest=d3b07384d113edec49eaa6238ad5ff00", "sha256digest=b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"},
|
||||
"./usr/share/nfpm-repro/b/": {"mode=755", "type=dir"},
|
||||
"./usr/share/nfpm-repro/b/b.txt": {"mode=644", "size=7", "type=file", "md5digest=551a67cc6e06de1910061fe318d28f72", "sha256digest=73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f"},
|
||||
"./.PKGINFO": {expectedTime, "mode=644", "size=185", "type=file", "md5digest=408daafbd01f6622f0bfd6ccdf96735f", "sha256digest=98468a4b87a677958f872662f476b14ff28cc1f8c6bd0029869e21946b4cd8d2"},
|
||||
"./usr/": {expectedTime, "mode=755", "type=dir"},
|
||||
"./usr/share/": {expectedTime, "mode=755", "type=dir"},
|
||||
"./usr/share/nfpm-repro/": {expectedTime, "mode=755", "type=dir"},
|
||||
"./usr/share/nfpm-repro/a/": {expectedTime, "mode=755", "type=dir"},
|
||||
"./usr/share/nfpm-repro/a/a.txt": {expectedTime, "mode=644", "size=4", "type=file", "md5digest=d3b07384d113edec49eaa6238ad5ff00", "sha256digest=b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"},
|
||||
"./usr/share/nfpm-repro/b/": {expectedTime, "mode=755", "type=dir"},
|
||||
"./usr/share/nfpm-repro/b/b.txt": {expectedTime, "mode=644", "size=7", "type=file", "md5digest=551a67cc6e06de1910061fe318d28f72", "sha256digest=73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f"},
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(string(mtreeContentBts), "\n") {
|
||||
|
@ -379,8 +385,6 @@ func TestGlob(t *testing.T) {
|
|||
parts := strings.Fields(line)
|
||||
filename := parts[0]
|
||||
expect := expected[filename]
|
||||
modTime := parts[1]
|
||||
require.Regexp(t, regexp.MustCompile(`time=\d+\.\d`), modTime)
|
||||
require.Equal(t, expect, parts[2:len(expect)+2], filename)
|
||||
require.Equal(t, expect, strings.Split(line, " ")[1:], filename)
|
||||
}
|
||||
}
|
||||
|
|
130
deb/deb.go
130
deb/deb.go
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/goreleaser/nfpm/v2"
|
||||
"github.com/goreleaser/nfpm/v2/deprecation"
|
||||
"github.com/goreleaser/nfpm/v2/files"
|
||||
"github.com/goreleaser/nfpm/v2/internal/maps"
|
||||
"github.com/goreleaser/nfpm/v2/internal/sign"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/ulikunitz/xz"
|
||||
|
@ -125,15 +126,15 @@ func (d *Deb) Package(info *nfpm.Info, deb io.Writer) (err error) { // nolint: f
|
|||
return fmt.Errorf("cannot write ar header to deb file: %w", err)
|
||||
}
|
||||
|
||||
if err := addArFile(w, "debian-binary", debianBinary); err != nil {
|
||||
if err := addArFile(w, "debian-binary", debianBinary, info.MTime); err != nil {
|
||||
return fmt.Errorf("cannot pack debian-binary: %w", err)
|
||||
}
|
||||
|
||||
if err := addArFile(w, "control.tar.gz", controlTarGz); err != nil {
|
||||
if err := addArFile(w, "control.tar.gz", controlTarGz, info.MTime); err != nil {
|
||||
return fmt.Errorf("cannot add control.tar.gz to deb: %w", err)
|
||||
}
|
||||
|
||||
if err := addArFile(w, dataTarballName, dataTarball); err != nil {
|
||||
if err := addArFile(w, dataTarballName, dataTarball, info.MTime); err != nil {
|
||||
return fmt.Errorf("cannot add data.tar.gz to deb: %w", err)
|
||||
}
|
||||
|
||||
|
@ -143,7 +144,7 @@ func (d *Deb) Package(info *nfpm.Info, deb io.Writer) (err error) { // nolint: f
|
|||
return err
|
||||
}
|
||||
|
||||
if err := addArFile(w, "_gpg"+sigType, sig); err != nil {
|
||||
if err := addArFile(w, "_gpg"+sigType, sig, info.MTime); err != nil {
|
||||
return &nfpm.ErrSigningFailure{
|
||||
Err: fmt.Errorf("add signature to ar file: %w", err),
|
||||
}
|
||||
|
@ -255,7 +256,7 @@ func newDpkgSigFileLine(name string, fileContent []byte) dpkgSigFileLine {
|
|||
func readDpkgSigData(info *nfpm.Info, debianBinary, controlTarGz, dataTarball []byte) (io.Reader, error) {
|
||||
data := dpkgSigData{
|
||||
Signer: info.Deb.Signature.Signer,
|
||||
Date: time.Now(),
|
||||
Date: info.MTime,
|
||||
Role: info.Deb.Signature.Type,
|
||||
Files: []dpkgSigFileLine{
|
||||
newDpkgSigFileLine("debian-binary", debianBinary),
|
||||
|
@ -290,12 +291,12 @@ func (*Deb) SetPackagerDefaults(info *nfpm.Info) {
|
|||
}
|
||||
}
|
||||
|
||||
func addArFile(w *ar.Writer, name string, body []byte) error {
|
||||
func addArFile(w *ar.Writer, name string, body []byte, date time.Time) error {
|
||||
header := ar.Header{
|
||||
Name: files.ToNixPath(name),
|
||||
Size: int64(len(body)),
|
||||
Mode: 0o644,
|
||||
ModTime: time.Now(),
|
||||
ModTime: date,
|
||||
}
|
||||
if err := w.WriteHeader(&header); err != nil {
|
||||
return fmt.Errorf("cannot write file header: %w", err)
|
||||
|
@ -510,7 +511,7 @@ func createChangelogInsideDataTar(
|
|||
return 0, err
|
||||
}
|
||||
|
||||
if err = newFileInsideTar(tarw, fileName, changelogData); err != nil {
|
||||
if err = newFileInsideTar(tarw, fileName, changelogData, info.MTime); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
|
@ -554,28 +555,18 @@ func createControl(instSize int64, md5sums []byte, info *nfpm.Info) (controlTarG
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// ensure predefined sort order of these items
|
||||
filesToCreateNames := []string{
|
||||
"./control",
|
||||
"./md5sums",
|
||||
"./conffiles",
|
||||
if err := newFileInsideTar(out, "./control", body.Bytes(), info.MTime); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := newFileInsideTar(out, "./md5sums", md5sums, info.MTime); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := newFileInsideTar(out, "./conffiles", conffiles(info), info.MTime); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filesToCreateContent := [][]byte{
|
||||
body.Bytes(),
|
||||
md5sums,
|
||||
conffiles(info),
|
||||
}
|
||||
|
||||
triggers := createTriggers(info)
|
||||
if len(triggers) > 0 {
|
||||
filesToCreateNames = append(filesToCreateNames, "./triggers")
|
||||
filesToCreateContent = append(filesToCreateContent, triggers)
|
||||
}
|
||||
|
||||
for idx, name := range filesToCreateNames {
|
||||
content := filesToCreateContent[idx]
|
||||
if err := newFileInsideTar(out, name, content); err != nil {
|
||||
if triggers := createTriggers(info); len(triggers) > 0 {
|
||||
if err := newFileInsideTar(out, "./triggers", triggers, info.MTime); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@ -585,41 +576,50 @@ func createControl(instSize int64, md5sums []byte, info *nfpm.Info) (controlTarG
|
|||
mode int64
|
||||
}
|
||||
|
||||
specialFiles := map[string]*fileAndMode{}
|
||||
specialFiles[info.Scripts.PreInstall] = &fileAndMode{
|
||||
fileName: "preinst",
|
||||
mode: 0o755,
|
||||
}
|
||||
specialFiles[info.Scripts.PostInstall] = &fileAndMode{
|
||||
fileName: "postinst",
|
||||
mode: 0o755,
|
||||
}
|
||||
specialFiles[info.Scripts.PreRemove] = &fileAndMode{
|
||||
fileName: "prerm",
|
||||
mode: 0o755,
|
||||
}
|
||||
specialFiles[info.Scripts.PostRemove] = &fileAndMode{
|
||||
fileName: "postrm",
|
||||
mode: 0o755,
|
||||
}
|
||||
specialFiles[info.Overridables.Deb.Scripts.Rules] = &fileAndMode{
|
||||
fileName: "rules",
|
||||
mode: 0o755,
|
||||
}
|
||||
specialFiles[info.Overridables.Deb.Scripts.Templates] = &fileAndMode{
|
||||
fileName: "templates",
|
||||
mode: 0o644,
|
||||
}
|
||||
specialFiles[info.Overridables.Deb.Scripts.Config] = &fileAndMode{
|
||||
fileName: "config",
|
||||
mode: 0o755,
|
||||
specialFiles := map[string]*fileAndMode{
|
||||
"preinst": {
|
||||
fileName: info.Scripts.PreInstall,
|
||||
mode: 0o755,
|
||||
},
|
||||
"postinst": {
|
||||
fileName: info.Scripts.PostInstall,
|
||||
mode: 0o755,
|
||||
},
|
||||
"prerm": {
|
||||
fileName: info.Scripts.PreRemove,
|
||||
mode: 0o755,
|
||||
},
|
||||
"postrm": {
|
||||
fileName: info.Scripts.PostRemove,
|
||||
mode: 0o755,
|
||||
},
|
||||
"rules": {
|
||||
fileName: info.Overridables.Deb.Scripts.Rules,
|
||||
mode: 0o755,
|
||||
},
|
||||
"templates": {
|
||||
fileName: info.Overridables.Deb.Scripts.Templates,
|
||||
mode: 0o644,
|
||||
},
|
||||
"config": {
|
||||
fileName: info.Overridables.Deb.Scripts.Config,
|
||||
mode: 0o755,
|
||||
},
|
||||
}
|
||||
|
||||
for path, destMode := range specialFiles {
|
||||
if path != "" {
|
||||
if err := newFilePathInsideTar(out, path, destMode.fileName, destMode.mode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, filename := range maps.Keys(specialFiles) {
|
||||
dets := specialFiles[filename]
|
||||
if dets.fileName == "" {
|
||||
continue
|
||||
}
|
||||
if err := newFilePathInsideTar(
|
||||
out,
|
||||
dets.fileName,
|
||||
filename,
|
||||
dets.mode,
|
||||
info.MTime,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -642,18 +642,18 @@ func newItemInsideTar(out *tar.Writer, content []byte, header *tar.Header) error
|
|||
return nil
|
||||
}
|
||||
|
||||
func newFileInsideTar(out *tar.Writer, name string, content []byte) error {
|
||||
func newFileInsideTar(out *tar.Writer, name string, content []byte, modtime time.Time) error {
|
||||
return newItemInsideTar(out, content, &tar.Header{
|
||||
Name: files.AsExplicitRelativePath(name),
|
||||
Size: int64(len(content)),
|
||||
Mode: 0o644,
|
||||
ModTime: time.Unix(0, 0),
|
||||
ModTime: modtime,
|
||||
Typeflag: tar.TypeReg,
|
||||
Format: tar.FormatGNU,
|
||||
})
|
||||
}
|
||||
|
||||
func newFilePathInsideTar(out *tar.Writer, path, dest string, mode int64) error {
|
||||
func newFilePathInsideTar(out *tar.Writer, path, dest string, mode int64, modtime time.Time) error {
|
||||
file, err := os.Open(path) //nolint:gosec
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -666,7 +666,7 @@ func newFilePathInsideTar(out *tar.Writer, path, dest string, mode int64) error
|
|||
Name: files.AsExplicitRelativePath(dest),
|
||||
Size: int64(len(content)),
|
||||
Mode: mode,
|
||||
ModTime: time.Unix(0, 0),
|
||||
ModTime: modtime,
|
||||
Typeflag: tar.TypeReg,
|
||||
Format: tar.FormatGNU,
|
||||
})
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/blakesmith/ar"
|
||||
"github.com/goreleaser/chglog"
|
||||
|
@ -30,6 +31,8 @@ import (
|
|||
// nolint: gochecknoglobals
|
||||
var update = flag.Bool("update", false, "update .golden files")
|
||||
|
||||
var mtime = time.Date(2023, 11, 5, 23, 15, 17, 0, time.UTC)
|
||||
|
||||
func exampleInfo() *nfpm.Info {
|
||||
return nfpm.WithDefaults(&nfpm.Info{
|
||||
Name: "foo",
|
||||
|
@ -269,8 +272,8 @@ 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))
|
||||
require.Error(t, newFilePathInsideTar(out, "doesnotexit", "templates", 0o644, mtime))
|
||||
require.NoError(t, newFilePathInsideTar(out, filePath, "templates", 0o644, mtime))
|
||||
in := tar.NewReader(&w)
|
||||
header, err := in.Next()
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -105,7 +105,7 @@ func (c Contents) ContainsDestination(dst string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (c *Content) WithFileInfoDefaults(umask fs.FileMode) *Content {
|
||||
func (c *Content) WithFileInfoDefaults(umask fs.FileMode, mtime time.Time) *Content {
|
||||
cc := &Content{
|
||||
Source: c.Source,
|
||||
Destination: c.Destination,
|
||||
|
@ -129,7 +129,7 @@ func (c *Content) WithFileInfoDefaults(umask fs.FileMode) *Content {
|
|||
cc.FileInfo.Mode = 0o755
|
||||
}
|
||||
if (cc.Type == TypeDir || cc.Type == TypeImplicitDir) && cc.FileInfo.MTime.IsZero() {
|
||||
cc.FileInfo.MTime = time.Now()
|
||||
cc.FileInfo.MTime = mtime
|
||||
}
|
||||
|
||||
// determine if we still need info
|
||||
|
@ -152,7 +152,7 @@ func (c *Content) WithFileInfoDefaults(umask fs.FileMode) *Content {
|
|||
}
|
||||
|
||||
if cc.FileInfo.MTime.IsZero() {
|
||||
cc.FileInfo.MTime = time.Now().UTC()
|
||||
cc.FileInfo.MTime = mtime
|
||||
}
|
||||
return cc
|
||||
}
|
||||
|
@ -234,7 +234,13 @@ func (c *Content) String() string {
|
|||
//
|
||||
// If no packager is specified, only the files that are relevant for any
|
||||
// packager are considered.
|
||||
func PrepareForPackager(rawContents Contents, umask fs.FileMode, packager string, disableGlobbing bool) (Contents, error) {
|
||||
func PrepareForPackager(
|
||||
rawContents Contents,
|
||||
umask fs.FileMode,
|
||||
packager string,
|
||||
disableGlobbing bool,
|
||||
mtime time.Time,
|
||||
) (Contents, error) {
|
||||
contentMap := make(map[string]*Content)
|
||||
|
||||
for _, content := range rawContents {
|
||||
|
@ -250,12 +256,12 @@ func PrepareForPackager(rawContents Contents, umask fs.FileMode, packager string
|
|||
return nil, contentCollisionError(content, presentContent)
|
||||
}
|
||||
|
||||
err := addParents(contentMap, content.Destination)
|
||||
err := addParents(contentMap, content.Destination, mtime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cc := content.WithFileInfoDefaults(umask)
|
||||
cc := content.WithFileInfoDefaults(umask, mtime)
|
||||
cc.Source = ToNixPath(cc.Source)
|
||||
cc.Destination = NormalizeAbsoluteDirPath(cc.Destination)
|
||||
contentMap[cc.Destination] = cc
|
||||
|
@ -269,17 +275,17 @@ func PrepareForPackager(rawContents Contents, umask fs.FileMode, packager string
|
|||
return nil, contentCollisionError(content, presentContent)
|
||||
}
|
||||
|
||||
err := addParents(contentMap, content.Destination)
|
||||
err := addParents(contentMap, content.Destination, mtime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cc := content.WithFileInfoDefaults(umask)
|
||||
cc := content.WithFileInfoDefaults(umask, mtime)
|
||||
cc.Source = ToNixPath(cc.Source)
|
||||
cc.Destination = NormalizeAbsoluteFilePath(cc.Destination)
|
||||
contentMap[cc.Destination] = cc
|
||||
case TypeTree:
|
||||
err := addTree(contentMap, content, umask)
|
||||
err := addTree(contentMap, content, umask, mtime)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("add tree: %w", err)
|
||||
}
|
||||
|
@ -293,8 +299,7 @@ func PrepareForPackager(rawContents Contents, umask fs.FileMode, packager string
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = addGlobbedFiles(contentMap, globbed, content, umask)
|
||||
if err != nil {
|
||||
if err := addGlobbedFiles(contentMap, globbed, content, umask, mtime); err != nil {
|
||||
return nil, fmt.Errorf("add globbed files from %q: %w", content.Source, err)
|
||||
}
|
||||
default:
|
||||
|
@ -336,7 +341,7 @@ func isRelevantForPackager(packager string, content *Content) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func addParents(contentMap map[string]*Content, path string) error {
|
||||
func addParents(contentMap map[string]*Content, path string, mtime time.Time) error {
|
||||
for _, parent := range sortedParents(path) {
|
||||
parent = NormalizeAbsoluteDirPath(parent)
|
||||
// check for content collision and just overwrite previously created
|
||||
|
@ -362,7 +367,7 @@ func addParents(contentMap map[string]*Content, path string) error {
|
|||
Owner: "root",
|
||||
Group: "root",
|
||||
Mode: 0o755,
|
||||
MTime: time.Now(),
|
||||
MTime: mtime,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -390,7 +395,13 @@ func sortedParents(dst string) []string {
|
|||
return paths
|
||||
}
|
||||
|
||||
func addGlobbedFiles(all map[string]*Content, globbed map[string]string, origFile *Content, umask fs.FileMode) error {
|
||||
func addGlobbedFiles(
|
||||
all map[string]*Content,
|
||||
globbed map[string]string,
|
||||
origFile *Content,
|
||||
umask fs.FileMode,
|
||||
mtime time.Time,
|
||||
) error {
|
||||
for src, dst := range globbed {
|
||||
dst = NormalizeAbsoluteFilePath(dst)
|
||||
presentContent, destinationOccupied := all[dst]
|
||||
|
@ -400,8 +411,7 @@ func addGlobbedFiles(all map[string]*Content, globbed map[string]string, origFil
|
|||
return contentCollisionError(&c, presentContent)
|
||||
}
|
||||
|
||||
err := addParents(all, dst)
|
||||
if err != nil {
|
||||
if err := addParents(all, dst, mtime); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -419,7 +429,7 @@ func addGlobbedFiles(all map[string]*Content, globbed map[string]string, origFil
|
|||
Type: origFile.Type,
|
||||
FileInfo: newFileInfo,
|
||||
Packager: origFile.Packager,
|
||||
}).WithFileInfoDefaults(umask)
|
||||
}).WithFileInfoDefaults(umask, mtime)
|
||||
if dst, err := os.Readlink(src); err == nil {
|
||||
newFile.Source = dst
|
||||
newFile.Type = TypeSymlink
|
||||
|
@ -431,7 +441,12 @@ func addGlobbedFiles(all map[string]*Content, globbed map[string]string, origFil
|
|||
return nil
|
||||
}
|
||||
|
||||
func addTree(all map[string]*Content, tree *Content, umask os.FileMode) error {
|
||||
func addTree(
|
||||
all map[string]*Content,
|
||||
tree *Content,
|
||||
umask os.FileMode,
|
||||
mtime time.Time,
|
||||
) error {
|
||||
if tree.Destination != "/" && tree.Destination != "" {
|
||||
presentContent, destinationOccupied := all[NormalizeAbsoluteDirPath(tree.Destination)]
|
||||
if destinationOccupied && presentContent.Type != TypeImplicitDir {
|
||||
|
@ -439,7 +454,7 @@ func addTree(all map[string]*Content, tree *Content, umask os.FileMode) error {
|
|||
}
|
||||
}
|
||||
|
||||
err := addParents(all, tree.Destination)
|
||||
err := addParents(all, tree.Destination, mtime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -491,7 +506,7 @@ func addTree(all map[string]*Content, tree *Content, umask os.FileMode) error {
|
|||
}
|
||||
}
|
||||
|
||||
all[c.Destination] = c.WithFileInfoDefaults(umask)
|
||||
all[c.Destination] = c.WithFileInfoDefaults(umask, mtime)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
|
|
@ -17,6 +17,8 @@ import (
|
|||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var mtime = time.Date(2023, 11, 5, 23, 15, 17, 0, time.UTC)
|
||||
|
||||
type testStruct struct {
|
||||
Contents files.Contents `yaml:"contents"`
|
||||
}
|
||||
|
@ -68,7 +70,13 @@ contents:
|
|||
err := dec.Decode(&config)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, config.Contents, 3)
|
||||
parsedContents, err := files.PrepareForPackager(config.Contents, 0o133, "", false)
|
||||
parsedContents, err := files.PrepareForPackager(
|
||||
config.Contents,
|
||||
0o133,
|
||||
"",
|
||||
false,
|
||||
mtime,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
for _, c := range parsedContents {
|
||||
switch c.Source {
|
||||
|
@ -99,7 +107,13 @@ contents:
|
|||
err := dec.Decode(&config)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, config.Contents, 1)
|
||||
parsedContents, err := files.PrepareForPackager(config.Contents, 0, "", true)
|
||||
parsedContents, err := files.PrepareForPackager(
|
||||
config.Contents,
|
||||
0,
|
||||
"",
|
||||
true,
|
||||
mtime,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
present := false
|
||||
|
||||
|
@ -129,7 +143,13 @@ contents:
|
|||
err := dec.Decode(&config)
|
||||
require.NoError(t, err)
|
||||
|
||||
config.Contents, err = files.PrepareForPackager(config.Contents, 0, "", true)
|
||||
config.Contents, err = files.PrepareForPackager(
|
||||
config.Contents,
|
||||
0,
|
||||
"",
|
||||
true,
|
||||
mtime,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, config.Contents, 1)
|
||||
|
||||
|
@ -159,7 +179,13 @@ contents:
|
|||
err := dec.Decode(&config)
|
||||
require.NoError(t, err)
|
||||
|
||||
config.Contents, err = files.PrepareForPackager(config.Contents, 0, "rpm", true)
|
||||
config.Contents, err = files.PrepareForPackager(
|
||||
config.Contents,
|
||||
0,
|
||||
"rpm",
|
||||
true,
|
||||
mtime,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, config.Contents, 1)
|
||||
|
||||
|
@ -191,7 +217,13 @@ contents:
|
|||
err := dec.Decode(&config)
|
||||
require.NoError(t, err)
|
||||
|
||||
config.Contents, err = files.PrepareForPackager(config.Contents, 0, "", true)
|
||||
config.Contents, err = files.PrepareForPackager(
|
||||
config.Contents,
|
||||
0,
|
||||
"",
|
||||
true,
|
||||
mtime,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
config.Contents = withoutFileInfo(config.Contents)
|
||||
|
||||
|
@ -251,7 +283,13 @@ contents:
|
|||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
_, err := files.PrepareForPackager(config.Contents, 0, "", false)
|
||||
_, err := files.PrepareForPackager(
|
||||
config.Contents,
|
||||
0,
|
||||
"",
|
||||
false,
|
||||
mtime,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
}
|
||||
|
@ -265,7 +303,13 @@ func TestCollision(t *testing.T) {
|
|||
{Source: "../testdata/whatever2.conf", Destination: "/samedestination"},
|
||||
}
|
||||
|
||||
_, err := files.PrepareForPackager(configuredFiles, 0, "", true)
|
||||
_, err := files.PrepareForPackager(
|
||||
configuredFiles,
|
||||
0,
|
||||
"",
|
||||
true,
|
||||
mtime,
|
||||
)
|
||||
require.ErrorIs(t, err, files.ErrContentCollision)
|
||||
})
|
||||
|
||||
|
@ -275,7 +319,13 @@ func TestCollision(t *testing.T) {
|
|||
{Source: "../testdata/whatever2.conf", Destination: "/samedestination", Packager: "bar"},
|
||||
}
|
||||
|
||||
_, err := files.PrepareForPackager(configuredFiles, 0, "foo", true)
|
||||
_, err := files.PrepareForPackager(
|
||||
configuredFiles,
|
||||
0,
|
||||
"foo",
|
||||
true,
|
||||
mtime,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
|
@ -285,7 +335,13 @@ func TestCollision(t *testing.T) {
|
|||
{Source: "../testdata/whatever2.conf", Destination: "/samedestination", Packager: ""},
|
||||
}
|
||||
|
||||
_, err := files.PrepareForPackager(configuredFiles, 0, "foo", true)
|
||||
_, err := files.PrepareForPackager(
|
||||
configuredFiles,
|
||||
0,
|
||||
"foo",
|
||||
true,
|
||||
mtime,
|
||||
)
|
||||
require.ErrorIs(t, err, files.ErrContentCollision)
|
||||
})
|
||||
}
|
||||
|
@ -316,7 +372,13 @@ func TestDisableGlobbing(t *testing.T) {
|
|||
content := testCase
|
||||
|
||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||
result, err := files.PrepareForPackager(files.Contents{&content}, 0, "", disableGlobbing)
|
||||
result, err := files.PrepareForPackager(
|
||||
files.Contents{&content},
|
||||
0,
|
||||
"",
|
||||
disableGlobbing,
|
||||
mtime,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("expand content globs: %v", err)
|
||||
}
|
||||
|
@ -358,12 +420,18 @@ func TestGlobbingWhenFilesHaveBrackets(t *testing.T) {
|
|||
if runtime.GOOS == "windows" {
|
||||
t.Skip("doesn't work on windows")
|
||||
}
|
||||
result, err := files.PrepareForPackager(files.Contents{
|
||||
{
|
||||
Source: "./testdata/\\{test\\}/",
|
||||
Destination: ".",
|
||||
result, err := files.PrepareForPackager(
|
||||
files.Contents{
|
||||
{
|
||||
Source: "./testdata/\\{test\\}/",
|
||||
Destination: ".",
|
||||
},
|
||||
},
|
||||
}, 0, "", false)
|
||||
0,
|
||||
"",
|
||||
false,
|
||||
mtime,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("expand content globs: %v", err)
|
||||
}
|
||||
|
@ -396,15 +464,21 @@ func TestGlobbingWhenFilesHaveBrackets(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGlobbingFilesWithDifferentSizesWithFileInfo(t *testing.T) {
|
||||
result, err := files.PrepareForPackager(files.Contents{
|
||||
{
|
||||
Source: "./testdata/globtest/different-sizes/**/*",
|
||||
Destination: ".",
|
||||
FileInfo: &files.ContentFileInfo{
|
||||
Mode: 0o777,
|
||||
result, err := files.PrepareForPackager(
|
||||
files.Contents{
|
||||
{
|
||||
Source: "./testdata/globtest/different-sizes/**/*",
|
||||
Destination: ".",
|
||||
FileInfo: &files.ContentFileInfo{
|
||||
Mode: 0o777,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, 0, "", false)
|
||||
0,
|
||||
"",
|
||||
false,
|
||||
mtime,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("expand content globs: %v", err)
|
||||
}
|
||||
|
@ -421,12 +495,18 @@ func TestGlobbingFilesWithDifferentSizesWithFileInfo(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDestEndsWithSlash(t *testing.T) {
|
||||
result, err := files.PrepareForPackager(files.Contents{
|
||||
{
|
||||
Source: "./testdata/globtest/a.txt",
|
||||
Destination: "./foo/",
|
||||
result, err := files.PrepareForPackager(
|
||||
files.Contents{
|
||||
{
|
||||
Source: "./testdata/globtest/a.txt",
|
||||
Destination: "./foo/",
|
||||
},
|
||||
},
|
||||
}, 0, "", false)
|
||||
0,
|
||||
"",
|
||||
false,
|
||||
mtime,
|
||||
)
|
||||
result = withoutImplicitDirs(result)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, result, 1)
|
||||
|
@ -443,7 +523,13 @@ contents:
|
|||
`))
|
||||
dec.KnownFields(true)
|
||||
require.NoError(t, dec.Decode(&config))
|
||||
_, err := files.PrepareForPackager(config.Contents, 0, "", false)
|
||||
_, err := files.PrepareForPackager(
|
||||
config.Contents,
|
||||
0,
|
||||
"",
|
||||
false,
|
||||
mtime,
|
||||
)
|
||||
require.EqualError(t, err, "invalid content type: filr")
|
||||
}
|
||||
|
||||
|
@ -474,17 +560,29 @@ contents:
|
|||
`))
|
||||
dec.KnownFields(true)
|
||||
require.NoError(t, dec.Decode(&config))
|
||||
_, err := files.PrepareForPackager(config.Contents, 0, "", false)
|
||||
_, err := files.PrepareForPackager(
|
||||
config.Contents,
|
||||
0,
|
||||
"",
|
||||
false,
|
||||
mtime,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestImplicitDirectories(t *testing.T) {
|
||||
results, err := files.PrepareForPackager(files.Contents{
|
||||
{
|
||||
Source: "./testdata/globtest/a.txt",
|
||||
Destination: "./foo/bar/baz",
|
||||
results, err := files.PrepareForPackager(
|
||||
files.Contents{
|
||||
{
|
||||
Source: "./testdata/globtest/a.txt",
|
||||
Destination: "./foo/bar/baz",
|
||||
},
|
||||
},
|
||||
}, 0, "", false)
|
||||
0,
|
||||
"",
|
||||
false,
|
||||
mtime,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := files.Contents{
|
||||
|
@ -557,7 +655,13 @@ func TestRelevantFiles(t *testing.T) {
|
|||
}
|
||||
|
||||
t.Run("deb", func(t *testing.T) {
|
||||
results, err := files.PrepareForPackager(contents, 0, "deb", false)
|
||||
results, err := files.PrepareForPackager(
|
||||
contents,
|
||||
0,
|
||||
"deb",
|
||||
false,
|
||||
mtime,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, files.Contents{
|
||||
{
|
||||
|
@ -580,7 +684,13 @@ func TestRelevantFiles(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("rpm", func(t *testing.T) {
|
||||
results, err := files.PrepareForPackager(contents, 0, "rpm", false)
|
||||
results, err := files.PrepareForPackager(
|
||||
contents,
|
||||
0,
|
||||
"rpm",
|
||||
false,
|
||||
mtime,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, files.Contents{
|
||||
{
|
||||
|
@ -623,7 +733,13 @@ func TestRelevantFiles(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("apk", func(t *testing.T) {
|
||||
results, err := files.PrepareForPackager(contents, 0, "apk", false)
|
||||
results, err := files.PrepareForPackager(
|
||||
contents,
|
||||
0,
|
||||
"apk",
|
||||
false,
|
||||
mtime,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, files.Contents{
|
||||
{
|
||||
|
@ -636,13 +752,19 @@ func TestRelevantFiles(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTree(t *testing.T) {
|
||||
results, err := files.PrepareForPackager(files.Contents{
|
||||
{
|
||||
Source: filepath.Join("testdata", "tree"),
|
||||
Destination: "/base",
|
||||
Type: files.TypeTree,
|
||||
results, err := files.PrepareForPackager(
|
||||
files.Contents{
|
||||
{
|
||||
Source: filepath.Join("testdata", "tree"),
|
||||
Destination: "/base",
|
||||
Type: files.TypeTree,
|
||||
},
|
||||
},
|
||||
}, 0, "", false)
|
||||
0,
|
||||
"",
|
||||
false,
|
||||
mtime,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, files.Contents{
|
||||
|
|
1
go.mod
1
go.mod
|
@ -23,6 +23,7 @@ require (
|
|||
github.com/stretchr/testify v1.8.4
|
||||
github.com/ulikunitz/xz v0.5.11
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8
|
||||
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
|
|
4
go.sum
4
go.sum
|
@ -180,6 +180,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz
|
|||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8=
|
||||
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
|
@ -237,8 +239,8 @@ golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtn
|
|||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package maps
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
func Keys[T any](m map[string]T) []string {
|
||||
keys := maps.Keys(m)
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
74
nfpm.go
74
nfpm.go
|
@ -8,8 +8,10 @@ import (
|
|||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"dario.cat/mergo"
|
||||
"github.com/AlekSi/pointer"
|
||||
|
@ -255,25 +257,26 @@ func (c *Config) expandEnvVars() {
|
|||
// Info contains information about a single package.
|
||||
type Info struct {
|
||||
Overridables `yaml:",inline" json:",inline"`
|
||||
Name string `yaml:"name" json:"name" jsonschema:"title=package name"`
|
||||
Arch string `yaml:"arch" json:"arch" jsonschema:"title=target architecture,example=amd64"`
|
||||
Platform string `yaml:"platform,omitempty" json:"platform,omitempty" jsonschema:"title=target platform,example=linux,default=linux"`
|
||||
Epoch string `yaml:"epoch,omitempty" json:"epoch,omitempty" jsonschema:"title=version epoch,example=2,default=extracted from version"`
|
||||
Version string `yaml:"version" json:"version" jsonschema:"title=version,example=v1.0.2,example=2.0.1"`
|
||||
VersionSchema string `yaml:"version_schema,omitempty" json:"version_schema,omitempty" jsonschema:"title=version schema,enum=semver,enum=none,default=semver"`
|
||||
Release string `yaml:"release,omitempty" json:"release,omitempty" jsonschema:"title=version release,example=1"`
|
||||
Prerelease string `yaml:"prerelease,omitempty" json:"prerelease,omitempty" jsonschema:"title=version prerelease,default=extracted from version"`
|
||||
VersionMetadata string `yaml:"version_metadata,omitempty" json:"version_metadata,omitempty" jsonschema:"title=version metadata,example=git"`
|
||||
Section string `yaml:"section,omitempty" json:"section,omitempty" jsonschema:"title=package section,example=default"`
|
||||
Priority string `yaml:"priority,omitempty" json:"priority,omitempty" jsonschema:"title=package priority,example=extra"`
|
||||
Maintainer string `yaml:"maintainer,omitempty" json:"maintainer,omitempty" jsonschema:"title=package maintainer,example=me@example.com"`
|
||||
Description string `yaml:"description,omitempty" json:"description,omitempty" jsonschema:"title=package description"`
|
||||
Vendor string `yaml:"vendor,omitempty" json:"vendor,omitempty" jsonschema:"title=package vendor,example=MyCorp"`
|
||||
Homepage string `yaml:"homepage,omitempty" json:"homepage,omitempty" jsonschema:"title=package homepage,example=https://example.com"`
|
||||
License string `yaml:"license,omitempty" json:"license,omitempty" jsonschema:"title=package license,example=MIT"`
|
||||
Changelog string `yaml:"changelog,omitempty" json:"changelog,omitempty" jsonschema:"title=package changelog,example=changelog.yaml,description=see https://github.com/goreleaser/chglog for more details"`
|
||||
DisableGlobbing bool `yaml:"disable_globbing,omitempty" json:"disable_globbing,omitempty" jsonschema:"title=whether to disable file globbing,default=false"`
|
||||
Target string `yaml:"-" json:"-"`
|
||||
Name string `yaml:"name" json:"name" jsonschema:"title=package name"`
|
||||
Arch string `yaml:"arch" json:"arch" jsonschema:"title=target architecture,example=amd64"`
|
||||
Platform string `yaml:"platform,omitempty" json:"platform,omitempty" jsonschema:"title=target platform,example=linux,default=linux"`
|
||||
Epoch string `yaml:"epoch,omitempty" json:"epoch,omitempty" jsonschema:"title=version epoch,example=2,default=extracted from version"`
|
||||
Version string `yaml:"version" json:"version" jsonschema:"title=version,example=v1.0.2,example=2.0.1"`
|
||||
VersionSchema string `yaml:"version_schema,omitempty" json:"version_schema,omitempty" jsonschema:"title=version schema,enum=semver,enum=none,default=semver"`
|
||||
Release string `yaml:"release,omitempty" json:"release,omitempty" jsonschema:"title=version release,example=1"`
|
||||
Prerelease string `yaml:"prerelease,omitempty" json:"prerelease,omitempty" jsonschema:"title=version prerelease,default=extracted from version"`
|
||||
VersionMetadata string `yaml:"version_metadata,omitempty" json:"version_metadata,omitempty" jsonschema:"title=version metadata,example=git"`
|
||||
Section string `yaml:"section,omitempty" json:"section,omitempty" jsonschema:"title=package section,example=default"`
|
||||
Priority string `yaml:"priority,omitempty" json:"priority,omitempty" jsonschema:"title=package priority,example=extra"`
|
||||
Maintainer string `yaml:"maintainer,omitempty" json:"maintainer,omitempty" jsonschema:"title=package maintainer,example=me@example.com"`
|
||||
Description string `yaml:"description,omitempty" json:"description,omitempty" jsonschema:"title=package description"`
|
||||
Vendor string `yaml:"vendor,omitempty" json:"vendor,omitempty" jsonschema:"title=package vendor,example=MyCorp"`
|
||||
Homepage string `yaml:"homepage,omitempty" json:"homepage,omitempty" jsonschema:"title=package homepage,example=https://example.com"`
|
||||
License string `yaml:"license,omitempty" json:"license,omitempty" jsonschema:"title=package license,example=MIT"`
|
||||
Changelog string `yaml:"changelog,omitempty" json:"changelog,omitempty" jsonschema:"title=package changelog,example=changelog.yaml,description=see https://github.com/goreleaser/chglog for more details"`
|
||||
DisableGlobbing bool `yaml:"disable_globbing,omitempty" json:"disable_globbing,omitempty" jsonschema:"title=whether to disable file globbing,default=false"`
|
||||
MTime time.Time `yaml:"mtime,omitempty" json:"mtime,omitempty" jsonschema:"title=time to set into the files generated by nFPM"`
|
||||
Target string `yaml:"-" json:"-"`
|
||||
}
|
||||
|
||||
func (i *Info) Validate() error {
|
||||
|
@ -470,7 +473,13 @@ func PrepareForPackager(info *Info, packager string) (err error) {
|
|||
return ErrFieldEmpty{"version"}
|
||||
}
|
||||
|
||||
info.Contents, err = files.PrepareForPackager(info.Contents, info.Umask, packager, info.DisableGlobbing)
|
||||
info.Contents, err = files.PrepareForPackager(
|
||||
info.Contents,
|
||||
info.Umask,
|
||||
packager,
|
||||
info.DisableGlobbing,
|
||||
info.MTime,
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
@ -489,7 +498,13 @@ func Validate(info *Info) (err error) {
|
|||
}
|
||||
|
||||
for packager := range packagers {
|
||||
_, err := files.PrepareForPackager(info.Contents, info.Umask, packager, info.DisableGlobbing)
|
||||
_, err := files.PrepareForPackager(
|
||||
info.Contents,
|
||||
info.Umask,
|
||||
packager,
|
||||
info.DisableGlobbing,
|
||||
info.MTime,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -521,7 +536,9 @@ func WithDefaults(info *Info) *Info {
|
|||
if info.Umask == 0 {
|
||||
info.Umask = 0o02
|
||||
}
|
||||
|
||||
if info.MTime.IsZero() {
|
||||
info.MTime = getSourceDateEpoch()
|
||||
}
|
||||
switch info.VersionSchema {
|
||||
case "none":
|
||||
// No change to the version or prerelease info set in the YAML file
|
||||
|
@ -535,6 +552,19 @@ func WithDefaults(info *Info) *Info {
|
|||
return info
|
||||
}
|
||||
|
||||
func getSourceDateEpoch() time.Time {
|
||||
now := time.Now().UTC()
|
||||
epoch := os.Getenv("SOURCE_DATE_EPOCH")
|
||||
if epoch == "" {
|
||||
return now
|
||||
}
|
||||
sde, err := strconv.ParseInt(epoch, 10, 64)
|
||||
if err != nil {
|
||||
return now
|
||||
}
|
||||
return time.Unix(sde, 0).UTC()
|
||||
}
|
||||
|
||||
// ErrSigningFailure is returned whenever something went wrong during
|
||||
// the package signing process. The underlying error can be unwrapped
|
||||
// and could be crypto-related or something that occurred while adding
|
||||
|
|
12
nfpm_test.go
12
nfpm_test.go
|
@ -5,15 +5,18 @@ import (
|
|||
"io"
|
||||
"net/mail"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/goreleaser/nfpm/v2"
|
||||
"github.com/goreleaser/nfpm/v2/files"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var mtime = time.Date(2023, 11, 5, 23, 15, 17, 0, time.UTC)
|
||||
|
||||
func TestRegister(t *testing.T) {
|
||||
format := "TestRegister"
|
||||
pkgr := &fakePackager{}
|
||||
|
@ -97,6 +100,7 @@ func TestDefaults(t *testing.T) {
|
|||
Version: "2.4.1",
|
||||
Description: "no description given",
|
||||
Arch: "arm64",
|
||||
MTime: mtime,
|
||||
Overridables: nfpm.Overridables{
|
||||
Umask: 0o112,
|
||||
},
|
||||
|
@ -107,6 +111,7 @@ func TestDefaults(t *testing.T) {
|
|||
require.Equal(t, makeinfo(), info)
|
||||
})
|
||||
t.Run("none given", func(t *testing.T) {
|
||||
t.Setenv("SOURCE_DATE_EPOCH", strconv.FormatInt(mtime.Unix(), 10))
|
||||
got := nfpm.WithDefaults(&nfpm.Info{})
|
||||
require.Equal(t, nfpm.Info{
|
||||
Platform: "linux",
|
||||
|
@ -114,6 +119,7 @@ func TestDefaults(t *testing.T) {
|
|||
Version: "0.0.0",
|
||||
Prerelease: "rc0",
|
||||
Description: "no description given",
|
||||
MTime: mtime,
|
||||
Overridables: nfpm.Overridables{
|
||||
Umask: 0o002,
|
||||
},
|
||||
|
@ -573,9 +579,9 @@ func TestOverrides(t *testing.T) {
|
|||
}
|
||||
|
||||
t.Run("no_overrides", func(t *testing.T) {
|
||||
info, err := config.Get("doesnotexist")
|
||||
pkg, err := config.Get("doesnotexist")
|
||||
require.NoError(t, err)
|
||||
require.True(t, reflect.DeepEqual(&config.Info, info))
|
||||
require.Empty(t, pkg.Depends)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -256,7 +256,7 @@ func buildRPMMeta(info *nfpm.Info) (*rpmpack.RPMMetaData, error) {
|
|||
Suggests: suggests,
|
||||
Conflicts: conflicts,
|
||||
Compressor: info.RPM.Compression,
|
||||
BuildTime: time.Now(),
|
||||
BuildTime: info.MTime,
|
||||
BuildHost: hostname,
|
||||
}, nil
|
||||
}
|
||||
|
@ -372,7 +372,7 @@ func createFilesInsideRPM(info *nfpm.Info, rpm *rpmpack.RPM) (err error) {
|
|||
case files.TypeSymlink:
|
||||
file = asRPMSymlink(content)
|
||||
case files.TypeDir:
|
||||
file = asRPMDirectory(content)
|
||||
file = asRPMDirectory(content, info.MTime)
|
||||
case files.TypeImplicitDir:
|
||||
// we don't need to add imlicit directories to RPMs
|
||||
continue
|
||||
|
@ -393,11 +393,11 @@ func createFilesInsideRPM(info *nfpm.Info, rpm *rpmpack.RPM) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func asRPMDirectory(content *files.Content) *rpmpack.RPMFile {
|
||||
func asRPMDirectory(content *files.Content, mtime time.Time) *rpmpack.RPMFile {
|
||||
return &rpmpack.RPMFile{
|
||||
Name: content.Destination,
|
||||
Mode: uint(content.Mode()) | tagDirectory,
|
||||
MTime: uint32(time.Now().Unix()),
|
||||
MTime: uint32(mtime.Unix()),
|
||||
Owner: content.FileInfo.Owner,
|
||||
Group: content.FileInfo.Group,
|
||||
}
|
||||
|
|
|
@ -100,6 +100,13 @@ homepage: https://nfpm.goreleaser.com
|
|||
# License.
|
||||
license: MIT
|
||||
|
||||
# Date to be used as mtime on internal files.
|
||||
#
|
||||
# Default is the value of $SOURCE_DATE_EPOCH (which should be an Unix time),
|
||||
# or the current time.
|
||||
# Read more about SOURCE_DATE_EPOCH at https://reproducible-builds.org/docs/source-date-epoch/
|
||||
mtime: "2009-11-10T23:00:00Z"
|
||||
|
||||
# Changelog YAML file, see: https://github.com/goreleaser/chglog
|
||||
changelog: "changelog.yaml"
|
||||
|
||||
|
|
Loading…
Reference in New Issue