Skip to content

Commit

Permalink
feat: reproducible packages (#748)
Browse files Browse the repository at this point in the history
* feat: allow to set a build date

defaults to $SOURCE_DATE_EPOCH

closes #744
closes #734

Signed-off-by: Carlos Alexandro Becker <[email protected]>

* fix: rename to mtime

* docs: fix systemd note

closes #739

* fix: improve arch packager

* fix: arch test

Signed-off-by: Carlos Alexandro Becker <[email protected]>

* fix: improve apk packager

* fix: improve deb special files

* fix: reuse keys func

* fix: deps

Signed-off-by: Carlos Alexandro Becker <[email protected]>

---------

Signed-off-by: Carlos Alexandro Becker <[email protected]>
  • Loading branch information
caarlos0 authored Dec 7, 2023
1 parent c314251 commit 9c4fc0e
Show file tree
Hide file tree
Showing 14 changed files with 410 additions and 207 deletions.
28 changes: 16 additions & 12 deletions apk/apk.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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
}
}

Expand Down
42 changes: 19 additions & 23 deletions arch/arch.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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
}
}
Expand All @@ -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
Expand All @@ -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,
Expand All @@ -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
}
}
Expand Down Expand Up @@ -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()
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
}
Expand Down
32 changes: 18 additions & 14 deletions arch/arch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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",
Expand Down Expand Up @@ -297,7 +300,7 @@ func TestArchMtree(t *testing.T) {
Mode: 0o777,
Type: files.TypeSymlink,
},
})
}, mtime)
require.NoError(t, err)

tw.Close()
Expand All @@ -322,14 +325,16 @@ func TestGlob(t *testing.T) {
Name: "nfpm-repro",
Version: "1.0.0",
Maintainer: "asdfasdf",
MTime: mtime,

Overridables: nfpm.Overridables{
Contents: files.Contents{
{
Destination: "/usr/share/nfpm-repro",
Source: "../files/testdata/globtest/different-sizes/*/*.txt",
FileInfo: &files.ContentFileInfo{
Mode: 0o644,
Mode: 0o644,
MTime: mtime,
},
},
},
Expand Down Expand Up @@ -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") {
Expand All @@ -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)
}
}
Loading

0 comments on commit 9c4fc0e

Please sign in to comment.