package cli import ( "context" "os" "github.com/spf13/cobra" "go.uber.org/fx" "sneak.berlin/go/vaultik/internal/log" "sneak.berlin/go/vaultik/internal/vaultik" ) // NewPruneCommand creates the prune command func NewPruneCommand() *cobra.Command { opts := &vaultik.PruneOptions{} cmd := &cobra.Command{ Use: "prune", Short: "Tidy local database and remote storage", Long: `Removes orphaned data from both the local index database and unreferenced blobs from the backup destination store. Local cleanup drops incomplete snapshots and any files, chunks, or blobs no longer referenced by a completed snapshot. Remote cleanup scans every snapshot manifest in the destination store, builds the set of still-referenced blob hashes, and deletes any blob not in that set. Snapshot create --prune and snapshot remove run the same cleanup automatically; this command is the manual entry point for the same work (e.g. after a crashed backup or to reclaim storage).`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { // Use unified config resolution configPath, err := ResolveConfigPath() if err != nil { return err } // Use the app framework like other commands rootFlags := GetRootFlags() return RunWithApp(cmd.Context(), AppOptions{ ConfigPath: configPath, LogOptions: log.LogOptions{ Verbose: rootFlags.Verbose, Debug: rootFlags.Debug, Quiet: rootFlags.Quiet || opts.JSON, }, Modules: []fx.Option{}, Invokes: []fx.Option{ fx.Invoke(func(v *vaultik.Vaultik, lc fx.Lifecycle) { lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { // Start the prune operation in a goroutine go func() { // Run the prune operation if err := v.Prune(opts); err != nil { if err != context.Canceled { if !opts.JSON { log.Error("Prune operation failed", "error", err) ReportError("Prune failed: %v", err) } os.Exit(1) } } // Shutdown the app when prune completes if err := v.Shutdowner.Shutdown(); err != nil { log.Error("Failed to shutdown", "error", err) } }() return nil }, OnStop: func(ctx context.Context) error { log.Debug("Stopping prune operation") v.Cancel() return nil }, }) }), }, }) }, } cmd.Flags().BoolVar(&opts.Force, "force", false, "Skip confirmation prompt") cmd.Flags().BoolVar(&opts.JSON, "json", false, "Output pruning stats as JSON") return cmd }