diff --git a/internal/restic/runner.go b/internal/restic/runner.go index 41b738a..7e40e7f 100644 --- a/internal/restic/runner.go +++ b/internal/restic/runner.go @@ -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