From a304826b98421f7dfc6b350aa0fc1e67231c633c Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sat, 13 Sep 2025 23:44:13 +0200 Subject: [PATCH] repair snapshots: convert to termstatus --- cmd/restic/cmd_repair_snapshots.go | 33 +++++++++++-------- .../cmd_repair_snapshots_integration_test.go | 5 ++- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/cmd/restic/cmd_repair_snapshots.go b/cmd/restic/cmd_repair_snapshots.go index 95506a400..339cde606 100644 --- a/cmd/restic/cmd_repair_snapshots.go +++ b/cmd/restic/cmd_repair_snapshots.go @@ -5,6 +5,7 @@ import ( "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/restic" + "github.com/restic/restic/internal/ui/termstatus" "github.com/restic/restic/internal/walker" "github.com/spf13/cobra" @@ -49,7 +50,9 @@ Exit status is 12 if the password is incorrect. `, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - return runRepairSnapshots(cmd.Context(), globalOptions, opts, args) + term, cancel := setupTermstatus() + defer cancel() + return runRepairSnapshots(cmd.Context(), globalOptions, opts, args, term) }, } @@ -72,7 +75,9 @@ func (opts *RepairOptions) AddFlags(f *pflag.FlagSet) { initMultiSnapshotFilter(f, &opts.SnapshotFilter, true) } -func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOptions, args []string) error { +func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOptions, args []string, term *termstatus.Terminal) error { + printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term) + ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, opts.DryRun) if err != nil { return err @@ -84,7 +89,7 @@ func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOpt return err } - bar := newIndexProgress(gopts.Quiet, gopts.JSON) + bar := newIndexTerminalProgress(printer) if err := repo.LoadIndex(ctx, bar); err != nil { return err } @@ -96,7 +101,7 @@ func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOpt rewriter := walker.NewTreeRewriter(walker.RewriteOpts{ RewriteNode: func(node *restic.Node, path string) *restic.Node { if node.Type == restic.NodeTypeIrregular || node.Type == restic.NodeTypeInvalid { - Verbosef(" file %q: removed node with invalid type %q\n", path, node.Type) + printer.P(" file %q: removed node with invalid type %q", path, node.Type) return nil } if node.Type != restic.NodeTypeFile { @@ -116,9 +121,9 @@ func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOpt } } if !ok { - Verbosef(" file %q: removed missing content\n", path) + printer.P(" file %q: removed missing content", path) } else if newSize != node.Size { - Verbosef(" file %q: fixed incorrect size\n", path) + printer.P(" file %q: fixed incorrect size", path) } // no-ops if already correct node.Content = newContent @@ -127,12 +132,12 @@ func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOpt }, RewriteFailedTree: func(_ restic.ID, path string, _ error) (restic.ID, error) { if path == "/" { - Verbosef(" dir %q: not readable\n", path) + printer.P(" dir %q: not readable", path) // remove snapshots with invalid root node return restic.ID{}, nil } // If a subtree fails to load, remove it - Verbosef(" dir %q: replaced with empty directory\n", path) + printer.P(" dir %q: replaced with empty directory", path) emptyID, err := restic.SaveTree(ctx, repo, &restic.Tree{}) if err != nil { return restic.ID{}, err @@ -144,7 +149,7 @@ func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOpt changedCount := 0 for sn := range FindFilteredSnapshots(ctx, snapshotLister, repo, &opts.SnapshotFilter, args) { - Verbosef("\n%v\n", sn) + printer.P("\n%v", sn) changed, err := filterAndReplaceSnapshot(ctx, repo, sn, func(ctx context.Context, sn *restic.Snapshot) (restic.ID, *restic.SnapshotSummary, error) { id, err := rewriter.RewriteTree(ctx, repo, "/", *sn.Tree) @@ -161,18 +166,18 @@ func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOpt return ctx.Err() } - Verbosef("\n") + printer.P("") if changedCount == 0 { if !opts.DryRun { - Verbosef("no snapshots were modified\n") + printer.P("no snapshots were modified") } else { - Verbosef("no snapshots would be modified\n") + printer.P("no snapshots would be modified") } } else { if !opts.DryRun { - Verbosef("modified %v snapshots\n", changedCount) + printer.P("modified %v snapshots", changedCount) } else { - Verbosef("would modify %v snapshots\n", changedCount) + printer.P("would modify %v snapshots", changedCount) } } diff --git a/cmd/restic/cmd_repair_snapshots_integration_test.go b/cmd/restic/cmd_repair_snapshots_integration_test.go index 05ad1868d..d6bf055a5 100644 --- a/cmd/restic/cmd_repair_snapshots_integration_test.go +++ b/cmd/restic/cmd_repair_snapshots_integration_test.go @@ -12,6 +12,7 @@ import ( "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" + "github.com/restic/restic/internal/ui/termstatus" ) func testRunRepairSnapshot(t testing.TB, gopts GlobalOptions, forget bool) { @@ -19,7 +20,9 @@ func testRunRepairSnapshot(t testing.TB, gopts GlobalOptions, forget bool) { Forget: forget, } - rtest.OK(t, runRepairSnapshots(context.TODO(), gopts, opts, nil)) + rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, term *termstatus.Terminal) error { + return runRepairSnapshots(context.TODO(), gopts, opts, nil, term) + })) } func createRandomFile(t testing.TB, env *testEnvironment, path string, size int) {