diff --git a/AUTHORS b/AUTHORS index 2f1b07aac5..67aa1eb224 100644 --- a/AUTHORS +++ b/AUTHORS @@ -79,3 +79,4 @@ List of contributors, in chronological order: * Ato Araki (https://github.com/atotto) * Roman Lebedev (https://github.com/LebedevRI) * Brian Witt (https://github.com/bwitt) +* Ales Bregar (https://github.com/abregar) diff --git a/api/publish.go b/api/publish.go index 3b970f4248..54dafce548 100644 --- a/api/publish.go +++ b/api/publish.go @@ -16,8 +16,8 @@ import ( type signingParams struct { // Don't sign published repository Skip bool ` json:"Skip" example:"false"` - // GPG key ID to use when signing the release, if not specified default key is used - GpgKey string ` json:"GpgKey" example:"A0546A43624A8331"` + // GPG key ID(s) to use when signing the release, separated by comma, and if not specified, default configured key(s) are used + GpgKey string ` json:"GpgKey" example:"KEY_ID_a, KEY_ID_b"` // GPG keyring to use (instead of default) Keyring string ` json:"Keyring" example:"trustedkeys.gpg"` // GPG secret keyring to use (instead of default) Note: depreciated with gpg2 @@ -41,7 +41,21 @@ func getSigner(options *signingParams) (pgp.Signer, error) { } signer := context.GetSigner() - signer.SetKey(options.GpgKey) + + var multiGpgKeys []string + // REST params have priority over config + if options.GpgKey != "" { + for _, p := range strings.Split(options.GpgKey, ",") { + if t := strings.TrimSpace(p); t != "" { + multiGpgKeys = append(multiGpgKeys, t) + } + } + } else if len(context.Config().GpgKeys) > 0 { + multiGpgKeys = context.Config().GpgKeys + } + for _, gpgKey := range multiGpgKeys { + signer.SetKey(gpgKey) + } signer.SetKeyRing(options.Keyring, options.SecretKeyring) signer.SetPassphrase(options.Passphrase, options.PassphraseFile) diff --git a/cmd/publish.go b/cmd/publish.go index 4217ff87a6..6f4904ada4 100644 --- a/cmd/publish.go +++ b/cmd/publish.go @@ -1,6 +1,8 @@ package cmd import ( + "strings" + "github.com/aptly-dev/aptly/pgp" "github.com/smira/commander" "github.com/smira/flag" @@ -12,7 +14,20 @@ 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 + } + + 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 +41,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 a254cfaf72..a088b40ef4 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 (flag is 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 fbdf559e40..67f3c009a7 100644 --- a/cmd/publish_snapshot.go +++ b/cmd/publish_snapshot.go @@ -234,7 +234,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 (flag is 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 6ae8531e95..77e6a32e05 100644 --- a/cmd/publish_switch.go +++ b/cmd/publish_switch.go @@ -155,7 +155,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 (flag is 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 7240863dc5..384847c3a7 100644 --- a/cmd/publish_update.go +++ b/cmd/publish_update.go @@ -127,7 +127,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 (flag is 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/docs/Publish.md b/docs/Publish.md index 0077f4d9b6..40cacbfe8b 100644 --- a/docs/Publish.md +++ b/docs/Publish.md @@ -11,7 +11,26 @@ Repositories can be published to local directories, Amazon S3 buckets, Azure or GPG key is required to sign any published repository. The key pari should be generated before publishing. -Publiс part of the key should be exported from your keyring using `gpg --export --armor` and imported on the system which uses a published repository. +Public part of the key should be exported from your keyring using `gpg --export --armor` and imported on the system which uses a published repository. + +* Multiple signing keys can be defined in aptly.conf using the gpgKeys array: +``` +"gpgKeys": [ + "KEY_ID_x", + "KEY_ID_y" +] +``` + +* It is also possible to pass multiple keys via the CLI using the repeatable `--gpg-key` flag: +``` +aptly publish repo my-repo --gpg-key=KEY_ID_a --gpg-key=KEY_ID_b +``` +* When using the REST API, the `gpgKey` parameter supports a comma-separated list of key IDs: +``` +"gpgKey": "KEY_ID_a,KEY_ID_b" +``` +* If `--gpg-key` is specified on the command line, or `gpgKey` is provided via the REST API, it takes precedence over any gpgKeys configuration in aptly.conf. +* With multi-key support, aptly will sign all Release files (both clearsigned and detached signatures) with each provided key, ensuring a smooth key rotation process while maintaining compatibility for existing clients. #### Parameters diff --git a/pgp/gnupg.go b/pgp/gnupg.go index 3edf121024..bab2db21b2 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,14 @@ func (g *GpgSigner) SetBatch(batch bool) { // SetKey sets key ID to use when signing files func (g *GpgSigner) SetKey(keyRef string) { - g.keyRef = keyRef + keyRef = strings.TrimSpace(keyRef) + if keyRef != "" { + if g.keyRefs == nil { + g.keyRefs = []string{keyRef} + } else { + g.keyRefs = append(g.keyRefs, keyRef) + } + } } // SetKeyRing allows to set custom keyring and secretkeyring @@ -57,8 +64,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/system/t02_config/ConfigShowTest_gold b/system/t02_config/ConfigShowTest_gold index 5a4a2273ae..ba3b88d65b 100644 --- a/system/t02_config/ConfigShowTest_gold +++ b/system/t02_config/ConfigShowTest_gold @@ -29,6 +29,7 @@ "gpgProvider": "gpg", "gpgDisableSign": false, "gpgDisableVerify": false, + "gpgKeys": [], "skipContentsPublishing": false, "skipBz2Publishing": false, "FileSystemPublishEndpoints": {}, diff --git a/system/t02_config/ConfigShowYAMLTest_gold b/system/t02_config/ConfigShowYAMLTest_gold index 615982b0db..02efe23d30 100644 --- a/system/t02_config/ConfigShowYAMLTest_gold +++ b/system/t02_config/ConfigShowYAMLTest_gold @@ -27,6 +27,7 @@ download_sourcepackages: false gpg_provider: gpg gpg_disable_sign: false gpg_disable_verify: false +gpg_keys: [] skip_contents_publishing: false skip_bz2_publishing: false filesystem_publish_endpoints: {} diff --git a/utils/config.go b/utils/config.go index 4cfac039bd..cb72105f53 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 da3cd7c6b1..294f41675c 100644 --- a/utils/config_test.go +++ b/utils/config_test.go @@ -19,8 +19,8 @@ func (s *ConfigSuite) TestLoadConfig(c *C) { _, _ = f.WriteString(configFile) _ = f.Close() - // start with empty config - s.config = ConfigStructure{} + // start with empty config + s.config = ConfigStructure{} err := LoadConfig(configname, &s.config) c.Assert(err, IsNil) @@ -32,8 +32,8 @@ func (s *ConfigSuite) TestLoadConfig(c *C) { func (s *ConfigSuite) TestSaveConfig(c *C) { configname := filepath.Join(c.MkDir(), "aptly.json2") - // start with empty config - s.config = ConfigStructure{} + // start with empty config + s.config = ConfigStructure{} s.config.RootDir = "/tmp/aptly" s.config.DownloadConcurrency = 5 @@ -71,93 +71,94 @@ func (s *ConfigSuite) TestSaveConfig(c *C) { _, _ = f.Read(buf) c.Check(string(buf), Equals, ""+ - "{\n"+ - " \"rootDir\": \"/tmp/aptly\",\n"+ - " \"logLevel\": \"info\",\n"+ - " \"logFormat\": \"json\",\n"+ - " \"databaseOpenAttempts\": 5,\n"+ - " \"architectures\": null,\n"+ - " \"skipLegacyPool\": false,\n"+ - " \"dependencyFollowSuggests\": false,\n"+ - " \"dependencyFollowRecommends\": false,\n"+ - " \"dependencyFollowAllVariants\": false,\n"+ - " \"dependencyFollowSource\": false,\n"+ - " \"dependencyVerboseResolve\": false,\n"+ - " \"ppaDistributorID\": \"\",\n"+ - " \"ppaCodename\": \"\",\n"+ - " \"serveInAPIMode\": false,\n"+ - " \"enableMetricsEndpoint\": false,\n"+ - " \"enableSwaggerEndpoint\": false,\n"+ - " \"AsyncAPI\": false,\n"+ - " \"databaseBackend\": {\n"+ - " \"type\": \"\",\n"+ - " \"dbPath\": \"\",\n"+ - " \"url\": \"\"\n"+ - " },\n"+ - " \"downloader\": \"\",\n"+ - " \"downloadConcurrency\": 5,\n"+ - " \"downloadSpeedLimit\": 0,\n"+ - " \"downloadRetries\": 0,\n"+ - " \"downloadSourcePackages\": false,\n"+ - " \"gpgProvider\": \"gpg\",\n"+ - " \"gpgDisableSign\": false,\n"+ - " \"gpgDisableVerify\": false,\n"+ - " \"skipContentsPublishing\": false,\n"+ - " \"skipBz2Publishing\": false,\n"+ - " \"FileSystemPublishEndpoints\": {\n"+ - " \"test\": {\n"+ - " \"rootDir\": \"/opt/aptly-publish\",\n"+ - " \"linkMethod\": \"\",\n"+ - " \"verifyMethod\": \"\"\n"+ - " }\n"+ - " },\n"+ - " \"S3PublishEndpoints\": {\n"+ - " \"test\": {\n"+ - " \"region\": \"us-east-1\",\n"+ - " \"bucket\": \"repo\",\n"+ - " \"prefix\": \"\",\n"+ - " \"acl\": \"\",\n"+ - " \"awsAccessKeyID\": \"\",\n"+ - " \"awsSecretAccessKey\": \"\",\n"+ - " \"awsSessionToken\": \"\",\n"+ - " \"endpoint\": \"\",\n"+ - " \"storageClass\": \"\",\n"+ - " \"encryptionMethod\": \"\",\n"+ - " \"plusWorkaround\": false,\n"+ - " \"disableMultiDel\": false,\n"+ - " \"forceSigV2\": false,\n"+ - " \"forceVirtualHostedStyle\": false,\n"+ - " \"debug\": false\n"+ - " }\n"+ - " },\n"+ - " \"SwiftPublishEndpoints\": {\n"+ - " \"test\": {\n"+ - " \"container\": \"repo\",\n"+ - " \"prefix\": \"\",\n"+ - " \"osname\": \"\",\n"+ - " \"password\": \"\",\n"+ - " \"tenant\": \"\",\n"+ - " \"tenantid\": \"\",\n"+ - " \"domain\": \"\",\n"+ - " \"domainid\": \"\",\n"+ - " \"tenantdomain\": \"\",\n"+ - " \"tenantdomainid\": \"\",\n"+ - " \"authurl\": \"\"\n"+ - " }\n"+ - " },\n"+ - " \"AzurePublishEndpoints\": {\n"+ - " \"test\": {\n"+ - " \"container\": \"repo\",\n"+ - " \"prefix\": \"\",\n"+ - " \"accountName\": \"\",\n"+ - " \"accountKey\": \"\",\n"+ - " \"endpoint\": \"\"\n"+ - " }\n"+ - " },\n"+ - " \"packagePoolStorage\": {\n"+ - " \"type\": \"local\",\n"+ - " \"path\": \"/tmp/aptly-pool\"\n"+ - " }\n"+ + "{\n" + + " \"rootDir\": \"/tmp/aptly\",\n" + + " \"logLevel\": \"info\",\n" + + " \"logFormat\": \"json\",\n" + + " \"databaseOpenAttempts\": 5,\n" + + " \"architectures\": null,\n" + + " \"skipLegacyPool\": false,\n" + + " \"dependencyFollowSuggests\": false,\n" + + " \"dependencyFollowRecommends\": false,\n" + + " \"dependencyFollowAllVariants\": false,\n" + + " \"dependencyFollowSource\": false,\n" + + " \"dependencyVerboseResolve\": false,\n" + + " \"ppaDistributorID\": \"\",\n" + + " \"ppaCodename\": \"\",\n" + + " \"serveInAPIMode\": false,\n" + + " \"enableMetricsEndpoint\": false,\n" + + " \"enableSwaggerEndpoint\": false,\n" + + " \"AsyncAPI\": false,\n" + + " \"databaseBackend\": {\n" + + " \"type\": \"\",\n" + + " \"dbPath\": \"\",\n" + + " \"url\": \"\"\n" + + " },\n" + + " \"downloader\": \"\",\n" + + " \"downloadConcurrency\": 5,\n" + + " \"downloadSpeedLimit\": 0,\n" + + " \"downloadRetries\": 0,\n" + + " \"downloadSourcePackages\": false,\n" + + " \"gpgProvider\": \"gpg\",\n" + + " \"gpgDisableSign\": false,\n" + + " \"gpgDisableVerify\": false,\n" + + " \"gpgKeys\": null,\n" + + " \"skipContentsPublishing\": false,\n" + + " \"skipBz2Publishing\": false,\n" + + " \"FileSystemPublishEndpoints\": {\n" + + " \"test\": {\n" + + " \"rootDir\": \"/opt/aptly-publish\",\n" + + " \"linkMethod\": \"\",\n" + + " \"verifyMethod\": \"\"\n" + + " }\n" + + " },\n" + + " \"S3PublishEndpoints\": {\n" + + " \"test\": {\n" + + " \"region\": \"us-east-1\",\n" + + " \"bucket\": \"repo\",\n" + + " \"prefix\": \"\",\n" + + " \"acl\": \"\",\n" + + " \"awsAccessKeyID\": \"\",\n" + + " \"awsSecretAccessKey\": \"\",\n" + + " \"awsSessionToken\": \"\",\n" + + " \"endpoint\": \"\",\n" + + " \"storageClass\": \"\",\n" + + " \"encryptionMethod\": \"\",\n" + + " \"plusWorkaround\": false,\n" + + " \"disableMultiDel\": false,\n" + + " \"forceSigV2\": false,\n" + + " \"forceVirtualHostedStyle\": false,\n" + + " \"debug\": false\n" + + " }\n" + + " },\n" + + " \"SwiftPublishEndpoints\": {\n" + + " \"test\": {\n" + + " \"container\": \"repo\",\n" + + " \"prefix\": \"\",\n" + + " \"osname\": \"\",\n" + + " \"password\": \"\",\n" + + " \"tenant\": \"\",\n" + + " \"tenantid\": \"\",\n" + + " \"domain\": \"\",\n" + + " \"domainid\": \"\",\n" + + " \"tenantdomain\": \"\",\n" + + " \"tenantdomainid\": \"\",\n" + + " \"authurl\": \"\"\n" + + " }\n" + + " },\n" + + " \"AzurePublishEndpoints\": {\n" + + " \"test\": {\n" + + " \"container\": \"repo\",\n" + + " \"prefix\": \"\",\n" + + " \"accountName\": \"\",\n" + + " \"accountKey\": \"\",\n" + + " \"endpoint\": \"\"\n" + + " }\n" + + " },\n" + + " \"packagePoolStorage\": {\n" + + " \"type\": \"local\",\n" + + " \"path\": \"/tmp/aptly-pool\"\n" + + " }\n" + "}") } @@ -167,8 +168,8 @@ func (s *ConfigSuite) TestLoadYAMLConfig(c *C) { _, _ = f.WriteString(configFileYAML) _ = f.Close() - // start with empty config - s.config = ConfigStructure{} + // start with empty config + s.config = ConfigStructure{} err := LoadConfig(configname, &s.config) c.Assert(err, IsNil) @@ -183,8 +184,8 @@ func (s *ConfigSuite) TestLoadYAMLErrorConfig(c *C) { _, _ = f.WriteString(configFileYAMLError) _ = f.Close() - // start with empty config - s.config = ConfigStructure{} + // start with empty config + s.config = ConfigStructure{} err := LoadConfig(configname, &s.config) c.Assert(err.Error(), Equals, "invalid yaml (unknown pool storage type: invalid) or json (invalid character 'p' looking for beginning of value)") @@ -196,13 +197,13 @@ func (s *ConfigSuite) TestSaveYAMLConfig(c *C) { _, _ = f.WriteString(configFileYAML) _ = f.Close() - // start with empty config - s.config = ConfigStructure{} + // start with empty config + s.config = ConfigStructure{} err := LoadConfig(configname, &s.config) c.Assert(err, IsNil) - err = SaveConfigYAML(configname, &s.config) + err = SaveConfigYAML(configname, &s.config) c.Assert(err, IsNil) f, _ = os.Open(configname) @@ -218,17 +219,17 @@ func (s *ConfigSuite) TestSaveYAMLConfig(c *C) { } func (s *ConfigSuite) TestSaveYAML2Config(c *C) { - // start with empty config - s.config = ConfigStructure{} + // start with empty config + s.config = ConfigStructure{} s.config.PackagePoolStorage.Local = &LocalPoolStorage{"/tmp/aptly-pool"} - s.config.PackagePoolStorage.Azure = nil + s.config.PackagePoolStorage.Azure = nil configname := filepath.Join(c.MkDir(), "aptly.yaml4") - err := SaveConfigYAML(configname, &s.config) + err := SaveConfigYAML(configname, &s.config) c.Assert(err, IsNil) - f, _ := os.Open(configname) + f, _ := os.Open(configname) defer func() { _ = f.Close() }() @@ -237,44 +238,45 @@ func (s *ConfigSuite) TestSaveYAML2Config(c *C) { buf := make([]byte, st.Size()) _, _ = f.Read(buf) - c.Check(string(buf), Equals, ""+ - "root_dir: \"\"\n"+ - "log_level: \"\"\n"+ - "log_format: \"\"\n"+ - "database_open_attempts: 0\n"+ - "architectures: []\n"+ - "skip_legacy_pool: false\n"+ - "dep_follow_suggests: false\n"+ - "dep_follow_recommends: false\n"+ - "dep_follow_all_variants: false\n"+ - "dep_follow_source: false\n"+ - "dep_verboseresolve: false\n"+ - "ppa_distributor_id: \"\"\n"+ - "ppa_codename: \"\"\n"+ - "serve_in_api_mode: false\n"+ - "enable_metrics_endpoint: false\n"+ - "enable_swagger_endpoint: false\n"+ - "async_api: false\n"+ - "database_backend:\n"+ - " type: \"\"\n"+ - " db_path: \"\"\n"+ - " url: \"\"\n"+ - "downloader: \"\"\n"+ - "download_concurrency: 0\n"+ - "download_limit: 0\n"+ - "download_retries: 0\n"+ - "download_sourcepackages: false\n"+ - "gpg_provider: \"\"\n"+ - "gpg_disable_sign: false\n"+ - "gpg_disable_verify: false\n"+ - "skip_contents_publishing: false\n"+ - "skip_bz2_publishing: false\n"+ - "filesystem_publish_endpoints: {}\n"+ - "s3_publish_endpoints: {}\n"+ - "swift_publish_endpoints: {}\n"+ - "azure_publish_endpoints: {}\n"+ - "packagepool_storage:\n"+ - " type: local\n"+ + c.Check(string(buf), Equals, "" + + "root_dir: \"\"\n" + + "log_level: \"\"\n" + + "log_format: \"\"\n" + + "database_open_attempts: 0\n" + + "architectures: []\n" + + "skip_legacy_pool: false\n" + + "dep_follow_suggests: false\n" + + "dep_follow_recommends: false\n" + + "dep_follow_all_variants: false\n" + + "dep_follow_source: false\n" + + "dep_verboseresolve: false\n" + + "ppa_distributor_id: \"\"\n" + + "ppa_codename: \"\"\n" + + "serve_in_api_mode: false\n" + + "enable_metrics_endpoint: false\n" + + "enable_swagger_endpoint: false\n" + + "async_api: false\n" + + "database_backend:\n" + + " type: \"\"\n" + + " db_path: \"\"\n" + + " url: \"\"\n" + + "downloader: \"\"\n" + + "download_concurrency: 0\n" + + "download_limit: 0\n" + + "download_retries: 0\n" + + "download_sourcepackages: false\n" + + "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" + + "s3_publish_endpoints: {}\n" + + "swift_publish_endpoints: {}\n" + + "azure_publish_endpoints: {}\n" + + "packagepool_storage:\n" + + " type: local\n" + " path: /tmp/aptly-pool\n") } @@ -283,8 +285,8 @@ func (s *ConfigSuite) TestLoadEmptyConfig(c *C) { f, _ := os.Create(configname) _ = f.Close() - // start with empty config - s.config = ConfigStructure{} + // start with empty config + s.config = ConfigStructure{} err := LoadConfig(configname, &s.config) c.Assert(err.Error(), Equals, "invalid yaml (EOF) or json (EOF)") @@ -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: