restic: treat 'config file already exists' on init as soft success

Re-running restic init on a repo that's already initialised exits
non-zero with "Fatal: ... config file already exists". Semantically
that's a no-op, not a failure — the repo IS initialised, the
caller's intent is satisfied. Sniff stderr for the magic string
and swallow the exit code in that case, emitting an event line
so the operator-facing log says what happened.

Caught while smoke-testing P2-04.5: I'd init'd the repo manually
during a debug session, then the operator clicking the UI's
Init-repo button would hit this and the host's repo_initialised_at
would never flip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-02 13:22:01 +01:00
parent c5777122db
commit f692ad592c
+23 -2
View File
@@ -173,15 +173,36 @@ func (e Env) RunInit(ctx context.Context, handle LineHandler) error {
return fmt.Errorf("restic init: start: %w", err)
}
// Sniff for "config file already exists" on stderr; if we see it
// we'll treat the non-zero exit as a soft success — running init
// against an already-initialised repo is a no-op semantically,
// not a failure. Wraps the caller's handle so the line still
// gets streamed verbatim to the operator-facing log.
alreadyInited := false
sniff := func(stream, line string, ev any) {
if stream == "stderr" && strings.Contains(line, "config file already exists") {
alreadyInited = true
}
if handle != nil {
handle(stream, line, ev)
}
}
done := make(chan error, 2)
go func() { done <- pumpPlain(stdout, "stdout", handle) }()
go func() { done <- pumpPlain(stderr, "stderr", handle) }()
go func() { done <- pumpPlain(stdout, "stdout", sniff) }()
go func() { done <- pumpPlain(stderr, "stderr", sniff) }()
for i := 0; i < 2; i++ {
if err := <-done; err != nil && handle != nil {
handle("event", fmt.Sprintf("pump error: %v", err), nil)
}
}
if werr := cmd.Wait(); werr != nil {
if alreadyInited {
if handle != nil {
handle("event", "repo already initialised — treating as success", nil)
}
return nil
}
return fmt.Errorf("restic init: %w", werr)
}
return nil