Atomic conflict pre-flight, --force hardening, and release tooling#1
Merged
Conversation
Add `# shellcheck shell=bash` directives to the sourced modules and condition plugins (they have no shebang, so shellcheck couldn't infer the dialect), remove a dead `candidate_set` map in fold.sh, and add scoped, justified disables for intentional glob matches (filter.sh) and nameref indirection / cross-module use (fold.sh, version.sh). No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three related safety changes to the core engine: 1. Atomic all-or-nothing operations. main() now runs every stow/unstow/ restow through a mutation-free pre-flight pass (_stow_sh_preflight + dry-run) that detects conflicts across all packages; if any unresolved conflict exists it aborts with ZERO changes, then applies for real only when the pre-flight is clean. Previously a conflict mid-run left a partially-applied tree. This matches GNU Stow's core guarantee. 2. Harden --force. __create_link now refuses to delete a path that resolves outside the target directory, and refuses to recursively delete a real directory (rm -rf -> rm -f). Both guards run before the dry-run return so they are caught during pre-flight rather than half-applied. 3. Remove --defer/--override. They were parsed and documented but never read anywhere — accepting them silently did nothing. They now error as unknown options instead of lying. BREAKING CHANGE: a conflict now aborts the entire run instead of applying what it can; --force no longer overwrites real directories; --defer and --override are removed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bats runs setup_file() in a separate shell from setup() and the tests, so the non-exported TEST_REPO was empty there — every filter test failed with "cd: null directory" on modern bats (1.13). Export it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The only workflow ran at tag time, so regressions on main were invisible until a release. Add a CI workflow that runs shellcheck and the bats suite on every push and PR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- make lint (shellcheck, required; shfmt -d, advisory), make fmt, make bundle, make clean; release target now gates on lint. - scripts/bundle.sh assembles all modules + built-in conditions into one self-contained stow.sh executable. - release.yml now lints, tests, and attaches three artifacts: the single-file bundle, the source tarball, and SHA256SUMS. - .gitignore now ignores /dist/. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Installed by `make hooks`. Runs shellcheck then the bats suite and blocks the commit on failure; missing tools are skipped with a warning (CI is the backstop). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…methods - README: add a newcomer "What is this?" intro with a before/after example; replace "Everything GNU Stow does" with an honest "Differences from GNU Stow" table; document the single-file install and atomic behavior. - CONTRIBUTING/AGENTS: document the pre-commit hook, lint/bundle workflow, the atomic pre-flight architecture, scripts/, and corrected test counts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Hardens stow.sh's safety model to match GNU Stow's core guarantee, makes the test suite actually gate changes, and adds real release artifacts.
Safety (the headline)
main()runs every stow/unstow/restow through a mutation-free pre-flight that detects conflicts across all packages; if any unresolved conflict exists it aborts with zero changes, then applies for real only when the pre-flight is clean. Previously a conflict mid-run left a half-applied tree.--forcehardening. No longer recursively deletes a real directory or anything resolving outside the target dir (rm -rf→rm -f). Guards run during pre-flight, so a--forceconflict aborts cleanly instead of half-applying.--defer/--override. They were parsed and documented but never read anywhere — accepting them did nothing. They now error as unknown options.Making tests matter
ci.yml: shellcheck + bats on every push/PR (previously tests only ran at tag time).filter.batsbug (non-exportedTEST_REPO) that failed all 32 filter tests on modern bats.hooks/pre-commitruns shellcheck + tests.Release artifacts
scripts/bundle.sh(+make bundle) assembles all modules and built-in conditions into one self-containedstow.sh.release.ymlnow lints, tests, and attaches: single-file bundle, source tarball, andSHA256SUMS.Docs
Breaking changes
--forceno longer overwrites real directories.--defer/--overrideremoved.Verification
make lintpasses; 276/276 tests pass.🤖 Generated with Claude Code