Print remote info and verify status lines through internal/ui (closes #410)
check / check (push) Successful in 26m13s
check / check (push) Successful in 26m13s
The status and progress lines of remote info and snapshot verify (shallow and deep) were written with stdoutf, so they had no marker and no color, unlike every other command's. They now go through Beginf, Completef and Progressf, as the README's output style section says. The reports stay plain, and --quiet and --json still drop these lines. Model: opus-5-5
This commit was merged in pull request #414.
This commit is contained in:
1 parent
5a0da6da12
commit
4418dc3e97
6 files changed
+90
-14
No files matched your search
@@ -24,6 +24,12 @@ owner's; a `v*` tag runs the release build in
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-11-10: Printed the status and progress lines of `remote info`
|
||||||
|
and `snapshot verify` through `internal/ui`, with its marker and
|
||||||
|
color, as the README describes
|
||||||
|
([issue #410](https://git.eeqj.de/sneak/vaultik/issues/410)). Their
|
||||||
|
reports stay plain.
|
||||||
|
|
||||||
- 2026-11-10: Made `config init` write the `apps` snapshot, which backs
|
- 2026-11-10: Made `config init` write the `apps` snapshot, which backs
|
||||||
up `/Applications`, only on macOS
|
up `/Applications`, only on macOS
|
||||||
([issue #408](https://git.eeqj.de/sneak/vaultik/issues/408)). On
|
([issue #408](https://git.eeqj.de/sneak/vaultik/issues/408)). On
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ func (v *Vaultik) RemoteInfo(jsonOutput bool) error {
|
|||||||
v.stdoutf("Type: %s\n", storageInfo.Type)
|
v.stdoutf("Type: %s\n", storageInfo.Type)
|
||||||
v.stdoutf("Location: %s\n", storageInfo.Location)
|
v.stdoutf("Location: %s\n", storageInfo.Location)
|
||||||
v.printlnStdout()
|
v.printlnStdout()
|
||||||
v.stdoutf("Scanning snapshot metadata...\n")
|
v.UI.Beginf("Scanning snapshot metadata...")
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshotMetadata, snapshotIDs, skippedManifestCount, err := v.collectSnapshotMetadata()
|
snapshotMetadata, snapshotIDs, skippedManifestCount, err := v.collectSnapshotMetadata()
|
||||||
@@ -265,7 +265,7 @@ func (v *Vaultik) RemoteInfo(jsonOutput bool) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
v.stdoutf("Downloading %d manifest(s)...\n", manifestCount)
|
v.UI.Beginf("Downloading %d manifest(s)...", manifestCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
referencedBlobs, unreadableManifests := v.collectReferencedBlobsFromManifests(
|
referencedBlobs, unreadableManifests := v.collectReferencedBlobsFromManifests(
|
||||||
@@ -447,7 +447,7 @@ func (v *Vaultik) scanRemoteBlobStorage(
|
|||||||
result *RemoteInfoResult, referencedBlobs map[string]int64, showText bool,
|
result *RemoteInfoResult, referencedBlobs map[string]int64, showText bool,
|
||||||
) error {
|
) error {
|
||||||
if showText {
|
if showText {
|
||||||
v.stdoutf("Scanning blobs...\n")
|
v.UI.Beginf("Scanning blobs...")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use prune's listing, so a file under blobs/ that prune would never
|
// Use prune's listing, so a file under blobs/ that prune would never
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"sneak.berlin/go/vaultik/internal/log"
|
"sneak.berlin/go/vaultik/internal/log"
|
||||||
"sneak.berlin/go/vaultik/internal/snapshot"
|
"sneak.berlin/go/vaultik/internal/snapshot"
|
||||||
|
"sneak.berlin/go/vaultik/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
// testBlobHashB is a blob that the manifest written by addRemote does
|
// testBlobHashB is a blob that the manifest written by addRemote does
|
||||||
@@ -259,3 +260,24 @@ func TestRemoteInfo_DirectoryWithoutManifestLeavesOrphansKnown(t *testing.T) {
|
|||||||
assert.NotContains(t, doc, "unreadable_manifests")
|
assert.NotContains(t, doc, "unreadable_manifests")
|
||||||
assert.NotContains(t, doc, "skipped_manifest_count")
|
assert.NotContains(t, doc, "skipped_manifest_count")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestRemoteInfo_StatusLineUsesUIMarker checks that the remote info
|
||||||
|
// status lines go through internal/ui: marker-prefixed, and silenced by
|
||||||
|
// --quiet.
|
||||||
|
func TestRemoteInfo_StatusLineUsesUIMarker(t *testing.T) {
|
||||||
|
log.Initialize(log.Config{})
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
env := newListEnv(t)
|
||||||
|
env.addRemote(t, listRemoteID, time.Date(2026, 3, 2, 0, 0, 0, 0, time.UTC))
|
||||||
|
|
||||||
|
const statusLine = ui.Marker + " Scanning snapshot metadata...\n"
|
||||||
|
|
||||||
|
require.NoError(t, env.v.RemoteInfo(false))
|
||||||
|
assert.Contains(t, env.stdout.String(), statusLine)
|
||||||
|
|
||||||
|
env.stdout.Reset()
|
||||||
|
env.v.UI.SetQuiet(true)
|
||||||
|
require.NoError(t, env.v.RemoteInfo(false))
|
||||||
|
assert.NotContains(t, env.stdout.String(), "Scanning snapshot metadata")
|
||||||
|
}
|
||||||
@@ -1019,7 +1019,7 @@ func (v *Vaultik) VerifySnapshotWithOptions(
|
|||||||
v.printlnStdout()
|
v.printlnStdout()
|
||||||
|
|
||||||
// Check each blob is present with the size the manifest records.
|
// Check each blob is present with the size the manifest records.
|
||||||
v.stdoutf("Checking blob presence and sizes...\n")
|
v.UI.Beginf("Checking blob presence and sizes...")
|
||||||
}
|
}
|
||||||
|
|
||||||
v.checkSnapshotDatabase(remoteKey, result)
|
v.checkSnapshotDatabase(remoteKey, result)
|
||||||
|
|||||||
+10
-10
@@ -190,7 +190,7 @@ func (v *Vaultik) loadVerificationData(
|
|||||||
log.Info("Downloading manifest", "remote_key", remoteKey)
|
log.Info("Downloading manifest", "remote_key", remoteKey)
|
||||||
|
|
||||||
if !opts.JSON && !v.UI.Quiet() {
|
if !opts.JSON && !v.UI.Quiet() {
|
||||||
v.stdoutf("Downloading manifest...\n")
|
v.UI.Beginf("Downloading manifest...")
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest, err := v.downloadManifestByKey(remoteKey)
|
manifest, err := v.downloadManifestByKey(remoteKey)
|
||||||
@@ -205,9 +205,9 @@ func (v *Vaultik) loadVerificationData(
|
|||||||
"manifest_total_size", ubytes(manifest.TotalCompressedSize))
|
"manifest_total_size", ubytes(manifest.TotalCompressedSize))
|
||||||
|
|
||||||
if !opts.JSON && !v.UI.Quiet() {
|
if !opts.JSON && !v.UI.Quiet() {
|
||||||
v.stdoutf("Manifest loaded: %d blobs (%s)\n",
|
v.UI.Completef("Manifest loaded: %d blobs (%s)",
|
||||||
manifest.BlobCount, ubytes(manifest.TotalCompressedSize))
|
manifest.BlobCount, ubytes(manifest.TotalCompressedSize))
|
||||||
v.stdoutf("Downloading and decrypting database...\n")
|
v.UI.Beginf("Downloading and decrypting database...")
|
||||||
}
|
}
|
||||||
|
|
||||||
tdb, err := v.downloadVerifiedSnapshotDB(
|
tdb, err := v.downloadVerifiedSnapshotDB(
|
||||||
@@ -235,7 +235,7 @@ func (v *Vaultik) loadVerificationData(
|
|||||||
"db_total_size", ubytes(dbTotalSize))
|
"db_total_size", ubytes(dbTotalSize))
|
||||||
|
|
||||||
if !opts.JSON && !v.UI.Quiet() {
|
if !opts.JSON && !v.UI.Quiet() {
|
||||||
v.stdoutf("Database loaded: %d blobs (%s)\n",
|
v.UI.Completef("Database loaded: %d blobs (%s)",
|
||||||
len(dbBlobs), ubytes(dbTotalSize))
|
len(dbBlobs), ubytes(dbTotalSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,7 +295,7 @@ func (v *Vaultik) runVerificationSteps(
|
|||||||
identities []age.Identity,
|
identities []age.Identity,
|
||||||
) error {
|
) error {
|
||||||
if !opts.JSON && !v.UI.Quiet() {
|
if !opts.JSON && !v.UI.Quiet() {
|
||||||
v.stdoutf("Verifying manifest against database...\n")
|
v.UI.Beginf("Verifying manifest against database...")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := v.verifyManifestAgainstDatabase(manifest, dbBlobs)
|
err := v.verifyManifestAgainstDatabase(manifest, dbBlobs)
|
||||||
@@ -304,8 +304,8 @@ func (v *Vaultik) runVerificationSteps(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !opts.JSON && !v.UI.Quiet() {
|
if !opts.JSON && !v.UI.Quiet() {
|
||||||
v.stdoutf("Manifest verified.\n")
|
v.UI.Completef("Manifest verified.")
|
||||||
v.stdoutf("Checking blob existence in remote storage...\n")
|
v.UI.Beginf("Checking blob existence in remote storage...")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = v.verifyBlobExistenceFromDB(dbBlobs, result)
|
err = v.verifyBlobExistenceFromDB(dbBlobs, result)
|
||||||
@@ -314,8 +314,8 @@ func (v *Vaultik) runVerificationSteps(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !opts.JSON && !v.UI.Quiet() {
|
if !opts.JSON && !v.UI.Quiet() {
|
||||||
v.stdoutf("All blobs exist.\n")
|
v.UI.Completef("All blobs exist.")
|
||||||
v.stdoutf("Downloading and verifying blob contents (%d blobs, %s)...\n",
|
v.UI.Beginf("Downloading and verifying blob contents (%d blobs, %s)...",
|
||||||
len(dbBlobs), ubytes(totalSize))
|
len(dbBlobs), ubytes(totalSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -790,7 +790,7 @@ func (v *Vaultik) performDeepVerificationFromDB(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if !opts.JSON && !v.UI.Quiet() {
|
if !opts.JSON && !v.UI.Quiet() {
|
||||||
v.stdoutf(" Verified %d/%d blobs (%d remaining) - %s/%s - elapsed %s, eta %s\n",
|
v.UI.Progressf("Verified %d/%d blobs (%d remaining) - %s/%s - elapsed %s, eta %s",
|
||||||
i+1, len(blobs), remaining,
|
i+1, len(blobs), remaining,
|
||||||
ubytes(bytesProcessed),
|
ubytes(bytesProcessed),
|
||||||
ubytes(totalBytesExpected),
|
ubytes(totalBytesExpected),
|
||||||
|
|||||||
@@ -100,3 +100,51 @@ func TestVerify_QuietSuppressesReport(t *testing.T) {
|
|||||||
require.Empty(t, stdout.String(),
|
require.Empty(t, stdout.String(),
|
||||||
"a failed verify must write no report under --quiet")
|
"a failed verify must write no report under --quiet")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestVerify_DeepStatusLineUsesUIMarker checks that the deep verify
|
||||||
|
// status lines go through internal/ui: marker-prefixed, and silenced by
|
||||||
|
// --quiet.
|
||||||
|
func TestVerify_DeepStatusLineUsesUIMarker(t *testing.T) {
|
||||||
|
log.Initialize(log.Config{})
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
fs := afero.NewOsFs()
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
|
||||||
|
dataDir := filepath.Join(tempDir, "source")
|
||||||
|
storeDir := t.TempDir()
|
||||||
|
dbPath := filepath.Join(tempDir, "index.sqlite")
|
||||||
|
|
||||||
|
require.NoError(t, fs.MkdirAll(dataDir, 0o755))
|
||||||
|
require.NoError(t, afero.WriteFile(fs,
|
||||||
|
filepath.Join(dataDir, "data.bin"),
|
||||||
|
bytesPattern("marker-", 64*1024), 0o644))
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
cfg, storer, snapshotID := runFileStorageBackup(
|
||||||
|
ctx, t, fs, dataDir, storeDir, dbPath, 32*1024, 128*1024)
|
||||||
|
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
|
||||||
|
v := &vaultik.Vaultik{
|
||||||
|
Config: cfg,
|
||||||
|
Storage: storer,
|
||||||
|
Fs: fs,
|
||||||
|
Stdout: &stdout,
|
||||||
|
Stderr: io.Discard,
|
||||||
|
UI: ui.NewWithColor(&stdout, false),
|
||||||
|
}
|
||||||
|
v.SetContext(ctx)
|
||||||
|
|
||||||
|
opts := &vaultik.VerifyOptions{Deep: true}
|
||||||
|
|
||||||
|
require.NoError(t, v.VerifySnapshotWithOptions(snapshotID, opts))
|
||||||
|
require.Contains(t, stdout.String(),
|
||||||
|
"\n"+ui.Marker+" Downloading manifest...\n")
|
||||||
|
|
||||||
|
stdout.Reset()
|
||||||
|
v.UI.SetQuiet(true)
|
||||||
|
require.NoError(t, v.VerifySnapshotWithOptions(snapshotID, opts))
|
||||||
|
require.NotContains(t, stdout.String(), "Downloading manifest")
|
||||||
|
}
|
||||||
Reference in new issue
Block a user