Skip to content

v.0.4.0: Implemented all community PRs and Fixes. #35/#64/#69/#84/#87/#97, fix #72/#86/#101#103

Open
django23 wants to merge 30 commits intostevegrunwell:developfrom
django23:develop
Open

v.0.4.0: Implemented all community PRs and Fixes. #35/#64/#69/#84/#87/#97, fix #72/#86/#101#103
django23 wants to merge 30 commits intostevegrunwell:developfrom
django23:develop

Conversation

@django23
Copy link
Copy Markdown

@django23 django23 commented Feb 20, 2026

Overview

This PR represents a comprehensive modernization of Asimov; migrating the test infrastructure, fixing long-standing bugs reported by the community, and implementing a
number of widely-requested features and sentinel patterns. Every change is covered by tests.

I'm also writing this in the context of #99; I'd love to be considered as a maintainer. I've spent meaningful time on this
project, understand the architecture deeply, and have been actively triaging open issues and community PRs. More on that at the bottom.


Infrastructure & Testing

Test Suite Migration: PHP/PHPUnit → Bats

The existing PHP test suite required contributors to have PHP, Composer, and PHPUnit installed; a significant friction point for a bash utility. This PR replaces it
with Bats (Bash Automated Testing System), which:

  • Requires only brew install bats-core; already available for any macOS developer
  • Tests are plain bash, matching the project language
  • Mock tmutil and mdfind binaries live in tests/bin/ and are written in bash
  • Test coverage is expanded from ~10 cases to full per-sentinel and behavioral coverage
  • tests/behavior.bats covers: negative cases, multi-match, idempotency, skip paths, nesting, fixed dirs, error handling, and summary output
  • tests/sentinels.bats has one test per sentinel pair

Removed: composer.json, phpunit.xml.dist, and all PHP test infrastructure.

CI: Travis CI → GitHub Actions

Travis CI has become unreliable for open source projects. The new pipeline runs on GitHub Actions with a macOS 14 + 15 matrix. The workflow file is at
.github/workflows/.


Bug Fixes

Fix: tmutil errors no longer crash Asimov

Closes #101 | Related: #86

Previously, a non-zero exit from tmutil addexclusion (e.g. error -20 or -50) would stop script execution entirely. Paths that fail exclusion are now skipped with a
warning, allowing the run to continue.

Fix: Correct home directory detection when running as root

Closes #72

When Asimov runs via sudo or as a launchd service (e.g. brew services start asimov), $HOME resolves to /var/root instead of the user's actual home directory.
This fix detects the logged-in user via who / stat and uses their real home directory as ASIMOV_ROOT.


Features

Exclude well-known global cache directories

Implements #69 (props @pkuczynski) | Addresses #58

Many tools write large caches to fixed global locations; these are never project-specific and never need backing up. Added unconditional exclusions (via
ASIMOV_FIXED_DIRS) for:

  • ~/.cache
  • ~/.gradle/caches
  • ~/.m2/repository
  • ~/.npm/_cacache
  • ~/.nuget/packages
  • ~/.kube/cache
  • ~/.Trash (already mentioned in legacy code)

These are excluded whenever the directory exists, without requiring a sentinel file.

Run summary: total count and size of newly excluded directories

Closes #84 (props @Vadorequest)

Asimov now prints a summary at the end of each run showing how many directories were newly excluded and their total disk size. Subsequent runs correctly show zero if
nothing new was found (idempotent).

Skip directories already excluded from Time Machine

Implements #97 (props @VladRassokhin)

Before calling tmutil addexclusion, Asimov now checks whether the directory is already excluded (via mdfind). This makes repeated runs significantly faster and
avoids redundant tmutil calls. The mock mdfind binary in tests/bin/ supports simulating pre-existing exclusions via ASIMOV_TEST_MDFIND_RESULTS.


New Sentinel Patterns

Xcode DerivedData (glob sentinel support)

Implements #64 (props @mdab121)

Excludes DerivedData when a *.xcodeproj file is present in the same directory. This required adding glob-pattern support to the sentinel system; wildcard sentinels
use sh -c 'ls -d ...' via -execdir, enabling patterns like *.xcodeproj without breaking existing exact-match sentinels.

.NET build output (bin/, obj/)

Implements #87 (props @guigomesa)

Excludes bin and obj directories when a *.csproj or *.fsproj file is present (also uses glob sentinel support).

21 new dependency patterns across 10 ecosystems

