mirror of
https://github.com/goreleaser/nfpm
synced 2024-05-18 08:16:22 +02:00
feat: add support for Arch Linux packages (#543)
* feat: add support for Arch Linux packages * test: Add initial tests * test: Increase coverage by modifying example info * test: Add test for ArchLinux.ConventionalFileName() * docs: Return error if package name is invalid * fix: Make empty name invalid * fix: Add replaces field to .PKGINFO generator * test: Add additional tests * test: Test for added replaces field * docs: Add more comments * style: Run gofumpt * fix: Handle errors as recommended by linter * fix: Allow changing the pkgbase * style: Resolve semgrep findings * docs: Change docs to reflect new Arch Linux packager * docs: Fix spelling mistake in comment Co-authored-by: Dj Gilcrease <digitalxero@gmail.com> * docs: use aspell to fix all spelling mistakes * feat: Handle packaging formats with non-distinct file extensions as described in #546 * fix: Add newline to generated .INSTALL file * fix: Take into account provided info for non-symlink files * docs: Fix names for arch-specific scripts in documentation * fix: Only consider files with the correct packager field * fix: Use correct scripts field for post_remove script * test: Implement archlinux acceptance tests * test: Add archlinux to acceptance_test.go * test: Add archlinux to github test matrix * test: Use updated build.yml from main branch * Fix ConventionalExtension() for apk * fix: Take epoch value into account * fix: Add arm5 and arm6 architectures Co-authored-by: Dj Gilcrease <digitalxero@gmail.com> Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
This commit is contained in:
parent
1a66c73f3a
commit
a18661b627
|
@ -64,7 +64,7 @@ jobs:
|
|||
acceptance-tests:
|
||||
strategy:
|
||||
matrix:
|
||||
pkgFormat: [ deb, rpm, apk ]
|
||||
pkgFormat: [ deb, rpm, apk, archlinux ]
|
||||
pkgPlatform: [ amd64, arm64, 386, ppc64le, armv6, armv7, s390x ]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
|
@ -88,4 +88,3 @@ jobs:
|
|||
run: ./scripts/test.sh acceptance ubuntu-latest
|
||||
env:
|
||||
TEST_PATTERN: "/${{ matrix.pkgFormat }}/${{ matrix.pkgPlatform }}/"
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 ".apk"
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
|
|
@ -0,0 +1,693 @@
|
|||
// Package arch implements nfpm.Packager providing bindings for Arch Linux packages.
|
||||
package arch
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"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"
|
||||
)
|
||||
|
||||
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
|
||||
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": "armv7h",
|
||||
"arm6": "armv6h",
|
||||
"arm5": "arm",
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// validPkgName removes 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' ||
|
||||
r >= '0' && r <= '9' ||
|
||||
isOneOf(r, '.', '_', '+', '-') {
|
||||
return r
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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{}{}
|
||||
var entries []MtreeEntry
|
||||
var totalSize int64
|
||||
|
||||
for _, content := range info.Contents {
|
||||
if content.Packager != "" && content.Packager != packagerName {
|
||||
continue
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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: 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 the time is given, use it
|
||||
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: 0o777,
|
||||
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(),
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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: 0o755,
|
||||
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) {
|
||||
if !nameIsValid(info.Name) {
|
||||
return nil, ErrInvalidPkgName
|
||||
}
|
||||
|
||||
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)
|
||||
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", " ")
|
||||
|
||||
_, 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)
|
||||
|
||||
err = writeKVPairs(buf, map[string]string{
|
||||
"size": totalSizeStr,
|
||||
"pkgname": info.Name,
|
||||
"pkgbase": defaultStr(info.ArchLinux.Pkgbase, info.Name),
|
||||
"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 _, 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 {
|
||||
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: 0o644,
|
||||
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: 0o644,
|
||||
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")
|
||||
return err
|
||||
}
|
||||
|
||||
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: 0o644,
|
||||
Name: ".MTREE",
|
||||
Size: int64(buf.Len()),
|
||||
ModTime: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(tw, buf)
|
||||
return err
|
||||
}
|
||||
|
||||
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: 0o755,
|
||||
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.PostRemove != "" {
|
||||
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 err
|
||||
}
|
||||
|
||||
err = tw.WriteHeader(&tar.Header{
|
||||
Typeflag: tar.TypeReg,
|
||||
Mode: 0o644,
|
||||
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
|
||||
}
|
||||
|
||||
_, err = io.Copy(w, fl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fl.Close()
|
||||
|
||||
_, err = io.WriteString(w, "\n}\n\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
package arch
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/goreleaser/nfpm/v2"
|
||||
"github.com/goreleaser/nfpm/v2/files"
|
||||
"github.com/klauspost/pgzip"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func exampleInfo() *nfpm.Info {
|
||||
return nfpm.WithDefaults(&nfpm.Info{
|
||||
Name: "foo-test",
|
||||
Arch: "amd64",
|
||||
Description: "Foo does things",
|
||||
Priority: "extra",
|
||||
Maintainer: "Carlos A Becker <pkg@carlosbecker.com>",
|
||||
Version: "1.0.0",
|
||||
Section: "default",
|
||||
Homepage: "http://carlosbecker.com",
|
||||
Vendor: "nope",
|
||||
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",
|
||||
},
|
||||
{
|
||||
Source: "/etc/fake/fake.conf",
|
||||
Destination: "/etc/fake/fake-link.conf",
|
||||
Type: "symlink",
|
||||
},
|
||||
},
|
||||
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 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
|
||||
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) {
|
||||
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"])
|
||||
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, "svn", fields["replaces"])
|
||||
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 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 = "#"
|
||||
_, 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 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"
|
||||
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) {
|
||||
t.Helper()
|
||||
|
||||
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)
|
||||
_, err = tr.Next()
|
||||
require.NoError(t, err)
|
||||
|
||||
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")
|
||||
strData = strings.TrimSpace(strData)
|
||||
|
||||
splitData := strings.Split(strData, "\n")
|
||||
out := map[string]string{}
|
||||
|
||||
for _, kvPair := range splitData {
|
||||
splitPair := strings.Split(kvPair, " = ")
|
||||
out[splitPair[0]] = splitPair[1]
|
||||
}
|
||||
|
||||
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: 0o644,
|
||||
Size: 100,
|
||||
Type: "file",
|
||||
MD5: []byte{0xAB, 0xCD},
|
||||
SHA256: []byte{0xEF, 0x12},
|
||||
},
|
||||
{
|
||||
LinkSource: "/bin/bash",
|
||||
Destination: "/sh",
|
||||
Time: 123456,
|
||||
Mode: 0o777,
|
||||
Type: "symlink",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
tw.Close()
|
||||
|
||||
tr := tar.NewReader(buf)
|
||||
_, err = tr.Next()
|
||||
require.NoError(t, err)
|
||||
|
||||
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)
|
||||
}
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -4,6 +4,7 @@ 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/spf13/cobra"
|
||||
|
|
18
nfpm.go
18
nfpm.go
|
@ -106,6 +106,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"`
|
||||
|
@ -294,6 +299,19 @@ 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 {
|
||||
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"`
|
||||
}
|
||||
|
||||
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.
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -44,3 +44,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
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
```
|
||||
|
||||
|
|
|
@ -231,6 +231,10 @@ overrides:
|
|||
# ...
|
||||
apk:
|
||||
# ...
|
||||
archlinux:
|
||||
depends:
|
||||
- baz
|
||||
- some-lib
|
||||
|
||||
# Custom configuration applied only to the RPM packager.
|
||||
rpm:
|
||||
|
@ -347,6 +351,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 <staff@goreleaser.com>
|
||||
|
||||
# Arch Linux specific scripts.
|
||||
scripts:
|
||||
# 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
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Templating
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue