Skip to content

feat(scripts): add diff-check.sh to detect repo ↔ deploy-dir drift#10

Merged
mlorentedev merged 1 commit into
mainfrom
feat/diff-check
May 13, 2026
Merged

feat(scripts): add diff-check.sh to detect repo ↔ deploy-dir drift#10
mlorentedev merged 1 commit into
mainfrom
feat/diff-check

Conversation

@mlorentedev
Copy link
Copy Markdown
Owner

Closes the dotfiles backlog item "Dotfiles diff/drift detection" (P2 in vault).

Why now

Two recent incidents motivated this:

  1. tmux clipboard work (PR feat(tmux): copy selection to system clipboard via xclip #6) — config changes in the repo were invisible to running tmux because ~/.dotfiles/tmux.conf (the middle-layer copy) hadn't been refreshed via ./setup-linux.sh. Vault lesson captured.
  2. issue secrets_rotate doesn't update the encrypted file #7secrets_rotate was thought to silently fail; it had actually worked, but the same kind of "did my edit really propagate?" confusion. PR fix(secrets): keep env, deployed, and repo in sync after every mutation #9 fixed the env-vs-disk drift; this PR makes the file-vs-deploy drift visible too.

What it does

scripts/diff-check.sh walks files tracked by git in the repo, compares each one against its counterpart in ~/.dotfiles/, and reports any divergences.

  • Filters to an explicit allowlist mirroring what setup-linux.sh actually deploys (versions.conf, .zshrc, .bashrc, .profile, .gitconfig, tmux.conf, .zsh/, ssh/, scripts/, sensitive/). Legacy cruft in the deploy-dir from older sync mechanisms is ignored — no noise.
  • Exit codes: 0 (clean), 1 (drift), 2 (setup error)
  • --verbose: shows the per-file diff
  • Wired as healthcheck section 10/10 (existing 9 sections renumbered to /10)
  • Alias dch added to .zsh/aliases.zsh

Test plan

  • ~/.local/bin/bats tests/*.bats → all green (11 new tests for diff-check, healthcheck section count test updated)
  • ~/.local/bin/shellcheck scripts/diff-check.sh → clean
  • Manual smoke against current state → reports .bashrc, .zshrc, tmux.conf as drifted (expected: I haven't run ./setup-linux.sh after recent edits)
  • After merge: run ./setup-linux.sh, then dch should report no drift

scripts/diff-check.sh compares files tracked in the repo against their
counterparts in ~/.dotfiles (the deploy-dir middle layer copied by
setup-linux.sh). When a repo file is edited without re-running setup, the
deploy-dir copy goes stale silently — every shell still reads the old
version. This script makes that drift visible.

Filters to the explicit allowlist of paths setup-linux.sh actually deploys
(versions.conf, .zshrc/.bashrc/.profile/.gitconfig/tmux.conf, .zsh/, ssh/,
scripts/, sensitive/) so legacy cruft in the deploy-dir from older sync
mechanisms doesn't pollute the output.

- Exit codes: 0 clean, 1 drift detected, 2 setup error
- --verbose shows the per-file diff
- Auto-resolves DOTFILES_REPO_DIR / DOTFILES_DIR with sensible defaults
- Wired into healthcheck.sh as section 10/10
- Alias `dch` added in .zsh/aliases.zsh

Motivation reinforced this week by two incidents: (1) tmux clipboard
changes invisible until setup-linux.sh ran (the original lesson), and
(2) confusion about whether secrets_rotate had updated the .age file
when in reality it had — same drift class, different surface.

Tests: 11 new bats covering exit codes, --verbose, allowlist filtering,
fixture isolation. Full suite green, shellcheck clean.
@mlorentedev mlorentedev merged commit fd71a24 into main May 13, 2026
5 checks passed
@mlorentedev mlorentedev deleted the feat/diff-check branch May 17, 2026 02:40
mlorentedev added a commit that referenced this pull request May 21, 2026
…hcheck sec 12 (#82)

Windows port of scripts/diff-check.sh (Linux side shipped 2026-05-12, PR #10).
Closes the permanent `SKIP: diff-check.ps1 not implemented` in
healthcheck.ps1 section 12/12 -- the last WIN-001-era SKIP introduced
by PR #71.

Changes:

1. scripts/diff-check.ps1 (new, ~120 LOC): faithful PowerShell port of
   the bash sibling. Same allowlist (versions.conf, .zshrc, .bashrc,
   .profile, .gitconfig, tmux.conf, .zsh/, ssh/, scripts/, sensitive/);
   same -VerboseOutput/-Help switches; same exit codes (0/1/2).
   Comparison: `Get-FileHash SHA256` byte-equality (PowerShell
   equivalent of bash `cmp -s`). Linux-only items implicit-skip on
   Windows via Test-Path guards.

2. setup-windows.ps1: Copy-Item line for diff-check.ps1 next to the
   doctor.ps1 deploy block. Mirrors the deploy idiom shipped in PR #74
   (healthcheck.ps1 deploy).

3. scripts/healthcheck.ps1 sec 12: replace the SKIP block with an
   actual `pwsh -NoProfile -File diff-check.ps1` invocation. Switch
   on exit code -> Write-Pass (no drift) / Write-Fail (drift). Same
   non-fatal pattern as sec 8d auto-wire from WIN-001.

4. powershell/profile.ps1: new `dch` function wrapping the script
   (Set-Alias cannot forward -VerboseOutput/-Help, so function-form
   is required). Mirrors the Linux `dch` bash function shipped in
   PR #10.

5. tests/healthcheck-ps1.bats: replace the "sec 12 emits SKIP with
   REFACTOR-003 reference" assert with "sec 12 invokes diff-check.ps1"
   (the post-impl assertion).

6. tests/setup-windows.bats: 2 new asserts for deploy line + dch
   function declaration in profile.ps1.

Empirical validation locally (Windows daily-driver):
- `pwsh -NoProfile -File diff-check.ps1` -> exit 0, "Checked: 33
  managed files (ignored 158 tracked but not deployed); No drift
  detected -- repo and deploy-dir agree".
- All 4 .ps1 files PSScriptAnalyzer clean (settings.psd1 + Error,Warning).
- AST parse clean.
- ASCII-only check passes (no em-dash regression).

Spec at specs/REFACTOR-003-diff-check-ps1/ (proposal + tasks + verification).

Lesson candidate (post-merge): "Get-FileHash SHA256 is the right
PowerShell idiom for byte-equality drift detection -- faster than
Compare-Object on file contents, deterministic, matches bash `cmp -s`
semantics."
mlorentedev added a commit that referenced this pull request May 22, 2026
…nly writer of rc files (#93)

* fix(BUG-024): bake PATH/alias lines into repo .bashrc/.zshrc to kill drift false-positive

setup-linux.sh appended 3 lines (opencode PATH, project-init alias,
dotfiles/scripts PATH) to ~/.bashrc and ~/.zshrc via ensure_line_in_file
*after* symlinking them to the repo. Since the rc files are symlinks
into the deploy-dir, the writes landed in the deploy-dir copies and
made diff-check.sh (PR #10) report drift on every fresh setup.

Root fix: repo source is now the only writer. The 3 lines are baked
into .bashrc/.zshrc directly; the corresponding ensure_line_in_file
blocks are removed from setup-linux.sh (L431-433, L903-905, L922-924).
Both rc files also gained the trailing newline they were missing.

Test: tests/opencode.bats #5 rewritten to assert the new invariant
(repo-as-SSOT) and to forbid the old ensure_line_in_file pattern.

* fix(BUG-024): update verify-setup.bats #50/#52 to match SSOT (quoted form)

verify-setup.bats Section 9 grepped for the un-quoted scripts PATH line
that ensure_line_in_file used to append. Now that the line is baked into
the repo .bashrc/.zshrc with double quotes around $HOME (safer + matches
the opencode PATH line's style), the old assertion no longer matched →
integration CI fail (#50, #52).

Updated:
- Section header: "ensure_line_in_file side effects" → "rc file SSOT
  (lines baked into repo .bashrc/.zshrc — BUG-024)" since the mechanism
  is no longer setup-time mutation.
- Test #50/#52 grep pattern now requires the double-quoted form that
  matches the repo source exactly (strict SSOT assertion, no looseness).
- Test names: "X added to .Yshrc" → "X in .Yshrc" since nothing is
  being "added" at setup time anymore.
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.

1 participant