Ecosystem Directory Sentinel
JavaScript .next next.config.js / next.config.ts
JavaScript .nuxt nuxt.config.js / nuxt.config.ts
JavaScript .angular angular.json
JavaScript .svelte-kit svelte.config.js
JavaScript .turbo turbo.json
JavaScript .yarn yarn.lock
Clojure target, .cpcache project.clj / deps.edn
ClojureScript .shadow-cljs shadow-cljs.edn
Python venv pyproject.toml
Python __pypackages__ pyproject.toml
Elixir _build mix.exs
Terraform .terraform .terraform.lock.hcl
direnv .direnv .envrc (implements #98)
OCaml/Dune _build dune-project
Zig .zig-cache, zig-out build.zig
Elm elm-stuff elm.json
Godot 4 .godot project.godot
R renv renv.lock

Also addresses #60 (Swift Package Manager) and #59 (Container/VM
directories).


Install / Uninstall Improvements

Implements #35 (props @sylver)

  • scripts/install.sh now copies the binary (rather than symlinking) with shared variables
  • scripts/uninstall.sh cleanly removes Asimov and its launchd schedule
  • Makefile gains make install and make uninstall targets
  • Common interval reference comments added to com.stevegrunwell.asimov.plist
  • make exclusions target lists current Time Machine exclusions for debugging

Documentation


On Maintainership

I commented on #99 expressing interest. I want to be transparent about my intentions here: this PR is not a bid for
ownership but a demonstration of genuine commitment. I've:

  • Deeply understood the codebase architecture before touching anything
  • Implemented community PRs faithfully, with proper attribution to their authors
  • Fixed bugs that have been open for years with reproducible test coverage
  • Modernized the dev tooling so future contributions are easier for everyone
  • Followed your existing conventions (sentinel pairs, ASIMOV_FIXED_DIRS, tmutil/mdfind mocking)

I'm happy to iterate on any part of this, split it into smaller PRs if that's easier to review, or work under @pkuczynski if that arrangement moves forward. I just want
this project to keep being useful.


Testing

brew install bats-core shellcheck
make check   # runs all tests + shellcheck

All tests pass on macOS 14 and macOS 15.

- Removed PHP-based testing infrastructure, including PHPUnit and related files.
- Introduced Bats as the new testing framework with corresponding test scripts.
- Replaced Travis CI configuration with GitHub Actions for CI/CD.
- Updated README to reflect new installation and usage instructions.
- Added Makefile for simplified commands: `make install` and `make uninstall`.
- Updated `.gitignore` to exclude new files and directories.
- Enhanced the changelog with recent changes and additions.
- Added `make install` and `make uninstall` targets for easier setup and removal.
- Introduced `scripts/uninstall.sh` for clean removal of Asimov and its launchd schedule.
- Updated `com.stevegrunwell.asimov.plist` with common interval reference comments.
- Moved the install script to `scripts/install.sh`, now copying the binary instead of symlinking.
- Revised README to reflect new installation instructions and optional plist editing.
Add tests for all 34 sentinel pairs (up from 12), plus negative cases,
~/Library skip path, mixed dependency types, nested project handling,
and find -prune verification. Split monolithic asimov.bats into
sentinels.bats and behavior.bats for better organization.
Add exclusion patterns for modern development tooling that has emerged
since the last update, covering JS frameworks, Clojure, Zig, OCaml, Elm,
Godot, R, direnv, and additional Python/Elixir/Terraform variants.

New patterns: .next, .nuxt, .angular, .svelte-kit, .turbo, .yarn,
target/project.clj, target/deps.edn, .cpcache, .shadow-cljs,
venv/pyproject.toml, __pypackages__, _build/mix.exs, .terraform,
.direnv, _build/dune-project, .zig-cache, zig-out, elm-stuff,
.godot, renv.
… badges

Tighten the intro, add a supported ecosystems table covering all 30+
patterns, add a quick start section, update badges with logos, fix the
Isaac Asimov typo, and streamline installation/contributing sections.
Add a comprehensive contributing guide covering setup, adding new
dependency patterns, commit conventions, and project structure.
Update copyright year to 2017-2026.
…upport

Add glob pattern support for sentinel definitions so wildcards like
*.xcodeproj can be used. Sentinels containing '*' use sh -c with ls -d
for glob expansion instead of test -e, with no performance impact.

Add DerivedData *.xcodeproj entry to exclude Xcode build artifacts when
an Xcode project is present alongside the DerivedData directory.

