Add 'vaultik remote nuke', rename Processing→Backing up, bits/sec rates

remote nuke: new subcommand that deletes every snapshot's metadata and
every blob from remote storage, leaving the bucket prefix empty.
Requires --force.

User-facing 'Processing' is now 'Backing up' everywhere it referred to
the chunking/upload phase. Files summary line says 'backed up' instead
of 'processed'.

ui.Speed now formats bytes/sec input as bits/sec output (bit/s, Kbit/s,
Mbit/s, Gbit/s). Network transfer rates are conventionally expressed
in bits — the per-blob heartbeat now matches the per-snapshot summary
line which has always been bits/sec.
This commit is contained in:
2026-06-17 06:21:21 +02:00
parent 64c69cd8e3
commit e75367c594
7 changed files with 141 additions and 25 deletions

View File

@@ -203,9 +203,26 @@ func (w *Writer) Size(bytes int64) string {
return w.paint(ansiMagenta, humanize.Bytes(uint64(bytes)))
}
// Speed colorizes a byte-per-second value as "<size>/sec".
// Speed colorizes a network transfer rate. Input is bytes/sec; output is
// bits/sec with an appropriate SI unit (bit/s, Kbit/s, Mbit/s, Gbit/s) —
// network transfer rates are conventionally expressed in bits.
func (w *Writer) Speed(bytesPerSec float64) string {
return w.paint(ansiMagenta, humanize.Bytes(uint64(bytesPerSec))+"/sec")
if bytesPerSec <= 0 {
return w.paint(ansiMagenta, "N/A")
}
bitsPerSec := bytesPerSec * 8
var s string
switch {
case bitsPerSec >= 1e9:
s = fmt.Sprintf("%.1f Gbit/sec", bitsPerSec/1e9)
case bitsPerSec >= 1e6:
s = fmt.Sprintf("%.0f Mbit/sec", bitsPerSec/1e6)
case bitsPerSec >= 1e3:
s = fmt.Sprintf("%.0f Kbit/sec", bitsPerSec/1e3)
default:
s = fmt.Sprintf("%.0f bit/sec", bitsPerSec)
}
return w.paint(ansiMagenta, s)
}
// Duration colorizes a time.Duration rounded to the nearest second.

View File

@@ -100,6 +100,17 @@ func TestValueFormattersPlain(t *testing.T) {
t.Errorf("Percent: got %q", got)
}
// Speed: input is bytes/sec, output is bits/sec.
if got := w.Speed(0); got != "N/A" {
t.Errorf("Speed(0): got %q, want N/A", got)
}
if got := w.Speed(125_000_000); got != "1.0 Gbit/sec" { // 1 Gbit/s = 125 MB/s
t.Errorf("Speed(125e6): got %q", got)
}
if got := w.Speed(125_000); got != "1 Mbit/sec" {
t.Errorf("Speed(125e3): got %q", got)
}
// Time format: today → HH:MM:SS, other day → YYYY-MM-DD HH:MM:SS.
today := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 14, 30, 45, 0, time.Local)
if got := w.Time(today); got != "14:30:45" {