refactor(mac): rework banner + chezmoi-fix UX, add bats harness#27
Merged
Conversation
The summary line was hard-coded with `· run 'mac' to resolve`, so it read awkwardly when echoed back from inside `mac` itself. The banner already adds its own call-to-action; let the data layer just describe state.
Today's session surfaced a real-world drift report where the banner said "2 thing(s) need attention" but the chezmoi-fix menu showed only one actionable item, alongside several smaller rough edges. This commit addresses them together since they all flow through the same banner-state-menu pipeline. - Pluralise drift counts everywhere: "1 thing" / "2 things", "1 security baseline failure" / "12 ... failures", etc. Adds a small `plural` helper in chezmoi-fix and inline word-flips in the banner block. - Compute the menu's description column width dynamically, so the arrow column stays aligned regardless of which options appear or which counts they embed. - Detect staleness: capture the cached drift total before chezmoi-fix re-runs the drift check, then print "(refreshed: banner showed N, now M)" when the in-`mac` numbers diverge from what the banner reported. The user is no longer left puzzling over the gap. - Hide "audit a clean baseline" entries when other categories are drifting, so the fix surface doesn't dilute itself with audit navigation in the common case. - Annotate the banner with "(as of Nh ago)" only when the cache is materially stale (>=1h), and TTL-gate the shell-open background refresh so a burst of new tmux panes doesn't spawn N redundant chezmoi-drift-check processes. - Add CHEZMOI_FIX_TEST_MODE so the bats harness in tests/ can exercise the menu logic against synthetic state files without needing chezmoi/brew/TTY at all.
These drift helpers had only ShellCheck and shfmt coverage in CI, so behavioural bugs (today's banner-vs-menu count mismatch, awkward pluralisation, audit-clean entries leaking into the fix surface) weren't catchable by the test suite. Adds tests/mac.bats with scenarios for clean/single/multi/error states, pluralisation, audit-entry hygiene, and arrow-column alignment. The script gains a CHEZMOI_FIX_TEST_MODE env var so the suite can run the menu logic against synthetic state files without a real chezmoi/brew/TTY. Wires it through: - Makefile: new `test-bats` target, added to the `ci` aggregate - .github/workflows/ci.yml: new `bats` job (apt-get install bats) - Brewfile.tmpl: declare bats-core so `make ci` works locally - .chezmoiignore: keep tests/ in the repo, not deployed to $HOME
Two tests were asserting the security menu entry appears with various counts, but the menu code gates that entry on `command -v chezmoi-security-audit` — which an empty test PATH defeated. Move the no-op stubs into setup() so every test exercises the post-gate code path; individual tests can override or unlink the stubs when needed. Also renames the "audit-clean entries appear when nothing is drifting" test to match what it actually proves (the hygiene rule still hides those entries under HAD_ERROR), since the truly-clean case is unreachable — chezmoi-fix exits at "Nothing to fix" before the menu.
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
Reworks the
mac(=chezmoi-fix) remediation surface and the drift banner indot_zshrcafter a real-world session exposed both a correctness bug and a handful of UX rough edges. Also adds a bats-core test harness so the same class of bug can't recur silently.Plan that drove this:
mac/ drift-banner review — focused scope was themacentry point only (not the wider zsh environment), output as findings + prioritised fix list, plus thin tests wired into CI.What changed
fix(drift-check)— drop· run 'mac' to resolvefrom the summary line. The banner already adds its own CTA, and the suffix read awkwardly when echoed back from insidemac.refactor(mac)— banner +chezmoi-fixUX:1 thing/2 things,1 security baseline failure/12 ... failures, etc.). Newpluralhelper inchezmoi-fix; inline word-flips in the zsh banner block.OPTSentries switched toid|description|command.chezmoi-fixnow captures the cached drift total before re-running the drift check, then prints(refreshed: banner showed N, now M)when they differ. Fixes the "banner said 2, menu shows 1" mystery surfaced today.(as of Nh ago)only when the cache is materially stale (≥ 1h).chezmoi-drift-checkprocesses.CHEZMOI_FIX_TEST_MODEenv var lets bats exercise menu logic without chezmoi/brew/TTY.test(mac)— bats harness:tests/mac.batscovers clean/singular/plural/error/inbox/alignment/audit-hygiene scenarios.Makefile: newtest-batstarget, added to theciaggregate..github/workflows/ci.yml: newbatsjob (apt-get installs bats on ubuntu-latest).Brewfile.tmpl: declarebats-coresomake ciworks locally..chezmoiignore: keeptests/in the repo, not deployed to\$HOME.Before / after sample
Before (real session today):
```
chezmoi: 2 thing(s) need attention — run 'mac'
…
drift: security: 1 · run 'mac' to resolve
```
After (synthetic
home=1, brew_extra=1, security=1, inbox=1):```
drift: home: 1, brew-extra: 1, security: 1
brew inbox: 1 event pending
```
Test plan