1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-03-17 20:47:50 +02:00

feat: add packager-specific nfpm config (#1849)

* feat: Add deb packager-specific nfpm config (#1829)

* Use env vars containing nfpm ids for deb pgp passphrase

* Add docs for nfpm id in env var

* Custon -> Custom

* Switch test cases

* Forward RPM specific config to nfpm

* Document rpm-specific nfpm config

* Add APK-specific nfpm config

* Document apk-specific nfpm config

* avaiable -> available

* Add deb scripts templates to nfpm config
This commit is contained in:
Jonathan Lloyd 2020-11-05 21:47:55 +00:00 committed by GitHub
parent f1049b94ef
commit 17e56747cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 446 additions and 0 deletions

View File

@ -5,6 +5,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/apex/log"
"github.com/goreleaser/nfpm"
@ -173,6 +174,43 @@ func create(ctx *context.Context, fpm config.NFPM, format, arch string, binaries
PreRemove: overridden.Scripts.PreRemove,
PostRemove: overridden.Scripts.PostRemove,
},
Deb: nfpm.Deb{
Scripts: nfpm.DebScripts{
Rules: overridden.Deb.Scripts.Rules,
Templates: overridden.Deb.Scripts.Templates,
},
Triggers: nfpm.DebTriggers{
Interest: overridden.Deb.Triggers.Interest,
InterestAwait: overridden.Deb.Triggers.InterestAwait,
InterestNoAwait: overridden.Deb.Triggers.InterestNoAwait,
Activate: overridden.Deb.Triggers.Activate,
ActivateAwait: overridden.Deb.Triggers.ActivateAwait,
ActivateNoAwait: overridden.Deb.Triggers.ActivateNoAwait,
},
Breaks: overridden.Deb.Breaks,
VersionMetadata: overridden.Deb.VersionMetadata,
Signature: nfpm.DebSignature{
KeyFile: overridden.Deb.Signature.KeyFile,
KeyPassphrase: getPassphraseFromEnv(ctx, "DEB", fpm.ID),
Type: overridden.Deb.Signature.Type,
},
},
RPM: nfpm.RPM{
Group: overridden.RPM.Group,
Compression: overridden.RPM.Compression,
ConfigNoReplaceFiles: overridden.RPM.ConfigNoReplaceFiles,
Signature: nfpm.RPMSignature{
KeyFile: overridden.RPM.Signature.KeyFile,
KeyPassphrase: getPassphraseFromEnv(ctx, "RPM", fpm.ID),
},
},
APK: nfpm.APK{
Signature: nfpm.APKSignature{
KeyFile: overridden.APK.Signature.KeyFile,
KeyPassphrase: getPassphraseFromEnv(ctx, "APK", fpm.ID),
KeyName: overridden.APK.Signature.KeyName,
},
},
},
}
@ -214,3 +252,22 @@ func create(ctx *context.Context, fpm config.NFPM, format, arch string, binaries
})
return nil
}
func getPassphraseFromEnv(ctx *context.Context, packager string, nfpmID string) string {
var passphrase string
nfpmID = strings.ToUpper(nfpmID)
packagerSpecificPassphrase := ctx.Env[fmt.Sprintf(
"NFPM_%s_%s_PASSPHRASE",
nfpmID,
packager,
)]
if packagerSpecificPassphrase != "" {
passphrase = packagerSpecificPassphrase
} else {
generalPassphrase := ctx.Env[fmt.Sprintf("NFPM_%s_PASSPHRASE", nfpmID)]
passphrase = generalPassphrase
}
return passphrase
}

View File

