diff --git a/cmd/publish.go b/cmd/publish.go index 4217ff87..70b65e34 100644 --- a/cmd/publish.go +++ b/cmd/publish.go @@ -1,6 +1,9 @@ package cmd import ( + "fmt" + "strings" + "github.com/aptly-dev/aptly/pgp" "github.com/smira/commander" "github.com/smira/flag" @@ -12,7 +15,23 @@ func getSigner(flags *flag.FlagSet) (pgp.Signer, error) { } signer := context.GetSigner() - signer.SetKey(flags.Lookup("gpg-key").Value.String()) + + var gpgKeys []string + + // CLI args have priority over config + cliKeys := flags.Lookup("gpg-key").Value.Get().([]string) + if len(cliKeys) > 0 { + gpgKeys = cliKeys + } else if len(context.Config().GpgKeys) > 0 { + gpgKeys = context.Config().GpgKeys + } + + if len(gpgKeys) > 0 { + fmt.Printf("Signing with following gpg keys %s\n", strings.Join(gpgKeys, ", ")) + } + for _, gpgKey := range gpgKeys { + signer.SetKey(gpgKey) + } signer.SetKeyRing(flags.Lookup("keyring").Value.String(), flags.Lookup("secret-keyring").Value.String()) signer.SetPassphrase(flags.Lookup("passphrase").Value.String(), flags.Lookup("passphrase-file").Value.String()) signer.SetBatch(flags.Lookup("batch").Value.Get().(bool)) @@ -26,6 +45,23 @@ func getSigner(flags *flag.FlagSet) (pgp.Signer, error) { } +type gpgKeyFlag struct { + gpgKeys []string +} + +func (k *gpgKeyFlag) Set(value string) error { + k.gpgKeys = append(k.gpgKeys, value) + return nil +} + +func (k *gpgKeyFlag) Get() interface{} { + return k.gpgKeys +} + +func (k *gpgKeyFlag) String() string { + return strings.Join(k.gpgKeys, ",") +} + func makeCmdPublish() *commander.Command { return &commander.Command{ UsageLine: "publish", diff --git a/cmd/publish_repo.go b/cmd/publish_repo.go index 91971022..7e7111ba 100644 --- a/cmd/publish_repo.go +++ b/cmd/publish_repo.go @@ -34,7 +34,7 @@ Example: } cmd.Flag.String("distribution", "", "distribution name to publish") cmd.Flag.String("component", "", "component name to publish (for multi-component publishing, separate components with commas)") - cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release") + cmd.Flag.Var(&gpgKeyFlag{}, "gpg-key", "GPG key ID to use when signing the release (repeatable, can be specified multiple times)") cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)") cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)") cmd.Flag.String("passphrase", "", "GPG passphrase for the key (warning: could be insecure)") diff --git a/cmd/publish_snapshot.go b/cmd/publish_snapshot.go index 7e0d8452..6ca9d7a2 100644 --- a/cmd/publish_snapshot.go +++ b/cmd/publish_snapshot.go @@ -230,7 +230,7 @@ Example: } cmd.Flag.String("distribution", "", "distribution name to publish") cmd.Flag.String("component", "", "component name to publish (for multi-component publishing, separate components with commas)") - cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release") + cmd.Flag.Var(&gpgKeyFlag{}, "gpg-key", "GPG key ID to use when signing the release (repeatable, can be specified multiple times)") cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)") cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)") cmd.Flag.String("passphrase", "", "GPG passphrase for the key (warning: could be insecure)") diff --git a/cmd/publish_switch.go b/cmd/publish_switch.go index f39269a1..fe800369 100644 --- a/cmd/publish_switch.go +++ b/cmd/publish_switch.go @@ -151,7 +151,7 @@ This command would switch published repository (with one component) named ppa/wh `, Flag: *flag.NewFlagSet("aptly-publish-switch", flag.ExitOnError), } - cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release") + cmd.Flag.Var(&gpgKeyFlag{}, "gpg-key", "GPG key ID to use when signing the release (repeatable, can be specified multiple times)") cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)") cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)") cmd.Flag.String("passphrase", "", "GPG passphrase for the key (warning: could be insecure)") diff --git a/cmd/publish_update.go b/cmd/publish_update.go index 6ea638d4..d15c7ab4 100644 --- a/cmd/publish_update.go +++ b/cmd/publish_update.go @@ -115,7 +115,7 @@ Example: `, Flag: *flag.NewFlagSet("aptly-publish-update", flag.ExitOnError), } - cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release") + cmd.Flag.Var(&gpgKeyFlag{}, "gpg-key", "GPG key ID to use when signing the release (repeatable, can be specified multiple times)") cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)") cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)") cmd.Flag.String("passphrase", "", "GPG passphrase for the key (warning: could be insecure)") diff --git a/pgp/gnupg.go b/pgp/gnupg.go index 3edf1210..dbff8ddc 100644 --- a/pgp/gnupg.go +++ b/pgp/gnupg.go @@ -22,7 +22,7 @@ var ( type GpgSigner struct { gpg string version GPGVersion - keyRef string + keyRefs []string keyring, secretKeyring string passphrase, passphraseFile string batch bool @@ -35,7 +35,11 @@ func (g *GpgSigner) SetBatch(batch bool) { // SetKey sets key ID to use when signing files func (g *GpgSigner) SetKey(keyRef string) { - g.keyRef = keyRef + if g.keyRefs == nil { + g.keyRefs = []string{strings.TrimSpace(keyRef)} + } else { + g.keyRefs = append(g.keyRefs, strings.TrimSpace(keyRef)) + } } // SetKeyRing allows to set custom keyring and secretkeyring @@ -57,8 +61,8 @@ func (g *GpgSigner) gpgArgs() []string { args = append(args, "--secret-keyring", g.secretKeyring) } - if g.keyRef != "" { - args = append(args, "-u", g.keyRef) + for _, k := range g.keyRefs { + args = append(args, "-u", k) } if g.passphrase != "" || g.passphraseFile != "" { diff --git a/utils/config.go b/utils/config.go index 4cfac039..cb72105f 100644 --- a/utils/config.go +++ b/utils/config.go @@ -49,9 +49,10 @@ type ConfigStructure struct { // nolint: maligned DownloadSourcePackages bool `json:"downloadSourcePackages" yaml:"download_sourcepackages"` // Signing - GpgProvider string `json:"gpgProvider" yaml:"gpg_provider"` - GpgDisableSign bool `json:"gpgDisableSign" yaml:"gpg_disable_sign"` - GpgDisableVerify bool `json:"gpgDisableVerify" yaml:"gpg_disable_verify"` + GpgProvider string `json:"gpgProvider" yaml:"gpg_provider"` + GpgDisableSign bool `json:"gpgDisableSign" yaml:"gpg_disable_sign"` + GpgDisableVerify bool `json:"gpgDisableVerify" yaml:"gpg_disable_verify"` + GpgKeys []string `json:"gpgKeys" yaml:"gpg_keys"` // Publishing SkipContentsPublishing bool `json:"skipContentsPublishing" yaml:"skip_contents_publishing"` @@ -226,6 +227,7 @@ var Config = ConfigStructure{ GpgProvider: "gpg", GpgDisableSign: false, GpgDisableVerify: false, + GpgKeys: []string{}, DownloadSourcePackages: false, PackagePoolStorage: PackagePoolStorage{ Local: &LocalPoolStorage{Path: ""}, diff --git a/utils/config_test.go b/utils/config_test.go index da3cd7c6..19fd0b52 100644 --- a/utils/config_test.go +++ b/utils/config_test.go @@ -102,6 +102,7 @@ func (s *ConfigSuite) TestSaveConfig(c *C) { " \"gpgProvider\": \"gpg\",\n"+ " \"gpgDisableSign\": false,\n"+ " \"gpgDisableVerify\": false,\n"+ + " \"gpgKeys\": null,\n"+ " \"skipContentsPublishing\": false,\n"+ " \"skipBz2Publishing\": false,\n"+ " \"FileSystemPublishEndpoints\": {\n"+ @@ -267,6 +268,7 @@ func (s *ConfigSuite) TestSaveYAML2Config(c *C) { "gpg_provider: \"\"\n"+ "gpg_disable_sign: false\n"+ "gpg_disable_verify: false\n"+ + "gpg_keys: []\n"+ "skip_contents_publishing: false\n"+ "skip_bz2_publishing: false\n"+ "filesystem_publish_endpoints: {}\n"+ @@ -322,6 +324,7 @@ download_sourcepackages: true gpg_provider: gpg gpg_disable_sign: true gpg_disable_verify: true +gpg_keys: [] skip_contents_publishing: true skip_bz2_publishing: true filesystem_publish_endpoints: