Commit 5184f23
Bubble errors up from worker goroutines; exit non-zero on failure (#66)
* Bubble errors up from worker goroutines; exit non-zero on failure
Replace every log.Fatal* in find_replace.go and file_handling.go with
returned errors so deferred cleanup can run, then collect errors across
walker goroutines and surface them at main.
Architecture:
- File methods (NewFile, Info, Read, Write, plus a new error-returning
Mode) now return (T, error). The walker calls Info on each child so
Write can rely on cached Mode without re-statting.
- findReplace.{WalkDir,HandleFile,RenameFile,ReplaceContents} return
errors (WalkDir records them on an embedded accumulator so its
goroutines can stay fire-and-forget).
- errAccumulator is a sync.Mutex-guarded []error with an errors.Join
exit point. Each error is also log.Print'd at the point of failure so
the existing operator-visible stderr UX is preserved; the join exists
so main can scrape stderr and so errors.Is unwraps the whole chain.
- main is now a thin os.Exit wrapper around a testable run(args, stderr)
that returns 1 on bad arg count or any traversal error and 0 on a
clean walk.
- File.Write now defer-Removes its temp file immediately after creating
it; on a successful rename the file no longer exists at tempName so
the deferred remove is a no-op, but on a rename failure the temp file
is cleaned up instead of being leaked.
New tests (each was confirmed to fail for the right reason before the
matching production change was finalized):
- TestWalkDir_PermissionDeniedSubdirContinues: a chmod-0 subdirectory
no longer aborts the walk; the sibling subtree is rewritten and the
walker records an fs.ErrPermission referencing the failing path.
Skips under root (where chmod 0 is bypassed) and Windows.
- TestRenameFile_ReturnsErrorOnExistingDestination: an occupied
destination is refused with an error, not log.Fatal.
- TestWalkDir_BadRenameTargetDoesNotAbortSiblings: a sibling whose
post-rename name is already occupied does not abort the rest of the
tree; the walker logs and records the failure and continues.
- TestWriteCleansUpTempFileOnRenameFailure: forcing the rename step to
fail (target is a non-empty directory; deterministic across users)
leaves no stray temp file behind.
- TestRun_{ExitsZeroOnSuccess,ExitsNonZeroOnTraversalError,
BadArgCountPrintsUsage}: run() returns 0 on a clean walk, non-zero
when any error was recorded, and non-zero with a usage line on bad
arg count.
Closes #6
Closes #11
Closes #5
https://claude.ai/code/session_01Tep5t8h97Q9KKbpLMbUEJr
* Bump go.mod to 1.20 to enable errors.Join
The error-aggregation primitive introduced in this PR uses errors.Join,
which was added in Go 1.20. CI was pinned to 1.19 via go.mod and 'go vet'
failed with 'Join not declared by package errors'.
This is the minimum bump needed for the chosen primitive. The broader
'be on a supported Go release' work (Go 1.19 has been EOL since August
2023) stays as #28.
* Remove deprecated rand.Seed call
Since Go 1.20 (the new toolchain floor in this PR), the math/rand global
generator is automatically seeded by the runtime; explicit rand.Seed is
a deprecated no-op and staticcheck (run by golangci-lint) flags it as
SA1019.
The line was already vestigial -- left in place by the original commit
to keep the diff narrow. The toolchain bump in the previous commit makes
that compromise no longer viable.
strings.go's RandomString still uses math/rand.Intn; the change to
crypto/rand for filesystem paths remains tracked under issue #3.
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent b144d40 commit 5184f23
5 files changed
Lines changed: 533 additions & 105 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
| |||
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
18 | | - | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
19 | 23 | | |
20 | 24 | | |
21 | | - | |
| 25 | + | |
22 | 26 | | |
23 | | - | |
| 27 | + | |
24 | 28 | | |
25 | 29 | | |
26 | 30 | | |
| |||
31 | 35 | | |
32 | 36 | | |
33 | 37 | | |
34 | | - | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
35 | 41 | | |
36 | 42 | | |
37 | 43 | | |
38 | | - | |
| 44 | + | |
39 | 45 | | |
40 | 46 | | |
41 | 47 | | |
42 | | - | |
| 48 | + | |
43 | 49 | | |
44 | 50 | | |
45 | | - | |
46 | | - | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
47 | 60 | | |
48 | 61 | | |
49 | | - | |
50 | | - | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
51 | 66 | | |
52 | 67 | | |
53 | | - | |
| 68 | + | |
54 | 69 | | |
55 | 70 | | |
56 | 71 | | |
57 | 72 | | |
58 | 73 | | |
59 | 74 | | |
60 | 75 | | |
61 | | - | |
| 76 | + | |
62 | 77 | | |
63 | 78 | | |
64 | 79 | | |
65 | 80 | | |
66 | | - | |
| 81 | + | |
67 | 82 | | |
68 | 83 | | |
69 | 84 | | |
70 | 85 | | |
71 | | - | |
| 86 | + | |
72 | 87 | | |
73 | | - | |
| 88 | + | |
74 | 89 | | |
75 | 90 | | |
76 | | - | |
77 | | - | |
78 | | - | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
79 | 101 | | |
80 | | - | |
81 | | - | |
| 102 | + | |
| 103 | + | |
82 | 104 | | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
83 | 109 | | |
84 | 110 | | |
85 | 111 | | |
86 | | - | |
| 112 | + | |
87 | 113 | | |
| 114 | + | |
88 | 115 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
9 | | - | |
10 | | - | |
11 | | - | |
12 | | - | |
| 8 | + | |
13 | 9 | | |
14 | 10 | | |
15 | 11 | | |
| |||
65 | 61 | | |
66 | 62 | | |
67 | 63 | | |
68 | | - | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
69 | 68 | | |
70 | 69 | | |
71 | 70 | | |
| |||
0 commit comments