Skip to content

Commit ab78112

Browse files
committed
feat: exclude well-known global cache directories
Add fixed directory exclusions for common tool caches (~/.cache, ~/.gradle/caches, ~/.m2/repository, ~/.npm/_cacache, ~/.nuget/packages, ~/.kube/cache) that are always safe to exclude without sentinel files. Inspired by stevegrunwell#69, props @pkuczynski.
1 parent 21ae4ed commit ab78112

File tree

4 files changed

+70
-1
lines changed

4 files changed

+70
-1
lines changed

CHANGELOG.md

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

1111
* Support glob patterns in sentinel definitions, enabling wildcards like `*.xcodeproj` ([stevegrunwell/asimov#64], props @mdab121)
1212
* Exclude Xcode DerivedData when `*.xcodeproj` is present ([stevegrunwell/asimov#64], props @mdab121)
13+
* Exclude well-known global cache directories (`~/.cache`, `~/.gradle/caches`, `~/.m2/repository`, `~/.npm/_cacache`, `~/.nuget/packages`, `~/.kube/cache`, etc.) without requiring sentinel files (inspired by [stevegrunwell/asimov#69], props @pkuczynski)
1314
* Exclude Next.js build cache (`.next`)
1415
* Exclude Nuxt build cache (`.nuxt`)
1516
* Exclude Angular CLI cache (`.angular`)
@@ -127,5 +128,6 @@ Initial public release.
127128
[#35]: https://github.com/stevegrunwell/asimov/pull/35
128129
[#56]: https://github.com/stevegrunwell/asimov/pull/56
129130
[stevegrunwell/asimov#64]: https://github.com/stevegrunwell/asimov/pull/64
131+
[stevegrunwell/asimov#69]: https://github.com/stevegrunwell/asimov/pull/69
130132
[stevegrunwell/asimov#87]: https://github.com/stevegrunwell/asimov/pull/87
131133
[stevegrunwell/asimov#97]: https://github.com/stevegrunwell/asimov/pull/97

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Asimov recognizes dependency directories across **30+ patterns** in these ecosys
4949
| **R** | `renv` |
5050
| **DevOps / IaC** | `.terraform`, `.terragrunt-cache`, `.vagrant`, `.direnv`, `cdk.out` |
5151
| **Game dev** | `.godot` |
52+
| **Global caches** | `~/.cache`, `~/.gradle/caches`, `~/.m2/repository`, `~/.npm/_cacache`, `~/.nuget/packages`, `~/.kube/cache` |
5253

5354
Each directory is only excluded when its corresponding config file (the "sentinel") exists — so `node_modules` is only excluded if `package.json` is present, `vendor` only if `composer.json`, `go.mod`, or `Gemfile` exists, etc.
5455

@@ -94,7 +95,7 @@ brew uninstall asimov
9495

9596
## How it works
9697

97-
Asimov is a thin wrapper around Apple's [`tmutil`](https://ss64.com/mac/tmutil.html). It builds a single `find` command from all known dependency patterns, walks your home directory (skipping `~/Library` and `~/.Trash`), and pipes matching paths through `tmutil addexclusion`. Directories already excluded are skipped automatically — safe to run as often as you like.
98+
Asimov is a thin wrapper around Apple's [`tmutil`](https://ss64.com/mac/tmutil.html). It builds a single `find` command from all known dependency patterns, walks your home directory (skipping `~/Library` and `~/.Trash`), and pipes matching paths through `tmutil addexclusion`. It also unconditionally excludes well-known global tool caches (like `~/.cache`, `~/.gradle/caches`, and `~/.npm/_cacache`) that can always be safely restored. Directories already excluded are skipped automatically — safe to run as often as you like.
9899

99100
> **Note:** Asimov only excludes directories from Time Machine backups. It does not affect Spotlight indexing. To prevent Spotlight from indexing a directory, add it to the Privacy tab in System Settings > Spotlight (or Siri & Spotlight on newer macOS versions).
100101

asimov

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,22 @@ readonly ASIMOV_VENDOR_DIR_SENTINELS=(
9696
'renv renv.lock' # renv (R)
9797
)
9898

99+
# A list of fixed directories to exclude from Time Machine backups.
100+
#
101+
# Unlike sentinel-based pairs above, these directories are always excluded
102+
# when they exist — they represent global tool caches and artifacts that
103+
# can be safely restored.
104+
readonly ASIMOV_FIXED_DIRS=(
105+
~/.cache # XDG cache directory
106+
~/.gradle/caches # Gradle download cache
107+
~/.gradle/wrapper # Gradle wrapper distributions
108+
~/.m2/repository # Maven local repository
109+
~/.npm/_cacache # npm content-addressable cache
110+
~/.nuget/packages # NuGet global packages
111+
~/.kube/cache # Kubernetes API cache
112+
~/.kube/http-cache # Kubernetes HTTP cache
113+
)
114+
99115
# Exclude the given paths from Time Machine backups.
100116
# Reads the newline-separated list of paths from stdin.
101117
exclude_file() {
@@ -164,3 +180,11 @@ printf '\n\033[0;36mFinding dependency directories with corresponding definition
164180
find "${ASIMOV_ROOT}" \( "${find_parameters_skip[@]}" \) \( -false "${find_parameters_vendor[@]}" \) \
165181
| exclude_file \
166182
;
183+
184+
# Exclude fixed directories (global tool caches) when they exist.
185+
printf '\n\033[0;36mExcluding known cache directories…\033[0m\n'
186+
for dir in "${ASIMOV_FIXED_DIRS[@]}"; do
187+
if [[ -d "$dir" ]]; then
188+
echo "$dir"
189+
fi
190+
done | exclude_file

tests/behavior.bats

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,45 @@ load test_helper
165165
# The already-excluded one should still only have 1 entry (not duplicated)
166166
[[ "$(count_exclusions)" -eq 2 ]]
167167
}
168+
169+
# =============================================================================
170+
# Fixed directories (global caches)
171+
# =============================================================================
172+
173+
@test "excludes fixed directory when it exists" {
174+
mkdir -p "${HOME}/.cache"
175+
run_asimov
176+
assert_excluded "${HOME}/.cache"
177+
}
178+
179+
@test "does not fail when fixed directory does not exist" {
180+
# Don't create any fixed dirs — asimov should still succeed
181+
run_asimov
182+
[[ "$status" -eq 0 ]]
183+
[[ "$(count_exclusions)" -eq 0 ]]
184+
}
185+
186+
@test "excludes multiple fixed directories when they exist" {
187+
mkdir -p "${HOME}/.cache"
188+
mkdir -p "${HOME}/.gradle/caches"
189+
mkdir -p "${HOME}/.npm/_cacache"
190+
run_asimov
191+
assert_excluded "${HOME}/.cache"
192+
assert_excluded "${HOME}/.gradle/caches"
193+
assert_excluded "${HOME}/.npm/_cacache"
194+
[[ "$(count_exclusions)" -eq 3 ]]
195+
}
196+
197+
@test "does not re-exclude already excluded fixed directory" {
198+
mkdir -p "${HOME}/.cache"
199+
run_asimov
200+
assert_excluded "${HOME}/.cache"
201+
local first_count
202+
first_count="$(count_exclusions)"
203+
[[ "$first_count" -eq 1 ]]
204+
205+
run_asimov
206+
local second_count
207+
second_count="$(count_exclusions)"
208+
[[ "$second_count" -eq 1 ]]
209+
}

0 commit comments

Comments
 (0)