Skip to content

Commit 010db52

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 64834be commit 010db52

File tree

4 files changed

+55
-1
lines changed

4 files changed

+55
-1
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
3030
* Added `scripts/uninstall.sh` to cleanly remove Asimov and its launchd schedule ([#35], props @sylver)
3131
* Added common interval reference comments to `com.stevegrunwell.asimov.plist` ([#35], props @sylver)
3232

33+
### Fixed
34+
35+
* Handle `tmutil` errors gracefully instead of crashing; paths that fail exclusion are skipped with a warning ([stevegrunwell/asimov#101], [stevegrunwell/asimov#86])
36+
3337
### Changed
3438

3539
* Migrated test suite from PHP/PHPUnit to [Bats](https://github.com/bats-core/bats-core) (Bash Automated Testing System), removing the PHP dependency for contributors
@@ -117,3 +121,5 @@ Initial public release.
117121
[#55]: https://github.com/stevegrunwell/asimov/pull/55
118122
[#35]: https://github.com/stevegrunwell/asimov/pull/35
119123
[#56]: https://github.com/stevegrunwell/asimov/pull/56
124+
[stevegrunwell/asimov#101]: https://github.com/stevegrunwell/asimov/issues/101
125+
[stevegrunwell/asimov#86]: https://github.com/stevegrunwell/asimov/issues/86

asimov

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,10 @@ exclude_file() {
103103
continue
104104
fi
105105

106-
tmutil addexclusion "${path}"
106+
if ! tmutil addexclusion "${path}" 2>/dev/null; then
107+
echo "! ${path}: failed to exclude (tmutil error), skipping." >&2
108+
continue
109+
fi
107110

108111
sizeondisk=$(du -hs "${path}" | cut -f1)
109112
echo "- ${path} has been excluded from Time Machine backups (${sizeondisk})."

tests/behavior.bats

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,41 @@ load test_helper
127127
refute_excluded "${HOME}/Code/My-Project/node_modules/dep/node_modules"
128128
[[ "$(count_exclusions)" -eq 1 ]]
129129
}
130+
131+
# =============================================================================
132+
# Error handling
133+
# =============================================================================
134+
135+
@test "continues when tmutil fails for a path" {
136+
create_project "Code/Good-Project" "package.json" "node_modules"
137+
create_project "Code/Bad-Project" "Cargo.toml" "target"
138+
139+
# Mark the bad project as one that will cause tmutil to fail
140+
ASIMOV_TEST_TMUTIL_FAIL_PATHS="${TEST_TEMP_DIR}/.tmutil_fail_paths"
141+
export ASIMOV_TEST_TMUTIL_FAIL_PATHS
142+
echo "${HOME}/Code/Bad-Project/target" > "$ASIMOV_TEST_TMUTIL_FAIL_PATHS"
143+
144+
run_asimov
145+
146+
# The good project should still be excluded
147+
assert_excluded "${HOME}/Code/Good-Project/node_modules"
148+
# The bad project should NOT be in the exclusions list
149+
refute_excluded "${HOME}/Code/Bad-Project/target"
150+
[[ "$(count_exclusions)" -eq 1 ]]
151+
}
152+
153+
@test "prints warning when tmutil fails" {
154+
create_project "Code/Bad-Project" "package.json" "node_modules"
155+
156+
ASIMOV_TEST_TMUTIL_FAIL_PATHS="${TEST_TEMP_DIR}/.tmutil_fail_paths"
157+
export ASIMOV_TEST_TMUTIL_FAIL_PATHS
158+
echo "${HOME}/Code/Bad-Project/node_modules" > "$ASIMOV_TEST_TMUTIL_FAIL_PATHS"
159+
160+
run_asimov
161+
162+
# Script should succeed (exit 0) even though tmutil failed
163+
[[ "$status" -eq 0 ]]
164+
# Output should contain the warning
165+
[[ "$output" == *"failed to exclude"* ]]
166+
[[ "$(count_exclusions)" -eq 0 ]]
167+
}

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)