@ -347,6 +347,220 @@ func TestOverrides(t *testing.T) {
require.Equal(t, "bar", merged.FileNameTemplate)
}
func TestDebSpecificConfig(t *testing.T) {
folder, err := ioutil.TempDir("", "archivetest")
require.NoError(t, err)
var dist = filepath.Join(folder, "dist")
require.NoError(t, os.Mkdir(dist, 0755))
require.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0755))
var binPath = filepath.Join(dist, "mybin", "mybin")
_, err = os.Create(binPath)
require.NoError(t, err)
var ctx = context.New(config.Project{
ProjectName: "mybin",
Dist: dist,
NFPMs: []config.NFPM{
{
ID: "someid",
Builds: []string{"default"},
Formats: []string{"deb"},
NFPMOverridables: config.NFPMOverridables{
PackageName: "foo",
Files: map[string]string{
"./testdata/testfile.txt": "/usr/share/testfile.txt",
},
Deb: config.NFPMDeb{
Signature: config.NFPMDebSignature{
KeyFile: "./testdata/privkey.gpg",
},
},
},
},
},
})
ctx.Version = "1.0.0"
ctx.Git = context.GitInfo{CurrentTag: "v1.0.0"}
for _, goos := range []string{"linux", "darwin"} {
for _, goarch := range []string{"amd64", "386"} {
ctx.Artifacts.Add(&artifact.Artifact{
Name: "mybin",
Path: binPath,
Goarch: goarch,
Goos: goos,
Type: artifact.Binary,
Extra: map[string]interface{}{
"ID": "default",
},
})
}
}
t.Run("no passphrase set", func(t *testing.T) {
require.Contains(
t,
Pipe{}.Run(ctx).Error(),
`key is encrypted but no passphrase was provided`,
)
})
t.Run("general passphrase set", func(t *testing.T) {
ctx.Env = map[string]string{
"NFPM_SOMEID_PASSPHRASE": "hunter2",
}
require.NoError(t, Pipe{}.Run(ctx))
})
t.Run("packager specific passphrase set", func(t *testing.T) {
ctx.Env = map[string]string{
"NFPM_SOMEID_DEB_PASSPHRASE": "hunter2",
}
require.NoError(t, Pipe{}.Run(ctx))
})
}
func TestRPMSpecificConfig(t *testing.T) {
folder, err := ioutil.TempDir("", "archivetest")
require.NoError(t, err)
var dist = filepath.Join(folder, "dist")
require.NoError(t, os.Mkdir(dist, 0755))
require.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0755))
var binPath = filepath.Join(dist, "mybin", "mybin")
_, err = os.Create(binPath)
require.NoError(t, err)
var ctx = context.New(config.Project{
ProjectName: "mybin",
Dist: dist,
NFPMs: []config.NFPM{
{
ID: "someid",
Builds: []string{"default"},
Formats: []string{"rpm"},
NFPMOverridables: config.NFPMOverridables{
PackageName: "foo",
Files: map[string]string{
"./testdata/testfile.txt": "/usr/share/testfile.txt",
},
RPM: config.NFPMRPM{
Signature: config.NFPMRPMSignature{
KeyFile: "./testdata/privkey.gpg",
},
},
},
},
},
})
ctx.Version = "1.0.0"
ctx.Git = context.GitInfo{CurrentTag: "v1.0.0"}
for _, goos := range []string{"linux", "darwin"} {
for _, goarch := range []string{"amd64", "386"} {
ctx.Artifacts.Add(&artifact.Artifact{
Name: "mybin",
Path: binPath,
Goarch: goarch,
Goos: goos,
Type: artifact.Binary,
Extra: map[string]interface{}{
"ID": "default",
},
})
}
}
t.Run("no passphrase set", func(t *testing.T) {
require.Contains(
t,
Pipe{}.Run(ctx).Error(),
`key is encrypted but no passphrase was provided`,
)
})
t.Run("general passphrase set", func(t *testing.T) {
ctx.Env = map[string]string{
"NFPM_SOMEID_PASSPHRASE": "hunter2",
}
require.NoError(t, Pipe{}.Run(ctx))
})
t.Run("packager specific passphrase set", func(t *testing.T) {
ctx.Env = map[string]string{
"NFPM_SOMEID_RPM_PASSPHRASE": "hunter2",
}
require.NoError(t, Pipe{}.Run(ctx))
})
}
func TestAPKSpecificConfig(t *testing.T) {
folder, err := ioutil.TempDir("", "archivetest")
require.NoError(t, err)
var dist = filepath.Join(folder, "dist")
require.NoError(t, os.Mkdir(dist, 0755))
require.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0755))
var binPath = filepath.Join(dist, "mybin", "mybin")
_, err = os.Create(binPath)
require.NoError(t, err)
var ctx = context.New(config.Project{
ProjectName: "mybin",
Dist: dist,
NFPMs: []config.NFPM{
{
ID: "someid",
Maintainer: "me@me",
Builds: []string{"default"},
Formats: []string{"apk"},
NFPMOverridables: config.NFPMOverridables{
PackageName: "foo",
Files: map[string]string{
"./testdata/testfile.txt": "/usr/share/testfile.txt",
},
APK: config.NFPMAPK{
Signature: config.NFPMAPKSignature{
KeyFile: "./testdata/rsa.priv",
},
},
},
},
},
})
ctx.Version = "1.0.0"
ctx.Git = context.GitInfo{CurrentTag: "v1.0.0"}
for _, goos := range []string{"linux", "darwin"} {
for _, goarch := range []string{"amd64", "386"} {
ctx.Artifacts.Add(&artifact.Artifact{
Name: "mybin",
Path: binPath,
Goarch: goarch,
Goos: goos,
Type: artifact.Binary,
Extra: map[string]interface{}{
"ID": "default",
},
})
}
}
t.Run("no passphrase set", func(t *testing.T) {
require.Contains(
t,
Pipe{}.Run(ctx).Error(),
`key is encrypted but no passphrase was provided`,
)
})
t.Run("general passphrase set", func(t *testing.T) {
ctx.Env = map[string]string{
"NFPM_SOMEID_PASSPHRASE": "hunter2",
}
require.NoError(t, Pipe{}.Run(ctx))
})
t.Run("packager specific passphrase set", func(t *testing.T) {
ctx.Env = map[string]string{
"NFPM_SOMEID_APK_PASSPHRASE": "hunter2",
}
require.NoError(t, Pipe{}.Run(ctx))
})
}
func TestSeveralNFPMsWithTheSameID(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{

BIN
internal/pipe/nfpm/testdata/privkey.gpg vendored Normal file

Binary file not shown.

30
internal/pipe/nfpm/testdata/rsa.priv vendored Normal file
View File

@ -0,0 +1,30 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,F681796F2F5F6592720D154E441631AF
U4aCJ0bGTN+/l162THI8CQdVmn62QX7+CxGOrxz8vbPOcMic8ppBD/h4lcUmca1L
B3q9gBV+nM4KWOUjKDsmXJ7PeHrQJxR30RbGKGOP8WZSlf7oqICHX9WfjzsaLmMC
zr7z7osO4DhW0nkr+CJATplKxjF5kry2jsBohmG7AJKVB6JRenpyz/icLrwfeywo
dM1aZEw2Tm6Aso+un+rdmRxvYG5SEVLVh5M334MvOwhKHNGGfNOUJG38uym/sYXM
WbBRyOUhQXabbuHlJdr5V7mnxm+KLi+hZQAIoRsFTS7tkIUpFjtvCS0RI0L6MtkL
3N76xiW82n3SxrPdZdJCGy9AWor66QTmr25yTZpUJMYqpHaXc2pKqZOWZ/U0bAbA
Oyf/CCTwI9lLT1a+CtGTHYTXHHKhQ9n1TI2FI4DRhnDcdDix2S/shF8aeu7mcKF1
SqS8r+DTKzmbohUVa0/Ad9Z6R8Q1Ed+fl2/l0/eb3tI7c7hH+AxV+AqSAgLsZ1/T
SQoIjUX4Kna0U2wLi7wEWlwrQQE2lugoJ2tOH6F33tEd4FFh4CjTw4LKKFEIgjPj
yNKJ5CC+5jQPdyN/utR6Q+PNlxnT5jU02dAupdGfS/On2ecONmBKFCHHT+Sh3F21
Q8zKZPKTUgIqG8JrjIaO1akjJqGgr6c0kefVTYpHEH3HNNkqUvzywIMR7IooPJrD
XQYLMgTuE4ccJk7JN5OCHlSkTHPcrkuD6rRmi7v9zIOMEmwfkN0Ea3HgNJ/7W8gL
mnv47HtwEuuXpyhyQ5xpaJcNWfSaUvSHBr33Ni8rDL2iJu22zEeZXlOU1t4h7wCA
VGd3GBk4n5PILABwILDWKP16bvB3I491QK6m7LehawJNMGDC4Yk379Havcl6BzSX
Rs6NOFcAGyM0VS97HYj3ju7fecdvFwmd86CA92CoK6JbafsQ6o/sOZOhxnrg6nLu
bS26TPwLoqNHrAzg2vTLosbiG3ezge7VuL2GBbQcZm4SsotNLbfpZIhn3JV9/Ivg
rkiUNOEExd52+VImeAJ15Pl6lOb2uAdXwZNsPXfBbanfc5qoggJJlU/xCnQyrPNW
xCi2y3+SQjjlEqdoQbmEEHrU4zzOSj505qDz/Md18W/aih5n1VdzI7Z1YeMAzlpe
X7cGHrQD83AsAtWWv5af5qkHCRYn3fQDGf4svJprsbYxz7e8oAoyjXpKPVbfZcfk
hqlR0T7yN91upFwHhq8q3ZY/CLsZbLecT28M3G7NgE+LtrF0bKcPrDfOCeWyyVBN
wtxPB0P62Nr4jHs5GVO5D1qyCiDMLsS3mwSz3IZn2xf0pit7+O2uu5yIUeGhUN8k
1Y4RwZyGf1e5y7Ulhb5OAbeDYtUTnvgWOqwj5grmACQhhWmKiXX6qUHHn54xyC9P
Yn5f0jdGGVRKINgA+KsipDs3OjMY0D9Dyx3ao646wX+1ypNkUq+hZhykuKlTgqi6
29LdVwXTbcYjc25hbIj9TrXZS7swmwtUhpLwtFyjqrCSMjHW6f6n27Vq8cvL788L
7SbWmpZlxaZA2lj3yjjYnY2bSQBfmA45fF7tBZfHq/ObWFbpvCOU5LMwKjffoPpC
-----END RSA PRIVATE KEY-----

View File

@ -323,6 +323,71 @@ type NFPMScripts struct {
PostRemove string `yaml:"postremove,omitempty"`
}
type NFPMRPMSignature struct {
// PGP secret key, can be ASCII-armored
KeyFile string `yaml:"key_file,omitempty"`
KeyPassphrase string `yaml:"-"` // populated from environment variable
}
// NFPMRPM is custom configs that are only available on RPM packages.
type NFPMRPM struct {
Group string `yaml:"group,omitempty"`
Compression string `yaml:"compression,omitempty"`
// https://www.cl.cam.ac.uk/~jw35/docs/rpm_config.html
ConfigNoReplaceFiles map[string]string `yaml:"config_noreplace_files,omitempty"`
Signature NFPMRPMSignature `yaml:"signature,omitempty"`
}
// NFPMDebScripts is scripts only available on deb packages.
type NFPMDebScripts struct {
Rules string `yaml:"rules,omitempty"`
Templates string `yaml:"templates,omitempty"`
}
// NFPMDebTriggers contains triggers only available for deb packages.
// https://wiki.debian.org/DpkgTriggers
// https://man7.org/linux/man-pages/man5/deb-triggers.5.html
type NFPMDebTriggers struct {
Interest []string `yaml:"interest,omitempty"`
InterestAwait []string `yaml:"interest_await,omitempty"`
InterestNoAwait []string `yaml:"interest_noawait,omitempty"`
Activate []string `yaml:"activate,omitempty"`
ActivateAwait []string `yaml:"activate_await,omitempty"`
ActivateNoAwait []string `yaml:"activate_noawait,omitempty"`
}
// NFPMDebSignature contains config for signing deb packages created by nfpm.
type NFPMDebSignature struct {
// PGP secret key, can be ASCII-armored
KeyFile string `yaml:"key_file,omitempty"`
KeyPassphrase string `yaml:"-"` // populated from environment variable
// origin, maint or archive (defaults to origin)
Type string `yaml:"type,omitempty"`
}
// NFPMDeb is custom configs that are only available on deb packages.
type NFPMDeb struct {
Scripts NFPMDebScripts `yaml:"scripts,omitempty"`
Triggers NFPMDebTriggers `yaml:"triggers,omitempty"`
Breaks []string `yaml:"breaks,omitempty"`
VersionMetadata string `yaml:"metadata,omitempty"` // Deprecated: Moved to Info
Signature NFPMDebSignature `yaml:"signature,omitempty"`
}
// NFPMAPKSignature contains config for signing apk packages created by nfpm.
type NFPMAPKSignature struct {
// RSA private key in PEM format
KeyFile string `yaml:"key_file,omitempty"`
KeyPassphrase string `yaml:"-"` // populated from environment variable
// defaults to <maintainer email>.rsa.pub
KeyName string `yaml:"key_name,omitempty"`
}
// NFPMAPK is custom config only available on apk packages.
type NFPMAPK struct {
Signature NFPMAPKSignature `yaml:"signature,omitempty"`
}
// NFPMOverridables is used to specify per package format settings.
type NFPMOverridables struct {
FileNameTemplate string `yaml:"file_name_template,omitempty"`
@ -338,6 +403,9 @@ type NFPMOverridables struct {
Files map[string]string `yaml:",omitempty"`
ConfigFiles map[string]string `yaml:"config_files,omitempty"`
Scripts NFPMScripts `yaml:"scripts,omitempty"`
RPM NFPMRPM `yaml:"rpm,omitempty"`
Deb NFPMDeb `yaml:"deb,omitempty"`
APK NFPMAPK `yaml:"apk,omitempty"`
}
// Sign config.

View File

@ -156,6 +156,83 @@ nfpms:
"tmp/app_generated.conf": "/etc/app-rpm.conf"
scripts:
preinstall: "scripts/preinstall-rpm.sh"
# Custon configuration applied only to the RPM packager.
rpm:
# The package group. This option is deprecated by most distros
# but required by old distros like CentOS 5 / EL 5 and earlier.
group: Unspecified
# Compression algorithm.
compression: lzma
# These config files will not be replaced by new versions if they were
# changed by the user. Corresponds to %config(noreplace).
config_noreplace_files:
path/to/local/bar.con: /etc/bar.conf
# The package is signed if a key_file is set
signature:
# PGP secret key (can also be ASCII-armored). The passphrase is taken
# from the environment variable $NFPM_ID_RPM_PASSPHRASE with a fallback
# to $NFPM_ID_PASSPHRASE, where ID is the id of the current nfpm config.
# The id will be transformed to uppercase.
# E.g. If your nfpm id is 'default' then the rpm-specific passphrase
# should be set as $NFPM_DEFAULT_RPM_PASSPHRASE
key_file: key.gpg
# Custom configuration applied only to the Deb packager.
deb:
# Custom deb rules script.
scripts:
rules: foo.sh
# Deb templates file, when using debconf.
templates: templates
# Custom deb triggers
triggers:
# register interrest on a trigger activated by another package
# (also available: interest_await, interest_noawait)
interest:
- some-trigger-name
# activate a trigger for another package
# (also available: activate_await, activate_noawait)
activate:
- another-trigger-name
# Packages which would break if this package would be installed.
# The installation of this package is blocked if `some-package`
# is already installed.
breaks:
- some-package
# The package is signed if a key_file is set
signature:
# PGP secret key (can also be ASCII-armored). The passphrase is taken
# from the environment variable $NFPM_ID_DEB_PASSPHRASE with a fallback
# to $NFPM_ID_PASSPHRASE, where ID is the id of the current nfpm config.
# The id will be transformed to uppercase.
# E.g. If your nfpm id is 'default' then the deb-specific passphrase
# should be set as $NFPM_DEFAULT_DEB_PASSPHRASE
key_file: key.gpg
# The type describes the signers role, possible values are "origin",
# "maint" and "archive". If unset, the type defaults to "origin".
type: origin
apk:
# The package is signed if a key_file is set
signature:
# RSA private key in the PEM format. The passphrase is taken
# from the environment variable $NFPM_ID_APK_PASSPHRASE with a fallback
# to $NFPM_ID_PASSPHRASE, where ID is the id of the current nfpm config.
# The id will be transformed to uppercase.
# E.g. If your nfpm id is 'default' then the deb-specific passphrase
# should be set as $NFPM_DEFAULT_APK_PASSPHRASE
key_file: key.gpg
# The name of the signing key. When verifying a package, the signature
# is matched to the public key store in /etc/apk/keys/<key_name>.rsa.pub.
# If unset, it defaults to the maintainer email address.
key_name: origin
```
!!! tip