From 9621f48d429009510109b82b987236af34eeaa74 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sun, 11 Sep 2022 14:32:53 -0700 Subject: [PATCH 01/30] feat: add support for Arch Linux packages --- arch/arch.go | 636 +++++++++++++++++++++++++++++++++++++++++++ internal/cmd/root.go | 1 + nfpm.go | 12 + 3 files changed, 649 insertions(+) create mode 100644 arch/arch.go diff --git a/arch/arch.go b/arch/arch.go new file mode 100644 index 00000000..69a67cee --- /dev/null +++ b/arch/arch.go @@ -0,0 +1,636 @@ +package arch + +import ( + "archive/tar" + "bytes" + "crypto/md5" + "crypto/sha256" + "fmt" + "io" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/goreleaser/nfpm/v2" + "github.com/goreleaser/nfpm/v2/files" + "github.com/klauspost/compress/zstd" + "github.com/klauspost/pgzip" +) + +const packagerName = "archlinux" + +// nolint: gochecknoinits +func init() { + nfpm.RegisterPackager(packagerName, Default) +} + +// Default ArchLinux packager. +// nolint: gochecknoglobals +var Default = ArchLinux{} + +type ArchLinux struct{} + +// nolint: gochecknoglobals +var archToArchLinux = map[string]string{ + "all": "any", + "amd64": "x86_64", + "386": "i686", + "arm64": "aarch64", + "arm7": "armv7hl", +} + +func ensureValidArch(info *nfpm.Info) *nfpm.Info { + if info.ArchLinux.Arch != "" { + info.Arch = info.ArchLinux.Arch + } else if arch, ok := archToArchLinux[info.Arch]; ok { + info.Arch = arch + } + + return info +} + +// ConventionalFileName returns a file name for a package conforming +// to Arch Linux package naming guidelines. See: +// https://wiki.archlinux.org/title/Arch_package_guidelines#Package_naming +func (ArchLinux) ConventionalFileName(info *nfpm.Info) string { + info = ensureValidArch(info) + + pkgrel, err := strconv.Atoi(info.Release) + if err != nil { + pkgrel = 1 + } + + name := fmt.Sprintf( + "%s-%s-%d-%s.pkg.tar.zst", + info.Name, + info.Version, + pkgrel, + info.Arch, + ) + + return validPkgName(name) +} + +func validPkgName(s string) string { + s = strings.Map(mapValidChar, s) + s = strings.TrimLeft(s, "-.") + return s +} + +func mapValidChar(r rune) rune { + if r >= 'a' && r <= 'z' || + r >= 'A' && r <= 'Z' || + r >= '0' && r <= '9' || + isOneOf(r, '.', '_', '+', '-') { + return r + } + return -1 +} + +func isOneOf(r rune, rr ...rune) bool { + for _, char := range rr { + if r == char { + return true + } + } + return false +} + +func (ArchLinux) Package(info *nfpm.Info, w io.Writer) error { + zw, err := zstd.NewWriter(w) + if err != nil { + return err + } + defer zw.Close() + + tw := tar.NewWriter(zw) + defer tw.Close() + + entries, totalSize, err := createFilesInTar(info, tw) + if err != nil { + return err + } + + pkginfoEntry, err := createPkginfo(info, tw, totalSize) + if err != nil { + return err + } + + // .PKGINFO must be the first entry in .MTREE + entries = append([]MtreeEntry{*pkginfoEntry}, entries...) + + err = createMtree(info, tw, entries) + if err != nil { + return err + } + + err = createScripts(info, tw) + if err != nil { + return err + } + + return nil +} + +func createFilesInTar(info *nfpm.Info, tw *tar.Writer) ([]MtreeEntry, int64, error) { + created := map[string]struct{}{} + var entries []MtreeEntry + var totalSize int64 + + for _, content := range info.Contents { + path := normalizePath(content.Destination) + + switch content.Type { + case "ghost": + // Ignore ghost files + case "dir": + err := createDirs(content.Destination, tw, created) + if err != nil { + return nil, 0, err + } + + srcFi, err := os.Stat(content.Destination) + if err != nil { + return nil, 0, err + } + + entries = append(entries, MtreeEntry{ + Destination: path, + Time: srcFi.ModTime().Unix(), + Type: content.Type, + }) + case "symlink": + dir := filepath.Dir(path) + err := createDirs(dir, tw, created) + if err != nil { + return nil, 0, err + } + + modtime := time.Now() + if content.FileInfo != nil && !content.ModTime().IsZero() { + modtime = content.ModTime() + } + + err = tw.WriteHeader(&tar.Header{ + Name: normalizePath(content.Destination), + Linkname: content.Source, + ModTime: modtime, + Typeflag: tar.TypeSymlink, + }) + if err != nil { + return nil, 0, err + } + + entries = append(entries, MtreeEntry{ + LinkSource: content.Source, + Destination: path, + Time: modtime.Unix(), + Mode: 0777, + Type: content.Type, + }) + default: + dir := filepath.Dir(path) + err := createDirs(dir, tw, created) + if err != nil { + return nil, 0, err + } + + src, err := os.Open(content.Source) + if err != nil { + return nil, 0, err + } + + srcFi, err := src.Stat() + if err != nil { + return nil, 0, err + } + + header := &tar.Header{ + Name: path, + Mode: int64(srcFi.Mode()), + Typeflag: tar.TypeReg, + Size: srcFi.Size(), + ModTime: srcFi.ModTime(), + } + + err = tw.WriteHeader(header) + if err != nil { + return nil, 0, err + } + + sha256Hash := sha256.New() + md5Hash := md5.New() + + w := io.MultiWriter(tw, sha256Hash, md5Hash) + + _, err = io.Copy(w, src) + if err != nil { + return nil, 0, err + } + + entries = append(entries, MtreeEntry{ + Destination: path, + Time: srcFi.ModTime().Unix(), + Mode: int64(srcFi.Mode()), + Size: srcFi.Size(), + Type: content.Type, + MD5: md5Hash.Sum(nil), + SHA256: sha256Hash.Sum(nil), + }) + + totalSize += srcFi.Size() + } + } + + return entries, totalSize, nil +} + +func createDirs(dst string, tw *tar.Writer, created map[string]struct{}) error { + for _, path := range neededPaths(dst) { + path = normalizePath(path) + "/" + + if _, ok := created[path]; ok { + continue + } + + err := tw.WriteHeader(&tar.Header{ + Name: path, + Mode: 0755, + Typeflag: tar.TypeDir, + ModTime: time.Now(), + Uname: "root", + Gname: "root", + }) + if err != nil { + return fmt.Errorf("failed to create folder: %w", err) + } + + created[path] = struct{}{} + } + + return nil +} + +func defaultStr(s, def string) string { + if s == "" { + return def + } + return s +} + +func neededPaths(dst string) []string { + dst = files.ToNixPath(dst) + split := strings.Split(strings.Trim(dst, "/."), "/") + + var sb strings.Builder + var paths []string + for index, elem := range split { + if index != 0 { + sb.WriteRune('/') + } + sb.WriteString(elem) + paths = append(paths, sb.String()) + } + + return paths +} + +func normalizePath(src string) string { + return files.ToNixPath(strings.TrimPrefix(src, "/")) +} + +func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntry, error) { + buf := &bytes.Buffer{} + + info = ensureValidArch(info) + + pkgrel, err := strconv.Atoi(info.Release) + if err != nil { + pkgrel = 1 + } + + pkgver := fmt.Sprintf("%s-%d", info.Version, pkgrel) + pkgname := validPkgName(info.Name) + + // Description cannot containe newlines + pkgdesc := strings.ReplaceAll(info.Description, "\n", " ") + + io.WriteString(buf, "# Generated by nfpm\n") + + builddate := strconv.FormatInt(time.Now().Unix(), 10) + totalSizeStr := strconv.FormatInt(totalSize, 10) + + err = writeKVPairs(buf, map[string]string{ + "size": totalSizeStr, + "pkgname": pkgname, + "pkgbase": pkgname, + "pkgver": pkgver, + "pkgdesc": pkgdesc, + "url": info.Homepage, + "builddate": builddate, + "packager": defaultStr(info.ArchLinux.Packager, "Unknown Packager"), + "arch": info.Arch, + "license": info.License, + }) + if err != nil { + return nil, err + } + + for _, conflict := range info.Conflicts { + err = writeKVPair(buf, "conflict", conflict) + if err != nil { + return nil, err + } + } + + for _, provides := range info.Provides { + err = writeKVPair(buf, "provides", provides) + if err != nil { + return nil, err + } + } + + for _, depend := range info.Depends { + err = writeKVPair(buf, "depend", depend) + if err != nil { + return nil, err + } + } + + for _, content := range info.Contents { + if content.Type == "config" || content.Type == "config|noreplace" { + path := normalizePath(content.Destination) + path = strings.TrimPrefix(path, "./") + + err = writeKVPair(buf, "backup", path) + if err != nil { + return nil, err + } + } + } + + size := buf.Len() + + err = tw.WriteHeader(&tar.Header{ + Typeflag: tar.TypeReg, + Mode: 0644, + Name: ".PKGINFO", + Size: int64(size), + ModTime: time.Now(), + }) + if err != nil { + return nil, err + } + + md5Hash := md5.New() + sha256Hash := sha256.New() + + r := io.TeeReader(buf, md5Hash) + r = io.TeeReader(r, sha256Hash) + + _, err = io.Copy(tw, r) + if err != nil { + return nil, err + } + + return &MtreeEntry{ + Destination: ".PKGINFO", + Time: time.Now().Unix(), + Mode: 0644, + Size: int64(size), + Type: "file", + MD5: md5Hash.Sum(nil), + SHA256: sha256Hash.Sum(nil), + }, nil +} + +func writeKVPairs(w io.Writer, s map[string]string) error { + for key, val := range s { + err := writeKVPair(w, key, val) + if err != nil { + return err + } + } + return nil +} + +func writeKVPair(w io.Writer, key, value string) error { + if value == "" { + return nil + } + + _, err := io.WriteString(w, key) + if err != nil { + return err + } + + _, err = io.WriteString(w, " = ") + if err != nil { + return err + } + + _, err = io.WriteString(w, value) + if err != nil { + return err + } + + _, err = io.WriteString(w, "\n") + if err != nil { + return err + } + + return nil +} + +type MtreeEntry struct { + LinkSource string + Destination string + Time int64 + Mode int64 + Size int64 + Type string + MD5 []byte + SHA256 []byte +} + +func (me *MtreeEntry) WriteTo(w io.Writer) (int64, error) { + switch me.Type { + case "dir": + n, err := fmt.Fprintf( + w, + "./%s time=%d.0 type=dir\n", + normalizePath(me.Destination), + me.Time, + ) + return int64(n), err + case "symlink": + n, err := fmt.Fprintf( + w, + "./%s time=%d.0 mode=%o type=link link=%s\n", + normalizePath(me.Destination), + me.Time, + me.Mode, + me.LinkSource, + ) + return int64(n), err + default: + n, err := fmt.Fprintf( + w, + "./%s time=%d.0 mode=%o size=%d type=file md5digest=%x sha256digest=%x\n", + normalizePath(me.Destination), + me.Time, + me.Mode, + me.Size, + me.MD5, + me.SHA256, + ) + return int64(n), err + } +} + +func createMtree(info *nfpm.Info, tw *tar.Writer, entries []MtreeEntry) error { + buf := &bytes.Buffer{} + gw := pgzip.NewWriter(buf) + defer gw.Close() + + created := map[string]struct{}{} + + _, err := io.WriteString(gw, "#mtree\n") + if err != nil { + return err + } + + for _, entry := range entries { + destDir := filepath.Dir(entry.Destination) + + dirs := createDirsMtree(destDir, created) + for _, dir := range dirs { + _, err = dir.WriteTo(gw) + if err != nil { + return err + } + } + + _, err = entry.WriteTo(gw) + if err != nil { + return err + } + } + + gw.Close() + + err = tw.WriteHeader(&tar.Header{ + Typeflag: tar.TypeReg, + Mode: 0644, + Name: ".MTREE", + Size: int64(buf.Len()), + ModTime: time.Now(), + }) + if err != nil { + return err + } + + _, err = io.Copy(tw, buf) + if err != nil { + return err + } + + return nil +} + +func createDirsMtree(dst string, created map[string]struct{}) []MtreeEntry { + var out []MtreeEntry + for _, path := range neededPaths(dst) { + path = normalizePath(path) + "/" + + if path == "./" { + continue + } + + if _, ok := created[path]; ok { + continue + } + + out = append(out, MtreeEntry{ + Destination: path, + Time: time.Now().Unix(), + Mode: 0755, + Type: "dir", + }) + + created[path] = struct{}{} + } + return out +} + +func createScripts(info *nfpm.Info, tw *tar.Writer) error { + scripts := map[string]string{} + + if info.Scripts.PreInstall != "" { + scripts["pre_install"] = info.Scripts.PreInstall + } + + if info.Scripts.PostInstall != "" { + scripts["post_install"] = info.Scripts.PostInstall + } + + if info.Scripts.PreRemove != "" { + scripts["pre_remove"] = info.Scripts.PreRemove + } + + if info.Scripts.PostInstall != "" { + scripts["post_remove"] = info.Scripts.PostRemove + } + + if info.ArchLinux.Scripts.PreUpgrade != "" { + scripts["pre_upgrade"] = info.ArchLinux.Scripts.PreUpgrade + } + + if info.ArchLinux.Scripts.PostUpgrade != "" { + scripts["post_upgrade"] = info.ArchLinux.Scripts.PostUpgrade + } + + if len(scripts) == 0 { + return nil + } + + buf := &bytes.Buffer{} + + err := writeScripts(buf, scripts) + if err != nil { + return nil + } + + err = tw.WriteHeader(&tar.Header{ + Typeflag: tar.TypeReg, + Mode: 0644, + Name: ".INSTALL", + Size: int64(buf.Len()), + ModTime: time.Now(), + }) + if err != nil { + return err + } + + _, err = io.Copy(tw, buf) + return err +} + +func writeScripts(w io.Writer, scripts map[string]string) error { + for script, path := range scripts { + fmt.Fprintf(w, "function %s() {\n", script) + + fl, err := os.Open(path) + if err != nil { + return err + } + io.Copy(w, fl) + fl.Close() + + io.WriteString(w, "}\n\n") + } + + return nil +} diff --git a/internal/cmd/root.go b/internal/cmd/root.go index 92bb8fe7..2c384410 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -6,6 +6,7 @@ import ( _ "github.com/goreleaser/nfpm/v2/apk" _ "github.com/goreleaser/nfpm/v2/deb" _ "github.com/goreleaser/nfpm/v2/rpm" + _ "github.com/goreleaser/nfpm/v2/arch" "github.com/spf13/cobra" ) diff --git a/nfpm.go b/nfpm.go index 6fe89d36..4f872eaf 100644 --- a/nfpm.go +++ b/nfpm.go @@ -270,6 +270,18 @@ type Overridables struct { RPM RPM `yaml:"rpm,omitempty" json:"rpm,omitempty" jsonschema:"title=rpm-specific settings"` Deb Deb `yaml:"deb,omitempty" json:"deb,omitempty" jsonschema:"title=deb-specific settings"` APK APK `yaml:"apk,omitempty" json:"apk,omitempty" jsonschema:"title=apk-specific settings"` + ArchLinux ArchLinux `yaml:"archlinux,omitempty" json:"archlinux,omitempty" jsonschema:"title=archlinux-specific settings"` +} + +type ArchLinux struct { + Arch string `yaml:"arch,omitempty" json:"arch,omitempty" jsonschema:"title=architecture in archlinux nomenclature"` + Packager string `yaml:"packager,omitempty" json:"packager,omitempty" jsonschema:"title=organization that packaged the software"` + Scripts ArchLinuxScripts `yaml:"scripts,omitempty" json:"scripts,omitempty" jsonschema:"title=archlinux-specific scripts"` +} + +type ArchLinuxScripts struct { + PreUpgrade string `yaml:"preupgrade,omitempty" json:"preupgrade,omitempty" jsonschema:"title=preupgrade script"` + PostUpgrade string `yaml:"postupgrade,omitempty" json:"postupgrade,omitempty" jsonschema:"title=postupgrade script"` } // RPM is custom configs that are only available on RPM packages. From 800db2205a0e9519570d644fe1f8df786ddb4072 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sun, 11 Sep 2022 18:19:23 -0700 Subject: [PATCH 02/30] test: Add initial tests --- arch/arch.go | 7 +-- arch/arch_test.go | 136 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 arch/arch_test.go diff --git a/arch/arch.go b/arch/arch.go index 69a67cee..0b90a690 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -151,14 +151,9 @@ func createFilesInTar(info *nfpm.Info, tw *tar.Writer) ([]MtreeEntry, int64, err return nil, 0, err } - srcFi, err := os.Stat(content.Destination) - if err != nil { - return nil, 0, err - } - entries = append(entries, MtreeEntry{ Destination: path, - Time: srcFi.ModTime().Unix(), + Time: time.Now().Unix(), Type: content.Type, }) case "symlink": diff --git a/arch/arch_test.go b/arch/arch_test.go new file mode 100644 index 00000000..45b578a6 --- /dev/null +++ b/arch/arch_test.go @@ -0,0 +1,136 @@ +package arch + +import ( + "archive/tar" + "bytes" + "fmt" + "io" + "strings" + "testing" + + "github.com/goreleaser/nfpm/v2" + "github.com/goreleaser/nfpm/v2/files" + "github.com/stretchr/testify/require" +) + +func exampleInfo() *nfpm.Info { + return nfpm.WithDefaults(&nfpm.Info{ + Name: "foo", + Arch: "amd64", + Description: "Foo does things", + Priority: "extra", + Maintainer: "Carlos A Becker ", + Version: "1.0.0", + Section: "default", + Homepage: "http://carlosbecker.com", + Vendor: "nope", + License: "MIT", + Overridables: nfpm.Overridables{ + Depends: []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: "/etc/fake/fake.conf", + Type: "config", + }, + { + Destination: "/var/log/whatever", + Type: "dir", + }, + { + Destination: "/usr/share/whatever", + Type: "dir", + }, + }, + Scripts: nfpm.Scripts{ + PreInstall: "../testdata/scripts/preinstall.sh", + PostInstall: "../testdata/scripts/postinstall.sh", + PreRemove: "../testdata/scripts/preremove.sh", + PostRemove: "../testdata/scripts/postremove.sh", + }, + ArchLinux: nfpm.ArchLinux{ + Scripts: nfpm.ArchLinuxScripts{ + PreUpgrade: "../testdata/scripts/preupgrade.sh", + PostUpgrade: "../testdata/scripts/postupgrade.sh", + }, + }, + }, + }) +} + +func TestArch(t *testing.T) { + for _, arch := range []string{"386", "amd64", "arm64"} { + arch := arch + t.Run(arch, func(t *testing.T) { + info := exampleInfo() + info.Arch = arch + err := Default.Package(info, io.Discard) + require.NoError(t, err) + }) + } +} + +func TestArchPkginfo(t *testing.T) { + buf := &bytes.Buffer{} + tw := tar.NewWriter(buf) + + entry, err := createPkginfo(exampleInfo(), tw, 1234) + require.NoError(t, err) + + tw.Close() + + tr := tar.NewReader(buf) + tr.Next() + + pkginfoData := make([]byte, entry.Size) + _, err = io.ReadFull(tr, pkginfoData) + require.NoError(t, err) + + fields := extractPkginfoFields(pkginfoData) + require.Equal(t, "foo", fields["pkgname"]) + require.Equal(t, "foo", fields["pkgbase"]) + require.Equal(t, "1.0.0-1", fields["pkgver"]) + require.Equal(t, "Foo does things", fields["pkgdesc"]) + require.Equal(t, "http://carlosbecker.com", fields["url"]) + require.Equal(t, "Unknown Packager", fields["packager"]) + require.Equal(t, "x86_64", fields["arch"]) + require.Equal(t, "MIT", fields["license"]) + require.Equal(t, "1234", fields["size"]) + require.Equal(t, "zsh", fields["conflict"]) + require.Equal(t, "bzr", fields["provides"]) + require.Equal(t, "bash", fields["depend"]) + require.Equal(t, "etc/fake/fake.conf", fields["backup"]) +} + +func extractPkginfoFields(data []byte) map[string]string { + strData := string(data) + strData = strings.TrimPrefix(strData, "# Generated by nfpm\n") + strData = strings.TrimSpace(strData) + + splitData := strings.Split(strData, "\n") + out := map[string]string{} + + for _, kvPair := range splitData { + fmt.Println(kvPair) + splitPair := strings.Split(kvPair, " = ") + fmt.Println(splitPair, len(splitPair), splitPair[0], splitPair[1]) + out[splitPair[0]] = splitPair[1] + } + + return out +} From e76830c4118104eebd03c97afe183bd9287d5ac3 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sun, 11 Sep 2022 18:28:20 -0700 Subject: [PATCH 03/30] test: Increase coverage by modifying example info --- arch/arch_test.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/arch_test.go b/arch/arch_test.go index 45b578a6..1dc4a006 100644 --- a/arch/arch_test.go +++ b/arch/arch_test.go @@ -15,7 +15,7 @@ import ( func exampleInfo() *nfpm.Info { return nfpm.WithDefaults(&nfpm.Info{ - Name: "foo", + Name: "foo-test", Arch: "amd64", Description: "Foo does things", Priority: "extra", @@ -56,6 +56,11 @@ func exampleInfo() *nfpm.Info { Destination: "/usr/share/whatever", Type: "dir", }, + { + Source: "/etc/fake/fake.conf", + Destination: "/etc/fake/fake-link.conf", + Type: "symlink", + }, }, Scripts: nfpm.Scripts{ PreInstall: "../testdata/scripts/preinstall.sh", @@ -102,8 +107,8 @@ func TestArchPkginfo(t *testing.T) { require.NoError(t, err) fields := extractPkginfoFields(pkginfoData) - require.Equal(t, "foo", fields["pkgname"]) - require.Equal(t, "foo", fields["pkgbase"]) + require.Equal(t, "foo-test", fields["pkgname"]) + require.Equal(t, "foo-test", fields["pkgbase"]) require.Equal(t, "1.0.0-1", fields["pkgver"]) require.Equal(t, "Foo does things", fields["pkgdesc"]) require.Equal(t, "http://carlosbecker.com", fields["url"]) From b33d6420b205dea911f30a0d92e21e8c4dc49f92 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sun, 11 Sep 2022 18:33:20 -0700 Subject: [PATCH 04/30] test: Add test for ArchLinux.ConventionalFileName() --- arch/arch_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/arch_test.go b/arch/arch_test.go index 1dc4a006..a38ca11b 100644 --- a/arch/arch_test.go +++ b/arch/arch_test.go @@ -90,6 +90,21 @@ func TestArch(t *testing.T) { } } +func TestArchConventionalFileName(t *testing.T) { + for _, arch := range []string{"386", "amd64", "arm64"} { + arch := arch + t.Run(arch, func(t *testing.T) { + info := exampleInfo() + info.Arch = arch + name := Default.ConventionalFileName(info) + require.Equal(t, + "foo-test-1.0.0-1-"+ archToArchLinux[arch] + ".pkg.tar.zst", + name, + ) + }) + } +} + func TestArchPkginfo(t *testing.T) { buf := &bytes.Buffer{} tw := tar.NewWriter(buf) From ff3851c3827cb11b7e160a960b8c09c0d9fdfa06 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sun, 11 Sep 2022 18:44:43 -0700 Subject: [PATCH 05/30] docs: Return error if package name is invalid --- arch/arch.go | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/arch/arch.go b/arch/arch.go index 0b90a690..7ef06bda 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -5,6 +5,7 @@ import ( "bytes" "crypto/md5" "crypto/sha256" + "errors" "fmt" "io" "os" @@ -19,6 +20,10 @@ import ( "github.com/klauspost/pgzip" ) +var ( + ErrInvalidPkgName = errors.New("archlinux: package names may only contain alphanumeric characters or one of ., _, +, or -, and may not start with hyphen or dot") +) + const packagerName = "archlinux" // nolint: gochecknoinits @@ -79,6 +84,10 @@ func validPkgName(s string) string { return s } +func nameIsValid(s string) bool { + return s == validPkgName(s) +} + func mapValidChar(r rune) rune { if r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || @@ -99,6 +108,10 @@ func isOneOf(r rune, rr ...rune) bool { } func (ArchLinux) Package(info *nfpm.Info, w io.Writer) error { + if !nameIsValid(info.Name) { + return ErrInvalidPkgName + } + zw, err := zstd.NewWriter(w) if err != nil { return err @@ -297,6 +310,10 @@ func normalizePath(src string) string { } func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntry, error) { + if !nameIsValid(info.Name) { + return nil, ErrInvalidPkgName + } + buf := &bytes.Buffer{} info = ensureValidArch(info) @@ -307,7 +324,6 @@ func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntr } pkgver := fmt.Sprintf("%s-%d", info.Version, pkgrel) - pkgname := validPkgName(info.Name) // Description cannot containe newlines pkgdesc := strings.ReplaceAll(info.Description, "\n", " ") @@ -319,8 +335,8 @@ func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntr err = writeKVPairs(buf, map[string]string{ "size": totalSizeStr, - "pkgname": pkgname, - "pkgbase": pkgname, + "pkgname": info.Name, + "pkgbase": info.Name, "pkgver": pkgver, "pkgdesc": pkgdesc, "url": info.Homepage, From 2068f22f667ed1da9114b42abe1810c212bacb45 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sun, 11 Sep 2022 18:53:35 -0700 Subject: [PATCH 06/30] fix: Make empty name invalid --- arch/arch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arch.go b/arch/arch.go index 7ef06bda..838b5be5 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -85,7 +85,7 @@ func validPkgName(s string) string { } func nameIsValid(s string) bool { - return s == validPkgName(s) + return s != "" && s == validPkgName(s) } func mapValidChar(r rune) rune { From 4219aadb2650361cb82c4ab7b476e840c6bfd338 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sun, 11 Sep 2022 19:21:47 -0700 Subject: [PATCH 07/30] fix: Add replaces field to .PKGINFO generator --- arch/arch.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arch.go b/arch/arch.go index 838b5be5..186540b8 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -349,6 +349,13 @@ func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntr return nil, err } + for _, replaces := range info.Replaces { + err = writeKVPair(buf, "replaces", replaces) + if err != nil { + return nil, err + } + } + for _, conflict := range info.Conflicts { err = writeKVPair(buf, "conflict", conflict) if err != nil { From af7817e0502a3e907cd04f4373bea6f5af77a204 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sun, 11 Sep 2022 19:22:18 -0700 Subject: [PATCH 08/30] test: Add additional tests --- arch/arch_test.go | 133 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 118 insertions(+), 15 deletions(-) diff --git a/arch/arch_test.go b/arch/arch_test.go index a38ca11b..4a1b8055 100644 --- a/arch/arch_test.go +++ b/arch/arch_test.go @@ -10,6 +10,7 @@ import ( "github.com/goreleaser/nfpm/v2" "github.com/goreleaser/nfpm/v2/files" + "github.com/klauspost/pgzip" "github.com/stretchr/testify/require" ) @@ -90,6 +91,20 @@ func TestArch(t *testing.T) { } } +func TestArchNoFiles(t *testing.T) { + info := exampleInfo() + info.Contents = nil + info.Scripts = nfpm.Scripts{} + info.ArchLinux = nfpm.ArchLinux{} + err := Default.Package(info, io.Discard) + require.NoError(t, err) +} + +func TestArchNoInfo(t *testing.T) { + err := Default.Package(nfpm.WithDefaults(&nfpm.Info{}), io.Discard) + require.Error(t, err) +} + func TestArchConventionalFileName(t *testing.T) { for _, arch := range []string{"386", "amd64", "arm64"} { arch := arch @@ -98,7 +113,7 @@ func TestArchConventionalFileName(t *testing.T) { info.Arch = arch name := Default.ConventionalFileName(info) require.Equal(t, - "foo-test-1.0.0-1-"+ archToArchLinux[arch] + ".pkg.tar.zst", + "foo-test-1.0.0-1-"+archToArchLinux[arch]+".pkg.tar.zst", name, ) }) @@ -106,21 +121,8 @@ func TestArchConventionalFileName(t *testing.T) { } func TestArchPkginfo(t *testing.T) { - buf := &bytes.Buffer{} - tw := tar.NewWriter(buf) - - entry, err := createPkginfo(exampleInfo(), tw, 1234) - require.NoError(t, err) - - tw.Close() - - tr := tar.NewReader(buf) - tr.Next() - - pkginfoData := make([]byte, entry.Size) - _, err = io.ReadFull(tr, pkginfoData) + pkginfoData, err := makeTestPkginfo(t, exampleInfo()) require.NoError(t, err) - fields := extractPkginfoFields(pkginfoData) require.Equal(t, "foo-test", fields["pkgname"]) require.Equal(t, "foo-test", fields["pkgbase"]) @@ -137,6 +139,55 @@ func TestArchPkginfo(t *testing.T) { require.Equal(t, "etc/fake/fake.conf", fields["backup"]) } +func TestArchInvalidName(t *testing.T) { + info := exampleInfo() + info.Name = "#" + _, err := makeTestPkginfo(t, info) + require.ErrorIs(t, err, ErrInvalidPkgName) +} + +func TestArchVersionWithRelease(t *testing.T) { + info := exampleInfo() + info.Version = "0.0.1" + info.Release = "4" + pkginfoData, err := makeTestPkginfo(t, info) + require.NoError(t, err) + fields := extractPkginfoFields(pkginfoData) + require.Equal(t, "0.0.1-4", fields["pkgver"]) +} + +func TestArchOverrideArchitecture(t *testing.T) { + info := exampleInfo() + info.ArchLinux.Arch = "randomarch" + pkginfoData, err := makeTestPkginfo(t, info) + require.NoError(t, err) + fields := extractPkginfoFields(pkginfoData) + require.Equal(t, "randomarch", fields["arch"]) +} + +func makeTestPkginfo(t *testing.T, info *nfpm.Info) ([]byte, error) { + buf := &bytes.Buffer{} + tw := tar.NewWriter(buf) + + entry, err := createPkginfo(info, tw, 1234) + if err != nil { + return nil, err + } + + tw.Close() + + tr := tar.NewReader(buf) + tr.Next() + + pkginfoData := make([]byte, entry.Size) + _, err = io.ReadFull(tr, pkginfoData) + if err != nil { + return nil, err + } + + return pkginfoData, nil +} + func extractPkginfoFields(data []byte) map[string]string { strData := string(data) strData = strings.TrimPrefix(strData, "# Generated by nfpm\n") @@ -154,3 +205,55 @@ func extractPkginfoFields(data []byte) map[string]string { return out } + +const correctMtree = `#mtree +./foo time=1234.0 type=dir +./3 time=12345.0 mode=644 size=100 type=file md5digest=abcd sha256digest=ef12 +./sh time=123456.0 mode=777 type=link link=/bin/bash +` + +func TestArchMtree(t *testing.T) { + info := exampleInfo() + + buf := &bytes.Buffer{} + tw := tar.NewWriter(buf) + + err := createMtree(info, tw, []MtreeEntry{ + { + Destination: "/foo", + Time: 1234, + Type: "dir", + }, + { + Destination: "/3", + Time: 12345, + Mode: 0644, + Size: 100, + Type: "file", + MD5: []byte{0xAB, 0xCD}, + SHA256: []byte{0xEF, 0x12}, + }, + { + LinkSource: "/bin/bash", + Destination: "/sh", + Time: 123456, + Mode: 0777, + Type: "symlink", + }, + }) + require.NoError(t, err) + + tw.Close() + + tr := tar.NewReader(buf) + tr.Next() + + gr, err := pgzip.NewReader(tr) + require.NoError(t, err) + defer gr.Close() + + mtree, err := io.ReadAll(gr) + require.NoError(t, err) + + require.InDeltaSlice(t, []byte(correctMtree), mtree, 0) +} From 1e1fe66f5988f5f0649fcb03599487a551ac9908 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sun, 11 Sep 2022 19:22:57 -0700 Subject: [PATCH 09/30] test: Test for added replaces field --- arch/arch_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arch_test.go b/arch/arch_test.go index 4a1b8055..bfb5ba00 100644 --- a/arch/arch_test.go +++ b/arch/arch_test.go @@ -133,6 +133,7 @@ func TestArchPkginfo(t *testing.T) { require.Equal(t, "x86_64", fields["arch"]) require.Equal(t, "MIT", fields["license"]) require.Equal(t, "1234", fields["size"]) + require.Equal(t, "svn", fields["replaces"]) require.Equal(t, "zsh", fields["conflict"]) require.Equal(t, "bzr", fields["provides"]) require.Equal(t, "bash", fields["depend"]) From 21701542c1aaae9abb9d1a17883066e02200da03 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sun, 11 Sep 2022 19:30:33 -0700 Subject: [PATCH 10/30] docs: Add more comments --- arch/arch.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/arch.go b/arch/arch.go index 186540b8..ae2872b1 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -1,3 +1,4 @@ +// Package arch implements nfpm.Packager providing bindings for Arch Linux packages. package arch import ( @@ -78,16 +79,19 @@ func (ArchLinux) ConventionalFileName(info *nfpm.Info) string { return validPkgName(name) } +// validPkgName remoces any invalid characters from a string func validPkgName(s string) string { s = strings.Map(mapValidChar, s) s = strings.TrimLeft(s, "-.") return s } +// nameIsValid checks whether a package name is valid func nameIsValid(s string) bool { return s != "" && s == validPkgName(s) } +// mapValidChar returns r if it is allowed, otherwise, returns -1 func mapValidChar(r rune) rune { if r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || @@ -98,6 +102,7 @@ func mapValidChar(r rune) rune { return -1 } +// isOneOf checks whether a rune is one of the runes on rr func isOneOf(r rune, rr ...rune) bool { for _, char := range rr { if r == char { @@ -107,6 +112,7 @@ func isOneOf(r rune, rr ...rune) bool { return false } +// Package writes a new archlinux package to the given writer using the given info. func (ArchLinux) Package(info *nfpm.Info, w io.Writer) error { if !nameIsValid(info.Name) { return ErrInvalidPkgName @@ -147,6 +153,7 @@ func (ArchLinux) Package(info *nfpm.Info, w io.Writer) error { return nil } +// createFilesInTar adds the files described in the given info to the given tar writer func createFilesInTar(info *nfpm.Info, tw *tar.Writer) ([]MtreeEntry, int64, error) { created := map[string]struct{}{} var entries []MtreeEntry @@ -177,6 +184,7 @@ func createFilesInTar(info *nfpm.Info, tw *tar.Writer) ([]MtreeEntry, int64, err } modtime := time.Now() + // If the time is given, use it if content.FileInfo != nil && !content.ModTime().IsZero() { modtime = content.ModTime() } @@ -313,7 +321,7 @@ func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntr if !nameIsValid(info.Name) { return nil, ErrInvalidPkgName } - + buf := &bytes.Buffer{} info = ensureValidArch(info) From 0d9cb169b533b6ebf1d704389c38733825dfe53b Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sun, 11 Sep 2022 19:50:24 -0700 Subject: [PATCH 11/30] style: Run gofumpt --- arch/arch.go | 18 ++++++++---------- arch/arch_test.go | 4 ++-- internal/cmd/root.go | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/arch/arch.go b/arch/arch.go index ae2872b1..0dacd1ac 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -21,9 +21,7 @@ import ( "github.com/klauspost/pgzip" ) -var ( - ErrInvalidPkgName = errors.New("archlinux: package names may only contain alphanumeric characters or one of ., _, +, or -, and may not start with hyphen or dot") -) +var ErrInvalidPkgName = errors.New("archlinux: package names may only contain alphanumeric characters or one of ., _, +, or -, and may not start with hyphen or dot") const packagerName = "archlinux" @@ -203,7 +201,7 @@ func createFilesInTar(info *nfpm.Info, tw *tar.Writer) ([]MtreeEntry, int64, err LinkSource: content.Source, Destination: path, Time: modtime.Unix(), - Mode: 0777, + Mode: 0o777, Type: content.Type, }) default: @@ -273,7 +271,7 @@ func createDirs(dst string, tw *tar.Writer, created map[string]struct{}) error { err := tw.WriteHeader(&tar.Header{ Name: path, - Mode: 0755, + Mode: 0o755, Typeflag: tar.TypeDir, ModTime: time.Now(), Uname: "root", @@ -401,7 +399,7 @@ func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntr err = tw.WriteHeader(&tar.Header{ Typeflag: tar.TypeReg, - Mode: 0644, + Mode: 0o644, Name: ".PKGINFO", Size: int64(size), ModTime: time.Now(), @@ -424,7 +422,7 @@ func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntr return &MtreeEntry{ Destination: ".PKGINFO", Time: time.Now().Unix(), - Mode: 0644, + Mode: 0o644, Size: int64(size), Type: "file", MD5: md5Hash.Sum(nil), @@ -549,7 +547,7 @@ func createMtree(info *nfpm.Info, tw *tar.Writer, entries []MtreeEntry) error { err = tw.WriteHeader(&tar.Header{ Typeflag: tar.TypeReg, - Mode: 0644, + Mode: 0o644, Name: ".MTREE", Size: int64(buf.Len()), ModTime: time.Now(), @@ -582,7 +580,7 @@ func createDirsMtree(dst string, created map[string]struct{}) []MtreeEntry { out = append(out, MtreeEntry{ Destination: path, Time: time.Now().Unix(), - Mode: 0755, + Mode: 0o755, Type: "dir", }) @@ -631,7 +629,7 @@ func createScripts(info *nfpm.Info, tw *tar.Writer) error { err = tw.WriteHeader(&tar.Header{ Typeflag: tar.TypeReg, - Mode: 0644, + Mode: 0o644, Name: ".INSTALL", Size: int64(buf.Len()), ModTime: time.Now(), diff --git a/arch/arch_test.go b/arch/arch_test.go index bfb5ba00..f459f02e 100644 --- a/arch/arch_test.go +++ b/arch/arch_test.go @@ -228,7 +228,7 @@ func TestArchMtree(t *testing.T) { { Destination: "/3", Time: 12345, - Mode: 0644, + Mode: 0o644, Size: 100, Type: "file", MD5: []byte{0xAB, 0xCD}, @@ -238,7 +238,7 @@ func TestArchMtree(t *testing.T) { LinkSource: "/bin/bash", Destination: "/sh", Time: 123456, - Mode: 0777, + Mode: 0o777, Type: "symlink", }, }) diff --git a/internal/cmd/root.go b/internal/cmd/root.go index 2c384410..b917e02c 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -4,9 +4,9 @@ import ( "fmt" _ "github.com/goreleaser/nfpm/v2/apk" + _ "github.com/goreleaser/nfpm/v2/arch" _ "github.com/goreleaser/nfpm/v2/deb" _ "github.com/goreleaser/nfpm/v2/rpm" - _ "github.com/goreleaser/nfpm/v2/arch" "github.com/spf13/cobra" ) From f7e4982df305d3b48ecd1b5c6a2d656ed59897e9 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sun, 11 Sep 2022 19:54:22 -0700 Subject: [PATCH 12/30] fix: Handle errors as recommended by linter --- arch/arch.go | 17 ++++++++++++++--- arch/arch_test.go | 8 ++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/arch/arch.go b/arch/arch.go index 0dacd1ac..a4d4ed2a 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -334,7 +334,10 @@ func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntr // Description cannot containe newlines pkgdesc := strings.ReplaceAll(info.Description, "\n", " ") - io.WriteString(buf, "# Generated by nfpm\n") + _, err = io.WriteString(buf, "# Generated by nfpm\n") + if err != nil { + return nil, err + } builddate := strconv.FormatInt(time.Now().Unix(), 10) totalSizeStr := strconv.FormatInt(totalSize, 10) @@ -650,10 +653,18 @@ func writeScripts(w io.Writer, scripts map[string]string) error { if err != nil { return err } - io.Copy(w, fl) + + _, err = io.Copy(w, fl) + if err != nil { + return err + } + fl.Close() - io.WriteString(w, "}\n\n") + _, err = io.WriteString(w, "}\n\n") + if err != nil { + return err + } } return nil diff --git a/arch/arch_test.go b/arch/arch_test.go index f459f02e..53bd37c5 100644 --- a/arch/arch_test.go +++ b/arch/arch_test.go @@ -167,6 +167,8 @@ func TestArchOverrideArchitecture(t *testing.T) { } func makeTestPkginfo(t *testing.T, info *nfpm.Info) ([]byte, error) { + t.Helper() + buf := &bytes.Buffer{} tw := tar.NewWriter(buf) @@ -178,7 +180,8 @@ func makeTestPkginfo(t *testing.T, info *nfpm.Info) ([]byte, error) { tw.Close() tr := tar.NewReader(buf) - tr.Next() + _, err = tr.Next() + require.NoError(t, err) pkginfoData := make([]byte, entry.Size) _, err = io.ReadFull(tr, pkginfoData) @@ -247,7 +250,8 @@ func TestArchMtree(t *testing.T) { tw.Close() tr := tar.NewReader(buf) - tr.Next() + _, err = tr.Next() + require.NoError(t, err) gr, err := pgzip.NewReader(tr) require.NoError(t, err) From c5c7bb48ba5cfe841a6fcfa617e84f9f9b936313 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sun, 11 Sep 2022 20:15:55 -0700 Subject: [PATCH 13/30] fix: Allow changing the pkgbase --- arch/arch.go | 2 +- arch/arch_test.go | 9 +++++++++ nfpm.go | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/arch.go b/arch/arch.go index a4d4ed2a..adceac8e 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -345,7 +345,7 @@ func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntr err = writeKVPairs(buf, map[string]string{ "size": totalSizeStr, "pkgname": info.Name, - "pkgbase": info.Name, + "pkgbase": defaultStr(info.ArchLinux.Pkgbase, info.Name), "pkgver": pkgver, "pkgdesc": pkgdesc, "url": info.Homepage, diff --git a/arch/arch_test.go b/arch/arch_test.go index 53bd37c5..b78918a4 100644 --- a/arch/arch_test.go +++ b/arch/arch_test.go @@ -140,6 +140,15 @@ func TestArchPkginfo(t *testing.T) { require.Equal(t, "etc/fake/fake.conf", fields["backup"]) } +func TestArchPkgbase(t *testing.T) { + info := exampleInfo() + info.ArchLinux.Pkgbase = "foo" + pkginfoData, err := makeTestPkginfo(t, info) + require.NoError(t, err) + fields := extractPkginfoFields(pkginfoData) + require.Equal(t, "foo", fields["pkgbase"]) +} + func TestArchInvalidName(t *testing.T) { info := exampleInfo() info.Name = "#" diff --git a/nfpm.go b/nfpm.go index 4f872eaf..927d4e26 100644 --- a/nfpm.go +++ b/nfpm.go @@ -274,6 +274,7 @@ type Overridables struct { } type ArchLinux struct { + Pkgbase string `yaml:"pkgbase,omitempty" json:"pkgbase,omitempty" jsonschema:"title=explicitly specify the name used to refer to a split package, defaults to name"` Arch string `yaml:"arch,omitempty" json:"arch,omitempty" jsonschema:"title=architecture in archlinux nomenclature"` Packager string `yaml:"packager,omitempty" json:"packager,omitempty" jsonschema:"title=organization that packaged the software"` Scripts ArchLinuxScripts `yaml:"scripts,omitempty" json:"scripts,omitempty" jsonschema:"title=archlinux-specific scripts"` From 549cd4a0dc8f7819a7abd3b2dd904ca2f4e0af6b Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sun, 11 Sep 2022 20:18:30 -0700 Subject: [PATCH 14/30] style: Resolve semgrep findings --- arch/arch.go | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/arch/arch.go b/arch/arch.go index adceac8e..f7588b96 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -143,12 +143,7 @@ func (ArchLinux) Package(info *nfpm.Info, w io.Writer) error { return err } - err = createScripts(info, tw) - if err != nil { - return err - } - - return nil + return createScripts(info, tw) } // createFilesInTar adds the files described in the given info to the given tar writer @@ -464,11 +459,7 @@ func writeKVPair(w io.Writer, key, value string) error { } _, err = io.WriteString(w, "\n") - if err != nil { - return err - } - - return nil + return err } type MtreeEntry struct { @@ -560,11 +551,7 @@ func createMtree(info *nfpm.Info, tw *tar.Writer, entries []MtreeEntry) error { } _, err = io.Copy(tw, buf) - if err != nil { - return err - } - - return nil + return err } func createDirsMtree(dst string, created map[string]struct{}) []MtreeEntry { From 72b40b87815480b0725c501d43735168d1ad0e1d Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Sun, 11 Sep 2022 20:32:44 -0700 Subject: [PATCH 15/30] docs: Change docs to reflect new Arch Linux packager --- internal/cmd/package.go | 2 +- www/docs/cmd/nfpm_package.md | 2 +- www/docs/configuration.md | 22 ++++++++++++++++++++++ www/docs/index.md | 2 +- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/internal/cmd/package.go b/internal/cmd/package.go index 044297ba..0f8ac139 100644 --- a/internal/cmd/package.go +++ b/internal/cmd/package.go @@ -34,7 +34,7 @@ func newPackageCmd() *packageCmd { cmd.Flags().StringVarP(&root.config, "config", "f", "nfpm.yaml", "config file to be used") cmd.Flags().StringVarP(&root.target, "target", "t", "", "where to save the generated package (filename, folder or empty for current folder)") - cmd.Flags().StringVarP(&root.packager, "packager", "p", "", "which packager implementation to use [apk|deb|rpm]") + cmd.Flags().StringVarP(&root.packager, "packager", "p", "", "which packager implementation to use [apk|deb|rpm|archlinux]") root.cmd = cmd return root diff --git a/www/docs/cmd/nfpm_package.md b/www/docs/cmd/nfpm_package.md index 60ba6acf..4990a11d 100644 --- a/www/docs/cmd/nfpm_package.md +++ b/www/docs/cmd/nfpm_package.md @@ -11,7 +11,7 @@ nfpm package [flags] ``` -f, --config string config file to be used (default "nfpm.yaml") -h, --help help for package - -p, --packager string which packager implementation to use [apk|deb|rpm] + -p, --packager string which packager implementation to use [apk|deb|rpm|archlinux] -t, --target string where to save the generated package (filename, folder or empty for current folder) ``` diff --git a/www/docs/configuration.md b/www/docs/configuration.md index 7781e8f4..8aabbcc2 100644 --- a/www/docs/configuration.md +++ b/www/docs/configuration.md @@ -213,6 +213,10 @@ overrides: # ... apk: # ... + archlinux: + depends: + - baz + - some-lib # Custom configuration applied only to the RPM packager. rpm: @@ -329,6 +333,24 @@ apk: key_name: origin # APK does not use pgp keys, so the key_id field is ignored. key_id: ignored + +archlinux: + # This value is used to specify the name used to refer to a group + # of packages when building a split package. Defaults to name + # See: https://wiki.archlinux.org/title/PKGBUILD#pkgbase + pkgbase: bar + # The packager identifies the organization packaging the software + # rather than the developer. Defaults to "Unknown Packager". + packager: GoReleaser + + # Arch Linux specific scripts. + scripts: + # The preupdate script runs before pacman updates the package + preupdate: ./scripts/preupdate.sh + # The postupdate script runs after pacman updates the package + postupdate: ./scripts/postupdate.sh + + ``` ## Templating diff --git a/www/docs/index.md b/www/docs/index.md index 683bf0b3..c5ac7c3e 100644 --- a/www/docs/index.md +++ b/www/docs/index.md @@ -2,7 +2,7 @@ ![](/static/banner.svg) -nFPM is a simple, 0-dependencies, `deb`, `rpm` and `apk` packager. +nFPM is a simple, 0-dependencies, `deb`, `rpm`, `apk`, and Arch Linux packager. ## Why From c5dfae631ce59f4d5a1ab3169f367f262adccbc6 Mon Sep 17 00:00:00 2001 From: Arsen6331 Date: Tue, 13 Sep 2022 00:26:41 +0000 Subject: [PATCH 16/30] docs: Fix spelling mistake in comment Co-authored-by: Dj Gilcrease --- arch/arch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arch.go b/arch/arch.go index f7588b96..f0e96c2e 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -77,7 +77,7 @@ func (ArchLinux) ConventionalFileName(info *nfpm.Info) string { return validPkgName(name) } -// validPkgName remoces any invalid characters from a string +// validPkgName removes any invalid characters from a string func validPkgName(s string) string { s = strings.Map(mapValidChar, s) s = strings.TrimLeft(s, "-.") From 19a84eb5b4090ee34b01d4cc0a31d7301f02dc53 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Mon, 12 Sep 2022 17:34:26 -0700 Subject: [PATCH 17/30] docs: use aspell to fix all spelling mistakes --- arch/arch.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arch.go b/arch/arch.go index f0e96c2e..2e4da9f9 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -100,7 +100,7 @@ func mapValidChar(r rune) rune { return -1 } -// isOneOf checks whether a rune is one of the runes on rr +// isOneOf checks whether a rune is one of the runes in rr func isOneOf(r rune, rr ...rune) bool { for _, char := range rr { if r == char { @@ -326,7 +326,7 @@ func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntr pkgver := fmt.Sprintf("%s-%d", info.Version, pkgrel) - // Description cannot containe newlines + // Description cannot contain newlines pkgdesc := strings.ReplaceAll(info.Description, "\n", " ") _, err = io.WriteString(buf, "# Generated by nfpm\n") From d091ba9c7d6092dd9dc7d1497f71db666a95a60f Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Tue, 13 Sep 2022 11:07:08 -0700 Subject: [PATCH 18/30] feat: Handle packaging formats with non-distinct file extensions as described in #546 --- apk/apk.go | 5 +++++ arch/arch.go | 5 +++++ deb/deb.go | 5 +++++ nfpm.go | 5 +++++ rpm/rpm.go | 5 +++++ 5 files changed, 25 insertions(+) diff --git a/apk/apk.go b/apk/apk.go index b3c5aed1..a5e2f909 100644 --- a/apk/apk.go +++ b/apk/apk.go @@ -101,6 +101,11 @@ func (a *Apk) ConventionalFileName(info *nfpm.Info) string { return fmt.Sprintf("%s_%s_%s.apk", info.Name, version, info.Arch) } +// ConventionalExtension returns the file name conventionally used for Apk packages +func (*Apk) ConventionalExtension() string { + return ".deb" +} + // Package writes a new apk package to the given writer using the given info. func (*Apk) Package(info *nfpm.Info, apk io.Writer) (err error) { info = ensureValidArch(info) diff --git a/arch/arch.go b/arch/arch.go index 2e4da9f9..7968dfdb 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -146,6 +146,11 @@ func (ArchLinux) Package(info *nfpm.Info, w io.Writer) error { return createScripts(info, tw) } +// ConventionalExtension returns the file name conventionally used for Arch Linux packages +func (ArchLinux) ConventionalExtension() string { + return ".pkg.tar.zst" +} + // createFilesInTar adds the files described in the given info to the given tar writer func createFilesInTar(info *nfpm.Info, tw *tar.Writer) ([]MtreeEntry, int64, error) { created := map[string]struct{}{} diff --git a/deb/deb.go b/deb/deb.go index ea755887..5266b67a 100644 --- a/deb/deb.go +++ b/deb/deb.go @@ -84,6 +84,11 @@ func (*Deb) ConventionalFileName(info *nfpm.Info) string { return fmt.Sprintf("%s_%s_%s.deb", info.Name, version, info.Arch) } +// ConventionalExtension returns the file name conventionally used for Deb packages +func (*Deb) ConventionalExtension() string { + return ".deb" +} + // ErrInvalidSignatureType happens if the signature type of a deb is not one of // origin, maint or archive. var ErrInvalidSignatureType = errors.New("invalid signature type") diff --git a/nfpm.go b/nfpm.go index 927d4e26..4df9d37c 100644 --- a/nfpm.go +++ b/nfpm.go @@ -100,6 +100,11 @@ type Packager interface { ConventionalFileName(info *Info) string } +type PackagerWithExtension interface { + Packager + ConventionalExtension() string +} + // Config contains the top level configuration for packages. type Config struct { Info `yaml:",inline" json:",inline"` diff --git a/rpm/rpm.go b/rpm/rpm.go index bf33e992..75ca7821 100644 --- a/rpm/rpm.go +++ b/rpm/rpm.go @@ -93,6 +93,11 @@ func (*RPM) ConventionalFileName(info *nfpm.Info) string { return fmt.Sprintf("%s-%s.%s.rpm", info.Name, version, info.Arch) } +// ConventionalExtension returns the file name conventionally used for RPM packages +func (*RPM) ConventionalExtension() string { + return ".rpm" +} + // Package writes a new RPM package to the given writer using the given info. func (*RPM) Package(info *nfpm.Info, w io.Writer) (err error) { var ( From 691959eb6469abda5a994ae8671a8d75f872b94f Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Wed, 14 Sep 2022 02:22:17 -0700 Subject: [PATCH 19/30] fix: Add newline to generated .INSTALL file --- arch/arch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arch.go b/arch/arch.go index 7968dfdb..01e9f743 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -653,7 +653,7 @@ func writeScripts(w io.Writer, scripts map[string]string) error { fl.Close() - _, err = io.WriteString(w, "}\n\n") + _, err = io.WriteString(w, "\n}\n\n") if err != nil { return err } From 08bb1863b0227f6bc5c89c6253636d4fc48703bc Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Wed, 14 Sep 2022 02:29:40 -0700 Subject: [PATCH 20/30] fix: Take into account provided info for non-symlink files --- arch/arch.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/arch/arch.go b/arch/arch.go index 01e9f743..9c206d86 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -169,9 +169,15 @@ func createFilesInTar(info *nfpm.Info, tw *tar.Writer) ([]MtreeEntry, int64, err return nil, 0, err } + modtime := time.Now() + // If the time is given, use it + if content.FileInfo != nil && !content.ModTime().IsZero() { + modtime = content.ModTime() + } + entries = append(entries, MtreeEntry{ Destination: path, - Time: time.Now().Unix(), + Time: modtime.Unix(), Type: content.Type, }) case "symlink": @@ -229,6 +235,18 @@ func createFilesInTar(info *nfpm.Info, tw *tar.Writer) ([]MtreeEntry, int64, err ModTime: srcFi.ModTime(), } + if content.FileInfo != nil && content.Mode() != 0 { + header.Mode = int64(content.Mode()) + } + + if content.FileInfo != nil && !content.ModTime().IsZero() { + header.ModTime = content.ModTime() + } + + if content.FileInfo != nil && content.Size() != 0 { + header.Size = content.Size() + } + err = tw.WriteHeader(header) if err != nil { return nil, 0, err From 05dacae49250c14a7ec2cade7643ac48df1c00f9 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Wed, 14 Sep 2022 02:55:17 -0700 Subject: [PATCH 21/30] docs: Fix names for arch-specific scripts in documentation --- www/docs/configuration.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/www/docs/configuration.md b/www/docs/configuration.md index 8aabbcc2..ab3e3948 100644 --- a/www/docs/configuration.md +++ b/www/docs/configuration.md @@ -345,10 +345,10 @@ archlinux: # Arch Linux specific scripts. scripts: - # The preupdate script runs before pacman updates the package - preupdate: ./scripts/preupdate.sh - # The postupdate script runs after pacman updates the package - postupdate: ./scripts/postupdate.sh + # The postupgrade script runs before pacman upgrades the package + preupgrade: ./scripts/preupgrade.sh + # The postupgrade script runs after pacman upgrades the package + postupgrade: ./scripts/postupgrade.sh ``` From c822651d71d611e8b31016989b87ac7f866003b2 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Wed, 14 Sep 2022 03:08:52 -0700 Subject: [PATCH 22/30] fix: Only consider files with the correct packager field --- arch/arch.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arch.go b/arch/arch.go index 9c206d86..20a57c82 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -158,6 +158,10 @@ func createFilesInTar(info *nfpm.Info, tw *tar.Writer) ([]MtreeEntry, int64, err var totalSize int64 for _, content := range info.Contents { + if content.Packager != "" && content.Packager != packagerName { + continue + } + path := normalizePath(content.Destination) switch content.Type { From aa0519e408b6ffee651843e21c75d5aaf05a1681 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Wed, 14 Sep 2022 12:38:47 -0700 Subject: [PATCH 23/30] fix: Use correct scripts field for post_remove script --- arch/arch.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arch.go b/arch/arch.go index 20a57c82..2c941224 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -621,7 +621,7 @@ func createScripts(info *nfpm.Info, tw *tar.Writer) error { scripts["pre_remove"] = info.Scripts.PreRemove } - if info.Scripts.PostInstall != "" { + if info.Scripts.PostRemove != "" { scripts["post_remove"] = info.Scripts.PostRemove } @@ -641,7 +641,7 @@ func createScripts(info *nfpm.Info, tw *tar.Writer) error { err := writeScripts(buf, scripts) if err != nil { - return nil + return err } err = tw.WriteHeader(&tar.Header{ From 58d9394c317b0c1a311fabdb28fc47887cb71c9d Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Wed, 14 Sep 2022 12:39:12 -0700 Subject: [PATCH 24/30] test: Implement archlinux acceptance tests --- testdata/acceptance/archlinux.dockerfile | 131 +++++++++++++++++++++++ testdata/acceptance/core.complex.yaml | 20 ++++ testdata/acceptance/core.overrides.yaml | 4 + testdata/acceptance/upgrade.v1.yaml | 4 + testdata/acceptance/upgrade.v2.yaml | 4 + 5 files changed, 163 insertions(+) create mode 100644 testdata/acceptance/archlinux.dockerfile diff --git a/testdata/acceptance/archlinux.dockerfile b/testdata/acceptance/archlinux.dockerfile new file mode 100644 index 00000000..75e5ab83 --- /dev/null +++ b/testdata/acceptance/archlinux.dockerfile @@ -0,0 +1,131 @@ +FROM archlinux AS test_base +ARG package +RUN echo "${package}" +COPY ${package} /tmp/foo.pkg.tar.zst + + +# ---- minimal test ---- +FROM test_base AS min +RUN pacman --noconfirm -U /tmp/foo.pkg.tar.zst + + +# ---- symlink test ---- +FROM min AS symlink +RUN ls -l /path/to/symlink | grep "/path/to/symlink -> /etc/foo/whatever.conf" + + +# ---- simple test ---- +FROM min AS simple +RUN test -e /usr/local/bin/fake +RUN test -f /etc/foo/whatever.conf +RUN echo wat >> /etc/foo/whatever.conf +RUN pacman --noconfirm -R foo +RUN test -f /etc/foo/whatever.conf.pacsave +RUN test ! -f /usr/local/bin/fake + + +# ---- no-glob test ---- +FROM min AS no-glob +RUN test -d /usr/share/whatever/ +RUN test -f /usr/share/whatever/file1 +RUN test -f /usr/share/whatever/file2 +RUN test -d /usr/share/whatever/folder2 +RUN test -f /usr/share/whatever/folder2/file1 +RUN test -f /usr/share/whatever/folder2/file2 + + +# ---- complex test ---- +FROM min AS complex +RUN pacman -Qi foo | grep "Depends On\\s*: bash" +RUN pacman -Qi foo | grep "Replaces\\s*: foo" +RUN pacman -Qi foo | grep "Provides\\s*: fake" +RUN test -e /usr/local/bin/fake +RUN test -f /etc/foo/whatever.conf +RUN test -d /usr/share/whatever/ +RUN test -d /usr/share/whatever/folder +RUN test -f /usr/share/whatever/folder/file1 +RUN test -f /usr/share/whatever/folder/file2 +RUN test -d /usr/share/whatever/folder/folder2 +RUN test -f /usr/share/whatever/folder/folder2/file1 +RUN test -f /usr/share/whatever/folder/folder2/file2 +RUN test -d /var/log/whatever +RUN test -d /usr/share/foo +RUN test -d /usr/foo/bar/something +RUN test $(stat -c %a /usr/bin/fake) -eq 4755 +RUN test -f /tmp/preinstall-proof +RUN test -f /tmp/postinstall-proof +RUN test ! -f /tmp/preremove-proof +RUN test ! -f /tmp/postremove-proof +RUN echo wat >> /etc/foo/whatever.conf +RUN pacman --noconfirm -R foo +RUN test -f /etc/foo/whatever.conf.pacsave +RUN test ! -f /usr/local/bin/fake +RUN test ! -f /usr/bin/fake +RUN test -f /tmp/preremove-proof +RUN test -f /tmp/postremove-proof +RUN test ! -d /var/log/whatever +RUN test ! -d /usr/share/foo +RUN test ! -d /usr/foo/bar/something + + +# ---- signed test ---- +FROM min AS signed +RUN echo "Arch Linux has no signature support" + + +# ---- overrides test ---- +FROM min AS overrides +RUN test -e /usr/local/bin/fake +RUN test -f /etc/foo/whatever.conf +RUN test ! -f /tmp/preinstall-proof +RUN test -f /tmp/postinstall-proof +RUN test ! -f /tmp/preremove-proof +RUN test ! -f /tmp/postremove-proof +RUN echo wat >> /etc/foo/whatever.conf +RUN pacman --noconfirm -R foo +RUN test -f /etc/foo/whatever.conf.pacsave +RUN test ! -f /usr/local/bin/fake +RUN test -f /tmp/preremove-proof +RUN test ! -f /tmp/postremove-proof + + +# ---- meta test ---- +FROM test_base AS meta +RUN pacman -Sy && pacman --noconfirm -U /tmp/foo.pkg.tar.zst +RUN command -v zsh + + +# ---- env-var-version test ---- +FROM min AS env-var-version +ENV EXPECTVER="foo 1.0.0-1" +RUN export FOUND_VER="$(pacman -Q foo)" && \ + echo "Expected: '${EXPECTVER}' :: Found: '${FOUND_VER}'" && \ + test "${FOUND_VER}" = "${EXPECTVER}" + + +# ---- changelog test ---- +FROM min AS withchangelog +RUN echo "Arch Linux has no changelog support" + + +# ---- upgrade test ---- +FROM test_base AS upgrade +ARG oldpackage +RUN echo "${oldpackage}" +COPY ${oldpackage} /tmp/old_foo.pkg.tar.zst +RUN pacman --noconfirm -U /tmp/old_foo.pkg.tar.zst +RUN test -f /tmp/preinstall-proof +RUN cat /tmp/preinstall-proof | grep "Install" +RUN test -f /tmp/postinstall-proof +RUN cat /tmp/postinstall-proof | grep "Install" +RUN test ! -f /tmp/preupgrade-proof +RUN test ! -f /tmp/postupgrade-proof +RUN echo modified > /etc/regular.conf +RUN echo modified > /etc/noreplace.conf +RUN pacman --noconfirm -U /tmp/foo.pkg.tar.zst +RUN test -f /tmp/preupgrade-proof +RUN test -f /tmp/postupgrade-proof +RUN test -f /etc/regular.conf +RUN test -f /etc/regular.conf.pacnew +RUN test -f /etc/noreplace.conf +RUN test -f /etc/noreplace.conf.pacnew \ No newline at end of file diff --git a/testdata/acceptance/core.complex.yaml b/testdata/acceptance/core.complex.yaml index 48e0114b..6e302ed1 100644 --- a/testdata/acceptance/core.complex.yaml +++ b/testdata/acceptance/core.complex.yaml @@ -29,8 +29,24 @@ contents: type: config - src: ./testdata/fake dst: /usr/sbin/fake + packager: deb file_info: mode: 04755 +- src: ./testdata/fake + dst: /usr/sbin/fake + packager: rpm + file_info: + mode: 04755 +- src: ./testdata/fake + dst: /usr/sbin/fake + packager: apk + file_info: + mode: 04755 +- src: ./testdata/fake + dst: /usr/bin/fake + file_info: + mode: 04755 + packager: archlinux - dst: /usr/foo/bar/something type: dir - dst: /var/log/whatever @@ -50,3 +66,7 @@ apk: scripts: preupgrade: ./testdata/acceptance/scripts/preupgrade.sh postupgrade: ./testdata/acceptance/scripts/postupgrade.sh +archlinux: + scripts: + preupgrade: ./testdata/acceptance/scripts/preupgrade.sh + postupgrade: ./testdata/acceptance/scripts/postupgrade.sh \ No newline at end of file diff --git a/testdata/acceptance/core.overrides.yaml b/testdata/acceptance/core.overrides.yaml index 0f31da91..010d1fbc 100644 --- a/testdata/acceptance/core.overrides.yaml +++ b/testdata/acceptance/core.overrides.yaml @@ -38,3 +38,7 @@ overrides: scripts: postinstall: ./testdata/acceptance/scripts/postinstall.sh preremove: ./testdata/acceptance/scripts/preremove.sh + archlinux: + scripts: + postinstall: ./testdata/acceptance/scripts/postinstall.sh + preremove: ./testdata/acceptance/scripts/preremove.sh \ No newline at end of file diff --git a/testdata/acceptance/upgrade.v1.yaml b/testdata/acceptance/upgrade.v1.yaml index bcbed9e2..d69ec75f 100644 --- a/testdata/acceptance/upgrade.v1.yaml +++ b/testdata/acceptance/upgrade.v1.yaml @@ -39,3 +39,7 @@ apk: scripts: preupgrade: ./testdata/acceptance/scripts/preupgrade.sh postupgrade: ./testdata/acceptance/scripts/postupgrade.sh +archlinux: + scripts: + preupgrade: ./testdata/acceptance/scripts/preupgrade.sh + postupgrade: ./testdata/acceptance/scripts/postupgrade.sh \ No newline at end of file diff --git a/testdata/acceptance/upgrade.v2.yaml b/testdata/acceptance/upgrade.v2.yaml index 09c11723..58899603 100644 --- a/testdata/acceptance/upgrade.v2.yaml +++ b/testdata/acceptance/upgrade.v2.yaml @@ -39,3 +39,7 @@ apk: scripts: preupgrade: ./testdata/acceptance/scripts/preupgrade.sh postupgrade: ./testdata/acceptance/scripts/postupgrade.sh +archlinux: + scripts: + preupgrade: ./testdata/acceptance/scripts/preupgrade.sh + postupgrade: ./testdata/acceptance/scripts/postupgrade.sh \ No newline at end of file From 27cae0b06c4d9892b919580bc14e548deda67973 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Wed, 14 Sep 2022 12:46:15 -0700 Subject: [PATCH 25/30] test: Add archlinux to acceptance_test.go --- acceptance_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/acceptance_test.go b/acceptance_test.go index 92012598..e2dd7d0c 100644 --- a/acceptance_test.go +++ b/acceptance_test.go @@ -13,6 +13,7 @@ import ( "github.com/goreleaser/nfpm/v2" _ "github.com/goreleaser/nfpm/v2/apk" + _ "github.com/goreleaser/nfpm/v2/arch" _ "github.com/goreleaser/nfpm/v2/deb" _ "github.com/goreleaser/nfpm/v2/rpm" "github.com/stretchr/testify/require" @@ -20,9 +21,10 @@ import ( // nolint: gochecknoglobals var formatArchs = map[string][]string{ - "apk": {"amd64", "arm64", "386", "ppc64le", "armv6", "armv7", "s390x"}, - "deb": {"amd64", "arm64", "ppc64le", "armv7", "s390x"}, - "rpm": {"amd64", "arm64", "ppc64le", "armv7"}, + "apk": {"amd64", "arm64", "386", "ppc64le", "armv6", "armv7", "s390x"}, + "deb": {"amd64", "arm64", "ppc64le", "armv7", "s390x"}, + "rpm": {"amd64", "arm64", "ppc64le", "armv7"}, + "archlinux": {"amd64"}, } func TestCore(t *testing.T) { From dc1f215248c80f34eed97fa1d098c874a5526773 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Wed, 14 Sep 2022 18:36:19 -0700 Subject: [PATCH 26/30] test: Add archlinux to github test matrix --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 35864713..e41efc76 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,7 +47,7 @@ jobs: strategy: matrix: go-version: [ 1.19 ] - pkgFormat: [ deb, rpm, apk ] + pkgFormat: [ deb, rpm, apk, archlinux ] pkgPlatform: [ amd64, arm64, 386, ppc64le, armv6, armv7, s390x ] runs-on: ubuntu-latest env: From b3e7c151d99a998b083a3d820d4558e96e8db1cd Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Wed, 14 Sep 2022 18:49:26 -0700 Subject: [PATCH 27/30] test: Use updated build.yml from main branch --- .github/workflows/build.yml | 55 ++++++++++++++----------------------- 1 file changed, 20 insertions(+), 35 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e41efc76..a1419c46 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,11 +8,21 @@ on: - main pull_request: +permissions: + contents: write + id-token: write + packages: write + jobs: + govulncheck: + uses: caarlos0/meta/.github/workflows/govulncheck.yml@main + semgrep: + uses: caarlos0/meta/.github/workflows/semgrep.yml@main + ruleguard: + uses: caarlos0/meta/.github/workflows/ruleguard.yml@main unit-tests: strategy: matrix: - go-version: [ 1.19 ] os: [ ubuntu-latest, macos-latest, windows-latest ] runs-on: ${{ matrix.os }} steps: @@ -21,18 +31,11 @@ jobs: fetch-depth: 0 - uses: actions/setup-go@v3 with: - go-version: ${{ matrix.go-version }} + go-version: '~1.19' + cache: true - uses: arduino/setup-task@v1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/cache@v3 - with: - path: | - ~/go/pkg/mod - ~/.cache/go-build - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - name: setup-tparse run: go install github.com/mfridman/tparse@latest - run: task setup @@ -43,10 +46,9 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} file: ./coverage.txt - Acceptance-Tests: + acceptance-tests: strategy: matrix: - go-version: [ 1.19 ] pkgFormat: [ deb, rpm, apk, archlinux ] pkgPlatform: [ amd64, arm64, 386, ppc64le, armv6, armv7, s390x ] runs-on: ubuntu-latest @@ -57,18 +59,11 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: ${{ matrix.go-version }} + go-version: '~1.19' + cache: true - uses: arduino/setup-task@v1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/cache@v3 - with: - path: | - ~/go/pkg/mod - ~/.cache/go-build - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - uses: docker/setup-qemu-action@v2 - uses: docker/setup-buildx-action@v2 - run: task setup @@ -79,9 +74,6 @@ jobs: env: TEST_PATTERN: "/${{ matrix.pkgFormat }}/${{ matrix.pkgPlatform }}/" goreleaser: - strategy: - matrix: - go-version: [ 1.19 ] runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/') needs: @@ -97,19 +89,12 @@ jobs: fetch-depth: 0 - uses: actions/setup-go@v3 with: - go-version: ${{ matrix.go-version }} + go-version: '~1.19' + cache: true - uses: arduino/setup-task@v1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/cache@v3 - with: - path: | - ~/go/pkg/mod - ~/.cache/go-build - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - uses: sigstore/cosign-installer@v2.5.1 + - uses: sigstore/cosign-installer@v2.6.0 - uses: anchore/sbom-action/download-syft@v0.12.0 - uses: docker/setup-qemu-action@v2 - uses: docker/setup-buildx-action@v2 @@ -142,4 +127,4 @@ jobs: DISCORD_WEBHOOK_ID: ${{ secrets.DISCORD_WEBHOOK_ID }} DISCORD_WEBHOOK_TOKEN: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} FURY_TOKEN: ${{ secrets.FURY_TOKEN }} - AUR_KEY: ${{ secrets.AUR_KEY }} + AUR_KEY: ${{ secrets.AUR_KEY }} \ No newline at end of file From 19e2c2498d2d6c95996c02a97553cc91aed12df3 Mon Sep 17 00:00:00 2001 From: Arsen6331 Date: Tue, 20 Sep 2022 01:18:39 +0000 Subject: [PATCH 28/30] Fix ConventionalExtension() for apk --- apk/apk.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apk/apk.go b/apk/apk.go index c1195d4d..c280cfe4 100644 --- a/apk/apk.go +++ b/apk/apk.go @@ -103,7 +103,7 @@ func (a *Apk) ConventionalFileName(info *nfpm.Info) string { // ConventionalExtension returns the file name conventionally used for Apk packages func (*Apk) ConventionalExtension() string { - return ".deb" + return ".apk" } // Package writes a new apk package to the given writer using the given info. From c0e6c43412fc4f26d561eb51aa55286a63c1db7f Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Thu, 22 Sep 2022 14:03:57 -0700 Subject: [PATCH 29/30] fix: Take epoch value into account --- arch/arch.go | 6 ++++++ arch/arch_test.go | 13 ++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/arch/arch.go b/arch/arch.go index 2c941224..3bfb8821 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -352,6 +352,12 @@ func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntr } pkgver := fmt.Sprintf("%s-%d", info.Version, pkgrel) + if info.Epoch != "" { + epoch, err := strconv.ParseUint(info.Epoch, 10, 64) + if err == nil { + pkgver = fmt.Sprintf("%d:%s-%d", epoch, info.Version, pkgrel) + } + } // Description cannot contain newlines pkgdesc := strings.ReplaceAll(info.Description, "\n", " ") diff --git a/arch/arch_test.go b/arch/arch_test.go index b78918a4..c51d0d8c 100644 --- a/arch/arch_test.go +++ b/arch/arch_test.go @@ -3,7 +3,6 @@ package arch import ( "archive/tar" "bytes" - "fmt" "io" "strings" "testing" @@ -166,6 +165,16 @@ func TestArchVersionWithRelease(t *testing.T) { require.Equal(t, "0.0.1-4", fields["pkgver"]) } +func TestArchVersionWithEpoch(t *testing.T) { + info := exampleInfo() + info.Version = "0.0.1" + info.Epoch = "2" + pkginfoData, err := makeTestPkginfo(t, info) + require.NoError(t, err) + fields := extractPkginfoFields(pkginfoData) + require.Equal(t, "2:0.0.1-1", fields["pkgver"]) +} + func TestArchOverrideArchitecture(t *testing.T) { info := exampleInfo() info.ArchLinux.Arch = "randomarch" @@ -210,9 +219,7 @@ func extractPkginfoFields(data []byte) map[string]string { out := map[string]string{} for _, kvPair := range splitData { - fmt.Println(kvPair) splitPair := strings.Split(kvPair, " = ") - fmt.Println(splitPair, len(splitPair), splitPair[0], splitPair[1]) out[splitPair[0]] = splitPair[1] } From eae88e8ea4b5d0c8153d641de4ff1420f7785d67 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Thu, 22 Sep 2022 14:04:14 -0700 Subject: [PATCH 30/30] fix: Add arm5 and arm6 architectures --- arch/arch.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arch.go b/arch/arch.go index 3bfb8821..e724fc1f 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -42,7 +42,9 @@ var archToArchLinux = map[string]string{ "amd64": "x86_64", "386": "i686", "arm64": "aarch64", - "arm7": "armv7hl", + "arm7": "armv7h", + "arm6": "armv6h", + "arm5": "arm", } func ensureValidArch(info *nfpm.Info) *nfpm.Info {