config: unify Get{Source,Store,Destination} behaviour - #2278
Conversation
we're moving to a world where we use the config more often, so make sure the object is always set at configuration creation time.
There was a problem hiding this comment.
Pull request overview
This PR refactors configuration lookup to unify GetSource, GetDestination, and GetRepository behavior: each now resolves @alias tokens internally (including root overrides), validates that location is present, and returns (map[string]string, error) instead of a boolean flag. It also centralizes passphrase command resolution into config resolution and updates subcommands/tests to rely on AppContext providing a non-nil config by default.
Changes:
- Reworked
config.Configgetters to share a common resolution path (get+resolve) and introduced sentinel errors (ErrNotFound,ErrNoLocation). - Updated subcommands (
backup,ptar,restore,config) to use the new error-returning getters and to stop duplicating@aliasresolution logic. - Simplified tests by removing manual
config.NewConfig()setup whereAppContext.NewAppContext()now initializesctx.Config.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| subcommands/sync/sync_test.go | Removes manual config initialization now handled by NewAppContext(). |
| subcommands/sync/sync_coverage3_test.go | Same: relies on default-initialized ctx.Config. |
| subcommands/restore/restore.go | Switches exporter config resolution to ctx.Config.GetDestination(...) with error handling. |
| subcommands/ptar/ptar.go | Switches importer config resolution to ctx.Config.GetSource(...) with error handling. |
| subcommands/ptar/ptar_coverage2_test.go | Removes manual config init; keeps source setup via ctx.Config.Sources[...]. |
| subcommands/ptar/ptar_coverage_test.go | Updates tests to new error messages / behavior and removes nil-config workaround. |
| subcommands/ptar/ptar_cov80_test.go | Removes manual config init; continues configuring peer repositories through ctx.Config. |
| subcommands/config/config.go | Updates config validation paths to use error-returning getters. |
| subcommands/backup/backup.go | Replaces local @source resolution/merging logic with GetSource + map merge. |
| subcommands/backup/backup_faults_test.go | Updates expectations to new standardized error message. |
| subcommands/backup/backup_coverage2_test.go | Removes manual config init and updates error message assertions. |
| subcommands/backup/backup_cov80_test.go | Removes manual config init in option-inheritance coverage test. |
| config/resolve.go | Adds shared config entry resolution, including raw: stripping and passphrase_cmd execution. |
| config/old.go | Adjusts old-config structures to the new map[string]map[string]string representation. |
| config/config.go | Introduces unified getter behavior (get) and sentinel errors; updates getter signatures. |
| config/config_test.go | Updates tests for new types/signatures and adds coverage for ErrNoLocation. |
| config/config_extra_test.go | Updates destination/source/repo tests to new getter semantics and @ usage. |
| appcontext/appcontext.go | Ensures NewAppContext() always initializes Config to a non-nil config object. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
1f089de to
ee47366
Compare
ee47366 to
0999ae9
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 18 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (1)
config/config.go:75
- GetDestination forwards the kind string as "source", which will produce incorrect error messages (e.g., "source configuration not found") for destination lookups.
return c.get(name, "destination", c.Destinations)
0999ae9 to
989ef93
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 18 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (4)
subcommands/config/config.go:142
normalizeNamestrips the "@" prefix, butConfig.GetSourcenow treats non-"@" inputs as literal locations. As a result,plakar config source check <name>will try to create an importer with location="" instead of looking up the configured source entry.
imp, err := importer.NewImporter(ctx.GetInner(), ctx.ImporterOpts(), cfg)
subcommands/config/config.go:153
normalizeNamestrips the "@" prefix, butConfig.GetDestinationnow treats non-"@" inputs as literal locations. This makesplakar config destination check <name>attempt to use location="" instead of resolving the configured destination.
exp, err := exporter.NewExporter(ctx.GetInner(), ctx.ExporterOpts(), cfg)
subcommands/config/config.go:276
- Same issue as the
checksubcommand:pingpasses a bare name intoGetSource, which now means "literal location" rather than alias lookup. This will breakplakar config source ping <name>.
imp, err := importer.NewImporter(ctx.GetInner(), ctx.ImporterOpts(), cfg)
subcommands/config/config.go:291
- Same issue as the
checksubcommand:pingpasses a bare name intoGetDestination, which now means "literal location" rather than alias lookup. This will breakplakar config destination ping <name>.
exp, err := exporter.NewExporter(ctx.GetInner(), ctx.ExporterOpts(), cfg)
989ef93 to
b643222
Compare
* let all of them to resolve internally the "at aliases" syntax * all of them return a map or an error, not a boolean * they verify internally that location is set The visible changes are: * the message for an alias not found or a location missing is slightly different * passphrase_cmd gets replaced everywhere: I've checked all the currently packaged importers and exporters, and no-one is using it, I'm fine with this trade-off for now, in the near future it'll get replaced by a better mechanism anyway.
b643222 to
cf16611
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 20 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (5)
subcommands/config/config.go:128
Config.GetSourceonly resolves configured sources when the argument starts with "@". Calling it with a plain config name (e.g. "mysrc") returns{location: "mysrc"}and ignores the configured section, soplakar source check <name>will validate the wrong thing (and likely fail) even when the config exists.
name := args[0]
if !hasFunc(name) {
return fmt.Errorf("%s %q does not exist", cmd, name)
}
subcommands/config/config.go:153
- Same issue as the source branch above:
Config.GetDestinationtreats a non-"@" argument as a literal location, soplakar destination check <name>won’t actually use the configured destination section.
case "destination":
cfg, err := ctx.Config.GetDestination(name)
if err != nil {
return err
}
exp, err := exporter.NewExporter(ctx.GetInner(), ctx.ExporterOpts(), cfg)
subcommands/config/config.go:258
Config.GetSourceonly resolves configured sources when the argument starts with "@". In the ping subcommand, calling it with the plain config name makesplakar source ping <name>ping{location: "<name>"}instead of the configured source.
name := args[0]
if !hasFunc(name) {
return fmt.Errorf("%s %q does not exist", cmd, name)
}
subcommands/config/config.go:291
- Same issue as the source ping branch:
Config.GetDestinationtreats a non-"@" argument as a literal location, soplakar destination ping <name>won’t ping the configured destination.
case "destination":
cfg, err := ctx.Config.GetDestination(name)
if err != nil {
return err
}
exp, err := exporter.NewExporter(ctx.GetInner(), ctx.ExporterOpts(), cfg)
config/resolve.go:66
resolve()iterates a map, so the relative handling ofpassphrasevspassphrase_cmdis currently nondeterministic when both keys are present: whichever key happens to be processed last wins. That can lead to flaky behavior depending on map iteration order. Make precedence explicit (e.g., prefer an explicitly configuredpassphrase, and only fall back topassphrase_cmdwhen nopassphraseis set).
for k, v := range conf {
if k == "passphrase_cmd" {
v, err := getPassphraseFromCommand(v)
if err != nil {
return nil, err
| return "", fmt.Errorf("passphrase_cmd returned too many lines") | ||
| } | ||
|
|
||
| return pass, nil |
There was a problem hiding this comment.
go home you're drunk. bufio.ScanLines, the default split function, says:
// ScanLines is a split function for a [Scanner] that returns each line of
// text, stripped of any trailing end-of-line marker. The returned line may
// be empty. The end-of-line marker is one optional carriage return followed
// by one mandatory newline. In regular expression notation, it is\r?\n.
// The last non-empty line of input will be returned even if it has no
// newline.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 20 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
subcommands/config/config.go:142
ctx.Config.GetSourcenow interprets non-"@" arguments as a literal location, socheck source <name>will try to build an importer withlocation=<name>instead of looking up the configured source section. Prefix the section name with "@" when callingGetSourcehere.
case "source":
cfg, err := ctx.Config.GetSource(name)
if err != nil {
return err
}
imp, err := importer.NewImporter(ctx.GetInner(), ctx.ImporterOpts(), cfg)
subcommands/config/config.go:154
ctx.Config.GetDestinationnow treats non-"@" arguments as a literal location;check destination <name>should look up the destination section, so pass it as an alias ("@") here.
case "destination":
cfg, err := ctx.Config.GetDestination(name)
if err != nil {
return err
}
exp, err := exporter.NewExporter(ctx.GetInner(), ctx.ExporterOpts(), cfg)
if err != nil {
subcommands/config/config.go:277
- Same issue as
check:ping source <name>passes a bare section name intoGetSource, which is now treated as a literal location. Prefix with "@" so the configured source section is resolved.
case "source":
cfg, err := ctx.Config.GetSource(name)
if err != nil {
return err
}
imp, err := importer.NewImporter(ctx.GetInner(), ctx.ImporterOpts(), cfg)
if err != nil {
subcommands/config/config.go:292
- Same issue as
check:ping destination <name>needs to resolve the named destination section, butGetDestinationtreats bare strings as literal locations. Prefix with "@" here.
case "destination":
cfg, err := ctx.Config.GetDestination(name)
if err != nil {
return err
}
exp, err := exporter.NewExporter(ctx.GetInner(), ctx.ExporterOpts(), cfg)
if err != nil {
The visible changes are:
slightly different
currently packaged importers and exporters, and no-one is using it,
I'm fine with this trade-off for now, in the near future it'll get
replaced by a better mechanism anyway.
(plus two minor improvements while here, this is the last step before overloading the config resolution.)