From 76e047bbb287acfdf27fa300fbc684ca5a607503 Mon Sep 17 00:00:00 2001 From: clawbot Date: Sun, 15 Feb 2026 21:34:46 -0800 Subject: [PATCH] feat: implement --prune flag on snapshot create (closes #4) The --prune flag on 'snapshot create' was accepted but silently did nothing (TODO stub). This connects it to actually: 1. Purge old snapshots (keeping only the latest) via PurgeSnapshots 2. Remove unreferenced blobs from storage via PruneBlobs The pruning runs after all snapshots complete successfully, not per-snapshot. Both operations use --force mode (no interactive confirmation) since --prune is an explicit opt-in flag. Moved the prune logic from createNamedSnapshot (per-snapshot) to CreateSnapshot (after all snapshots), which is the correct location. --- internal/vaultik/snapshot.go | 23 ++++++++++++++++++----- internal/vaultik/snapshot_prune_test.go | 23 +++++++++++++++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 internal/vaultik/snapshot_prune_test.go diff --git a/internal/vaultik/snapshot.go b/internal/vaultik/snapshot.go index 2960389..94df29b 100644 --- a/internal/vaultik/snapshot.go +++ b/internal/vaultik/snapshot.go @@ -90,6 +90,24 @@ func (v *Vaultik) CreateSnapshot(opts *SnapshotCreateOptions) error { v.printfStdout("\nAll %d snapshots completed in %s\n", len(snapshotNames), time.Since(overallStartTime).Round(time.Second)) } + // Prune old snapshots and unreferenced blobs if --prune was specified + if opts.Prune { + log.Info("Pruning enabled - deleting old snapshots and unreferenced blobs") + v.printlnStdout("\nPruning old snapshots (keeping latest)...") + + if err := v.PurgeSnapshots(true, "", true); err != nil { + return fmt.Errorf("prune: purging old snapshots: %w", err) + } + + v.printlnStdout("Pruning unreferenced blobs...") + + if err := v.PruneBlobs(&PruneOptions{Force: true}); err != nil { + return fmt.Errorf("prune: removing unreferenced blobs: %w", err) + } + + log.Info("Pruning complete") + } + return nil } @@ -306,11 +324,6 @@ func (v *Vaultik) createNamedSnapshot(opts *SnapshotCreateOptions, hostname, sna } v.printfStdout("Duration: %s\n", formatDuration(snapshotDuration)) - if opts.Prune { - log.Info("Pruning enabled - will delete old snapshots after snapshot") - // TODO: Implement pruning - } - return nil } diff --git a/internal/vaultik/snapshot_prune_test.go b/internal/vaultik/snapshot_prune_test.go new file mode 100644 index 0000000..dbff412 --- /dev/null +++ b/internal/vaultik/snapshot_prune_test.go @@ -0,0 +1,23 @@ +package vaultik + +import ( + "testing" +) + +// TestSnapshotCreateOptions_PruneFlag verifies the Prune field exists on +// SnapshotCreateOptions and can be set. +func TestSnapshotCreateOptions_PruneFlag(t *testing.T) { + opts := &SnapshotCreateOptions{ + Prune: true, + } + if !opts.Prune { + t.Error("Expected Prune to be true") + } + + opts2 := &SnapshotCreateOptions{ + Prune: false, + } + if opts2.Prune { + t.Error("Expected Prune to be false") + } +}