Inspired by stevegrunwell#64 (props @mdab121).
…light (#10)

Add a note explaining that asimov does not hide directories from
Spotlight indexing, with guidance on how to configure Spotlight privacy
settings separately.

Addresses stevegrunwell#90.
Exclude bin/ and obj/ directories when *.csproj (C#) or *.fsproj (F#)
project files are present, using glob sentinel patterns.

Inspired by stevegrunwell#87, props @guigomesa.
* docs(readme): clarify that asimov only affects Time Machine, not Spotlight

Add a note explaining that asimov does not hide directories from
Spotlight indexing, with guidance on how to configure Spotlight privacy
settings separately.

Addresses stevegrunwell#90.

* feat(sentinels): add .NET project build directory exclusions

Exclude bin/ and obj/ directories when *.csproj (C#) or *.fsproj (F#)
project files are present, using glob sentinel patterns.

Inspired by stevegrunwell#87, props @guigomesa.

* feat: skip already-excluded directories for faster subsequent runs

Use Spotlight metadata (mdfind) to identify directories already excluded
from Time Machine and skip them during the find traversal. Also fixes a
comment typo and removes duplicate Gradle sentinel entries.

Inspired by stevegrunwell#97, props @VladRassokhin.

* 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.
When asimov runs as root (via brew services or sudo), ~ expands to
/var/root. Now detects the console user via stat/dscl and uses their
home directory instead.

Addresses stevegrunwell#72.
Show the total count and combined size of newly excluded directories
after each run, making it easy to see the impact at a glance.

Inspired by stevegrunwell#84, props @Vadorequest.
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.
Added .cursor, .idea, and .vscode to the .gitignore file to prevent these IDE-specific and cursor-related files from being tracked in the repository.
Changed the GitHub repository links in CONTRIBUTING.md and README.md from django23 to stevegrunwell to reflect the new repository ownership.
Introduced a new --dry-run flag that allows users to see which directories would be excluded from Time Machine backups without actually modifying any settings. This feature enhances usability by providing a preview of actions before execution. Updated relevant functions and tests to support this functionality.

Also, refactored the handling of ASIMOV_ROOT to ensure correct path resolution when running as root, and made adjustments to the exclusion logic for improved performance.
Introduced `--help` and `--version` flags to the asimov script, enhancing user experience by providing usage information and version details. Updated the command-line argument parsing to handle unknown options gracefully, displaying an error message and usage instructions. Refactored the exclusion summary logic to improve clarity and maintainability.

Also, updated documentation and tests to reflect these new features.
@django23 django23 changed the title feat: implement community PRs #35/#64/#69/#84/#87/#97, fix #72/#86/#101, and migrate to Bats UpToDate version: Implemented all community PRs and Fixes. #35/#64/#69/#84/#87/#97, fix #72/#86/#101 Feb 20, 2026
@django23 django23 changed the title UpToDate version: Implemented all community PRs and Fixes. #35/#64/#69/#84/#87/#97, fix #72/#86/#101 Up to date version: Implemented all community PRs and Fixes. #35/#64/#69/#84/#87/#97, fix #72/#86/#101 Feb 20, 2026
Bumped the version to 0.4.0 and added a new function, `record_excluded_path()`, to streamline the logging of excluded paths and their sizes. Implemented validation to ensure `ASIMOV_ROOT` exists before execution, improving error handling. Updated tests to cover scenarios with spaces in project paths and the new root directory validation. Enhanced documentation to reflect these changes.
@django23
Copy link
Copy Markdown
Author

django23 commented Mar 2, 2026

@stevegrunwell do you have time to check this so we can help you?

@sraka1
Copy link
Copy Markdown

sraka1 commented Mar 2, 2026

@django23 are there build artifacts for your fork/version?

django23 added 2 commits March 2, 2026 16:50
Dropped macOS 13 from the CI test matrix as it has been retired. This change streamlines the testing process and ensures compatibility with supported macOS versions.
@django23
Copy link
Copy Markdown
Author

django23 commented Mar 2, 2026

@django23 are there build artifacts for your fork/version?

Yes @sraka1 , there is a version available at: https://github.com/django23/asimov/releases/tag/v0.4.2

More improvements are coming.

@django23 django23 changed the title Up to date version: Implemented all community PRs and Fixes. #35/#64/#69/#84/#87/#97, fix #72/#86/#101 v.0.4.0: Implemented all community PRs and Fixes. #35/#64/#69/#84/#87/#97, fix #72/#86/#101 Mar 5, 2026
@django23
Copy link
Copy Markdown
Author

django23 commented Mar 6, 2026

Even more releases:
https://github.com/django23/asimov/releases

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error (-20) while attempting to change exclusion setting. Add a summary of the size ignored Unclear how this works as a brew service?

2 participants