From 115ecb3c92ca2ca81c1b09d9f0072c80defa092f Mon Sep 17 00:00:00 2001 From: Srigovind Nayak <5201843+konidev20@users.noreply.github.com> Date: Tue, 14 Jan 2025 23:27:47 +0530 Subject: [PATCH] tag: output the original ID and new snapshotID (#5144) * tag: output the original ID and new snapshotID tag: print changed snapshot information immediately * print changed snapshot immediately after it has been saved * add message type to the changedSnapshot * add a summary type which will share the JSON output of the numer of changed snapshots * updated verbosity of the changed snapshot in text mode to only work when verbosity > 2 * also use the terminal status printer for a standard handling for stdout messages --- changelog/unreleased/issue-5137 | 8 ++++ cmd/restic/cmd_tag.go | 61 +++++++++++++++++++++----- cmd/restic/cmd_tag_integration_test.go | 2 +- doc/075_scripting.rst | 24 ++++++++++ 4 files changed, 82 insertions(+), 13 deletions(-) create mode 100644 changelog/unreleased/issue-5137 diff --git a/changelog/unreleased/issue-5137 b/changelog/unreleased/issue-5137 new file mode 100644 index 000000000..ba681202c --- /dev/null +++ b/changelog/unreleased/issue-5137 @@ -0,0 +1,8 @@ +Enhancement: Restic tag command returns the modified snapshot information + +Restic `tag` command now returns the modified snapshot information in the +output. Added `--json` option to the command to get the output in JSON format +for scripting access. + +https://github.com/restic/restic/issues/5137 +https://github.com/restic/restic/pull/5144 \ No newline at end of file diff --git a/cmd/restic/cmd_tag.go b/cmd/restic/cmd_tag.go index 539a0cc59..f71e2556c 100644 --- a/cmd/restic/cmd_tag.go +++ b/cmd/restic/cmd_tag.go @@ -9,6 +9,8 @@ import ( "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" + "github.com/restic/restic/internal/ui" + "github.com/restic/restic/internal/ui/termstatus" ) var cmdTag = &cobra.Command{ @@ -34,7 +36,9 @@ Exit status is 12 if the password is incorrect. GroupID: cmdGroupDefault, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - return runTag(cmd.Context(), tagOptions, globalOptions, args) + term, cancel := setupTermstatus() + defer cancel() + return runTag(cmd.Context(), tagOptions, globalOptions, term, args) }, } @@ -58,7 +62,18 @@ func init() { initMultiSnapshotFilter(tagFlags, &tagOptions.SnapshotFilter, true) } -func changeTags(ctx context.Context, repo *repository.Repository, sn *restic.Snapshot, setTags, addTags, removeTags []string) (bool, error) { +type changedSnapshot struct { + MessageType string `json:"message_type"` // changed + OldSnapshotID restic.ID `json:"old_snapshot_id"` + NewSnapshotID restic.ID `json:"new_snapshot_id"` +} + +type changedSnapshotsSummary struct { + MessageType string `json:"message_type"` // summary + ChangedSnapshots int `json:"changed_snapshots"` +} + +func changeTags(ctx context.Context, repo *repository.Repository, sn *restic.Snapshot, setTags, addTags, removeTags []string, printFunc func(changedSnapshot)) (bool, error) { var changed bool if len(setTags) != 0 { @@ -87,7 +102,7 @@ func changeTags(ctx context.Context, repo *repository.Repository, sn *restic.Sna return false, err } - debug.Log("new snapshot saved as %v", id) + debug.Log("old snapshot %v saved as a new snapshot %v", sn.ID(), id) // Remove the old snapshot. if err = repo.RemoveUnpacked(ctx, restic.WriteableSnapshotFile, *sn.ID()); err != nil { @@ -95,11 +110,13 @@ func changeTags(ctx context.Context, repo *repository.Repository, sn *restic.Sna } debug.Log("old snapshot %v removed", sn.ID()) + + printFunc(changedSnapshot{MessageType: "changed", OldSnapshotID: *sn.ID(), NewSnapshotID: id}) } return changed, nil } -func runTag(ctx context.Context, opts TagOptions, gopts GlobalOptions, args []string) error { +func runTag(ctx context.Context, opts TagOptions, gopts GlobalOptions, term *termstatus.Terminal, args []string) error { if len(opts.SetTags) == 0 && len(opts.AddTags) == 0 && len(opts.RemoveTags) == 0 { return errors.Fatal("nothing to do!") } @@ -114,24 +131,44 @@ func runTag(ctx context.Context, opts TagOptions, gopts GlobalOptions, args []st } defer unlock() - changeCnt := 0 + printFunc := func(c changedSnapshot) { + Verboseff("old snapshot ID: %v -> new snapshot ID: %v\n", c.OldSnapshotID, c.NewSnapshotID) + } + + summary := changedSnapshotsSummary{MessageType: "summary", ChangedSnapshots: 0} + printSummary := func(c changedSnapshotsSummary) { + if c.ChangedSnapshots == 0 { + Verbosef("no snapshots were modified\n") + } else { + Verbosef("modified %v snapshots\n", c.ChangedSnapshots) + } + } + + if gopts.JSON { + printFunc = func(c changedSnapshot) { + term.Print(ui.ToJSONString(c)) + } + printSummary = func(c changedSnapshotsSummary) { + term.Print(ui.ToJSONString(c)) + } + } + for sn := range FindFilteredSnapshots(ctx, repo, repo, &opts.SnapshotFilter, args) { - changed, err := changeTags(ctx, repo, sn, opts.SetTags.Flatten(), opts.AddTags.Flatten(), opts.RemoveTags.Flatten()) + changed, err := changeTags(ctx, repo, sn, opts.SetTags.Flatten(), opts.AddTags.Flatten(), opts.RemoveTags.Flatten(), printFunc) if err != nil { Warnf("unable to modify the tags for snapshot ID %q, ignoring: %v\n", sn.ID(), err) continue } if changed { - changeCnt++ + summary.ChangedSnapshots++ } } + if ctx.Err() != nil { return ctx.Err() } - if changeCnt == 0 { - Verbosef("no snapshots were modified\n") - } else { - Verbosef("modified tags on %v snapshots\n", changeCnt) - } + + printSummary(summary) + return nil } diff --git a/cmd/restic/cmd_tag_integration_test.go b/cmd/restic/cmd_tag_integration_test.go index 6979f9c11..53360ca84 100644 --- a/cmd/restic/cmd_tag_integration_test.go +++ b/cmd/restic/cmd_tag_integration_test.go @@ -9,7 +9,7 @@ import ( ) func testRunTag(t testing.TB, opts TagOptions, gopts GlobalOptions) { - rtest.OK(t, runTag(context.TODO(), opts, gopts, []string{})) + rtest.OK(t, runTag(context.TODO(), opts, gopts, nil, []string{})) } // nolint: staticcheck // false positive nil pointer dereference check diff --git a/doc/075_scripting.rst b/doc/075_scripting.rst index 39a6dbc7f..57a8e2872 100644 --- a/doc/075_scripting.rst +++ b/doc/075_scripting.rst @@ -722,6 +722,30 @@ The stats command returns a single JSON object. | ``compression_space_saving`` | Overall space saving due to compression | +------------------------------+-----------------------------------------------------+ +tag +--- + +The ``tag`` command uses the JSON lines format with the following message types. + +Changed +^^^^^^^ + ++--------------------------+-------------------------------------------+ +| ``message_type`` | Always "changed" | ++--------------------------+-------------------------------------------+ +| ``old_snapshot_id`` | ID of the snapshot before the change | ++--------------------------+-------------------------------------------+ +| ``new_snapshot_id`` | ID of the snapshot after the change | ++--------------------------+-------------------------------------------+ + +Summary +^^^^^^^ + ++-----------------------------+-------------------------------------------+ +| ``message_type`` | Always "summary" | ++-----------------------------+-------------------------------------------+ +| ``changed_snapshot_count`` | Total number of changed snapshots | ++-----------------------------+-------------------------------------------+ version -------