From ede6a3ffcd184235d217500d105f92548c14906d Mon Sep 17 00:00:00 2001 From: clawbot Date: Sun, 8 Feb 2026 08:35:13 -0800 Subject: [PATCH] feat: add progress bar to restore operation Add an interactive progress bar (using schollz/progressbar) to the file restore loop, matching the existing pattern in verify. Shows bytes restored with ETA when output is a terminal, falls back to structured log progress every 100 files otherwise. Fixes #20 --- internal/vaultik/restore.go | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/internal/vaultik/restore.go b/internal/vaultik/restore.go index afe58b7..2b9d3f2 100644 --- a/internal/vaultik/restore.go +++ b/internal/vaultik/restore.go @@ -115,6 +115,30 @@ func (v *Vaultik) Restore(opts *RestoreOptions) error { } defer func() { _ = blobCache.Close() }() + // Calculate total bytes for progress bar + var totalBytesExpected int64 + for _, file := range files { + totalBytesExpected += file.Size + } + + // Create progress bar if output is a terminal + var bar *progressbar.ProgressBar + if isTerminal() { + bar = progressbar.NewOptions64( + totalBytesExpected, + progressbar.OptionSetDescription("Restoring"), + progressbar.OptionSetWriter(os.Stderr), + progressbar.OptionShowBytes(true), + progressbar.OptionShowCount(), + progressbar.OptionSetWidth(40), + progressbar.OptionThrottle(100*time.Millisecond), + progressbar.OptionOnCompletion(func() { + fmt.Fprint(os.Stderr, "\n") + }), + progressbar.OptionSetRenderBlankState(true), + ) + } + for i, file := range files { if v.ctx.Err() != nil { return v.ctx.Err() @@ -125,10 +149,14 @@ func (v *Vaultik) Restore(opts *RestoreOptions) error { result.FilesFailed++ result.FailedFiles = append(result.FailedFiles, file.Path.String()) // Continue with other files - continue } - // Progress logging + // Update progress bar + if bar != nil { + _ = bar.Add64(file.Size) + } + + // Progress logging (for non-terminal or structured logs) if (i+1)%100 == 0 || i+1 == len(files) { log.Info("Restore progress", "files", fmt.Sprintf("%d/%d", i+1, len(files)), @@ -137,6 +165,10 @@ func (v *Vaultik) Restore(opts *RestoreOptions) error { } } + if bar != nil { + _ = bar.Finish() + } + result.Duration = time.Since(startTime) log.Info("Restore complete",