Exit non-zero when snapshot create --skip-errors skips anything (closes #310)
check / check (push) Successful in 12m20s
check / check (push) Successful in 12m20s
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 anything failed under --skip-errors, the run keeps its snapshots, runs the post-backup prune as before, and then returns an error that the command reports as "Backup completed, but N file(s) failed to back up". Judgement call: without --skip-errors a file removed during the run is still counted as failed and the exit status stays 0. Model: opus-5-5
This commit is contained in:
@@ -141,10 +141,11 @@ type ScannerConfig struct {
|
||||
// ScanResult contains the results of a scan operation. Files and bytes
|
||||
// are counted per file: BytesScanned is the size of the new and changed
|
||||
// files, BytesSkipped that of the unchanged ones. FilesFailed counts the
|
||||
// new and changed files that phase 2 could not store; FilesScanned
|
||||
// includes them and BytesScanned does not. ChunksCreated counts the
|
||||
// chunks that were not already stored, and NewChunkSize is their total
|
||||
// size.
|
||||
// new and changed files that phase 2 could not store and, under
|
||||
// --skip-errors, each entry the walk could not read, such as a directory
|
||||
// it could not list; FilesScanned includes them and BytesScanned does
|
||||
// not. ChunksCreated counts the chunks that were not already stored, and
|
||||
// NewChunkSize is their total size.
|
||||
type ScanResult struct {
|
||||
FilesScanned int
|
||||
FilesSkipped int
|
||||
@@ -883,7 +884,7 @@ func (s *Scanner) scanPhase(
|
||||
|
||||
walkFn := func(filePath string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return s.handleWalkError(filePath, err)
|
||||
return s.handleWalkError(filePath, err, result)
|
||||
}
|
||||
|
||||
// Check context cancellation
|
||||
@@ -959,7 +960,7 @@ func (s *Scanner) recordSpecialEntry(
|
||||
if info.Mode()&os.ModeSymlink != 0 {
|
||||
file, err := s.buildSymlinkEntry(filePath, info)
|
||||
if err != nil {
|
||||
return true, s.handleWalkError(filePath, err)
|
||||
return true, s.handleWalkError(filePath, err, result)
|
||||
}
|
||||
|
||||
existingFiles[filePath] = struct{}{}
|
||||
@@ -983,14 +984,20 @@ func (s *Scanner) recordSpecialEntry(
|
||||
}
|
||||
|
||||
// handleWalkError deals with a filesystem error surfaced by the walk:
|
||||
// skipped (with a UI notice) under --skip-errors, fatal otherwise.
|
||||
func (s *Scanner) handleWalkError(filePath string, err error) error {
|
||||
// under --skip-errors the entry is skipped with a UI notice and counted
|
||||
// as examined and failed, otherwise the error is fatal.
|
||||
func (s *Scanner) handleWalkError(
|
||||
filePath string, err error, result *ScanResult,
|
||||
) error {
|
||||
if s.skipErrors {
|
||||
log.Error("Failed to access file (skipping due to --skip-errors)",
|
||||
"path", filePath, "error", err)
|
||||
s.ui.Errorf("Failed to access %s: %v. Skipping (--skip-errors).",
|
||||
s.ui.Path(filePath), err)
|
||||
|
||||
result.FilesScanned++
|
||||
result.FilesFailed++
|
||||
|
||||
return nil // Continue scanning
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user