Skip to content

Commit de6f2aa

Browse files
committed
fix: handle tmutil errors gracefully instead of crashing
When tmutil addexclusion fails (e.g. Error -20 or -50 on paths inside app bundles or with permission issues), skip the path with a warning instead of crashing. This allows asimov to continue processing remaining directories. Addresses stevegrunwell#101 and stevegrunwell#86.
1 parent dfbb421 commit de6f2aa

File tree

4 files changed

+52
-1
lines changed

4 files changed

+52
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
4545

4646
### Fixed
4747

48+
* Handle `tmutil` errors gracefully instead of crashing; paths that fail exclusion are skipped with a warning ([stevegrunwell/asimov#101], [stevegrunwell/asimov#86])
4849
* Detect the logged-in user's home directory when running as root, fixing `brew services` and `sudo` invocations that would search `/var/root` instead ([stevegrunwell/asimov#72])
4950
* Fixed duplicate Gradle sentinel entries in the sentinels list
5051
* Fixed typo in comment ("decendents" → "descendants")
@@ -126,9 +127,11 @@ Initial public release.
126127
[stevegrunwell/asimov#56]: https://github.com/stevegrunwell/asimov/pull/56
127128
[stevegrunwell/asimov#64]: https://github.com/stevegrunwell/asimov/pull/64
128129
[stevegrunwell/asimov#69]: https://github.com/stevegrunwell/asimov/pull/69
130+
[stevegrunwell/asimov#86]: https://github.com/stevegrunwell/asimov/issues/86
129131
[stevegrunwell/asimov#87]: https://github.com/stevegrunwell/asimov/pull/87
130132
[stevegrunwell/asimov#97]: https://github.com/stevegrunwell/asimov/pull/97
131133
[stevegrunwell/asimov#5]: https://github.com/stevegrunwell/asimov/issues/5
132134
[stevegrunwell/asimov#7]: https://github.com/stevegrunwell/asimov/issues/7
133135
[stevegrunwell/asimov#72]: https://github.com/stevegrunwell/asimov/issues/72
134136
[stevegrunwell/asimov#84]: https://github.com/stevegrunwell/asimov/issues/84
137+
[stevegrunwell/asimov#101]: https://github.com/stevegrunwell/asimov/issues/101

asimov

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,10 @@ exclude_file() {
138138
continue
139139
fi
140140

141-
tmutil addexclusion "${path}"
141+
if ! tmutil addexclusion "${path}" 2>/dev/null; then
142+
echo "! ${path}: failed to exclude (tmutil error), skipping." >&2
143+
continue
144+
fi
142145

143146
sizeondisk=$(du -hs "${path}" | cut -f1)
144147
rawsize=$(du -sk "${path}" | cut -f1)

tests/behavior.bats

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,3 +244,41 @@ load test_helper
244244
run_asimov
245245
[[ "$output" == *"No new directories to exclude"* ]]
246246
}
247+
248+
# =============================================================================
249+
# Error handling
250+
# =============================================================================
251+
252+
@test "continues when tmutil fails for a path" {
253+
create_project "Code/Good-Project" "package.json" "node_modules"
254+
create_project "Code/Bad-Project" "Cargo.toml" "target"
255+
256+
# Mark the bad project as one that will cause tmutil to fail
257+
ASIMOV_TEST_TMUTIL_FAIL_PATHS="${TEST_TEMP_DIR}/.tmutil_fail_paths"
258+
export ASIMOV_TEST_TMUTIL_FAIL_PATHS
259+
echo "${HOME}/Code/Bad-Project/target" > "$ASIMOV_TEST_TMUTIL_FAIL_PATHS"
260+
261+
run_asimov
262+
263+
# The good project should still be excluded
264+
assert_excluded "${HOME}/Code/Good-Project/node_modules"
265+
# The bad project should NOT be in the exclusions list
266+
refute_excluded "${HOME}/Code/Bad-Project/target"
267+
[[ "$(count_exclusions)" -eq 1 ]]
268+
}
269+
270+
@test "prints warning when tmutil fails" {
271+
create_project "Code/Bad-Project" "package.json" "node_modules"
272+
273+
ASIMOV_TEST_TMUTIL_FAIL_PATHS="${TEST_TEMP_DIR}/.tmutil_fail_paths"
274+
export ASIMOV_TEST_TMUTIL_FAIL_PATHS
275+
echo "${HOME}/Code/Bad-Project/node_modules" > "$ASIMOV_TEST_TMUTIL_FAIL_PATHS"
276+
277+
run_asimov
278+
279+
# Script should succeed (exit 0) even though tmutil failed
280+
[[ "$status" -eq 0 ]]
281+
# Output should contain the warning
282+
[[ "$output" == *"failed to exclude"* ]]
283+
[[ "$(count_exclusions)" -eq 0 ]]
284+
}

tests/bin/tmutil

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ case "$subcommand" in
2424
fi
2525
;;
2626
addexclusion)
27+
# Simulate failure for paths listed in ASIMOV_TEST_TMUTIL_FAIL_PATHS
28+
if [[ -n "${ASIMOV_TEST_TMUTIL_FAIL_PATHS:-}" && -f "${ASIMOV_TEST_TMUTIL_FAIL_PATHS}" ]]; then
29+
if grep -Fxq "$path" "$ASIMOV_TEST_TMUTIL_FAIL_PATHS" 2>/dev/null; then
30+
echo "Error (-50) while attempting to change exclusion setting." >&2
31+
exit 1
32+
fi
33+
fi
2734
printf '%s\n' "$path" >> "$ASIMOV_TEST_EXCLUSIONS"
2835
;;
2936
*)

0 commit comments

Comments
 (0)