1 Commits
Author SHA1 Message Date
sneak cafab5dc86 Exit non-zero when snapshot create --skip-errors skips anything it cannot read (closes #310)
check / check (push) Canceled after 0s
A backup under --skip-errors printed its skip errors and failed count,
then "Finished successfully.", and exited 0, so a cron wrapper could
not tell it from a complete backup. A directory the walk could not list
was not counted at all.

Each entry the walk skips is now counted as examined and failed. When
--skip-errors skipped anything it could not read or list, the run keeps
its snapshots, runs the post-backup prune as before, and returns an
error the command reports as "Backup completed, but N file(s) could not
be read". A file removed during the run, including one the walk finds
gone after listing its directory, is counted once, as failed, and
leaves the exit status at 0 with or without the flag; without it, such
a removal no longer stops the backup.

Model: opus-5-5
2026-10-14 04:15:08 +00:00
@@ -167,14 +167,14 @@ func TestBackupCountsFileRemovedBeforeWalkReachesItAsFailed(t *testing.T) {
t.Parallel()
env := newDeletedCountEnv(t, map[string][]byte{
"kept.txt": []byte("kept"),
"stays.txt": []byte("stays"),
"removed.txt": []byte("removed"),
})
env.setSnapshot("work", "")
env.setSnapshot("walk", "")
// The previous snapshot has the file, so a removal the walk
// also counted as deleted would show.
env.backUp(t, "work")
env.backUp(t, "walk")
env.v.Fs = &removeOnLookupFs{
path: filepath.Join(env.src, "removed.txt"),
@@ -183,7 +183,7 @@ func TestBackupCountsFileRemovedBeforeWalkReachesItAsFailed(t *testing.T) {
env.out.Reset()
require.NoError(t, env.v.CreateSnapshot(&vaultik.SnapshotCreateOptions{
SkipErrors: tt.skipErrors,
Snapshots: []string{"work"},
Snapshots: []string{"walk"},
}))
assert.Contains(t, env.out.String(),
"Files: 2 examined, 0 backed up, 1 unchanged, 1 failed.")