fix: lint .github/scripts with dotglob, and assert the sweep is total #71
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:bootstrap
scope:coolify
scope:db
scope:docs
scope:drill
scope:installer
scope:labels
scope:platform
scope:runner
scope:users
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/rig#71
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/shellcheck-dotglob"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The defect
CI's shellcheck step swept
bin/* **/*.shundershopt -s globstar.globstarmakes**descend into subdirectories, but globs do not match dot-prefixed names withoutdotglob— so**/never descended into.github/, and two tracked scripts were linted by nothing:release-lib.shis why this is worth fixing here rather than filing it as tidy-up. It holdschangelog_section— the extractionrelease.ymlsources to build the published release body, and the same functiontest/release.sh'schangelog_armedguard (#66) calls to decide whethermainis armed. The script that decides both what gets published and whether the changelog is safe was the one shellcheck never saw. It is also the file most likely to be edited next, since #66's fix landed in it.What is now linted
shopt -s globstar dotglob. Measured on rig's own line rather than assumed —dotglobadds exactly those two files and nothing else:The other thing
dotglobchanges is that**now descends into.git/. Checked against a realactions/checkout-shaped clone (this branch was developed in a worktree, where.gitis a file and would have hidden the question): zero.shmatches — git's stock hooks are*.sample.bin/*gains nothing;bin/holds onlyrig.Did it pass as-is?
Yes —
shellcheck -xon both files is clean, exit 0. This closes a hole in the net; it is not a bug fix wearing a lint fix's clothes. The gap was that nothing would catch a regression inrelease-lib.sh, not that anything was wrong in it.The class check
dotglobfixes the instance. The assertion fixes the class — the step now fails, naming names, if any tracked.shfalls outside the globbed set:It runs before
shellcheck, so a coverage hole is reported on its own terms instead of being read as a passing lint. The comparison is one-way on purpose: every tracked.shmust be in the set, while the set may hold more (bin/rigis extensionless and matches no*.sh).Worth noting it catches an escape
dotglobdoes not close:globstardeclines to traverse symlinked directories, so a tracked script behind a symlinked dir would still miss the glob. Verified by negative test — the guard fires and names the file. No such path exists today; the point is that it can't be introduced silently.Verification
shellcheck— the exact CI block replayed locally, all 24 files, exit 0 (was 22)bash test/cli.sh— 400 passed, 0 failedbash test/release.sh— 68 passed, 0 failedChangelog entry added under
## Unreleased.Closes #70
Verdict: Approve
Reviewed at head
173456d. Every claim in the PR body was re-verified independently in a worktree; all of them held.Mechanism (
.github/workflows/ci.yml:23-31)shopt -s globstar dotglobis the right minimal fix — it keeps the glob-based sweep (new subdirectories are picked up with no list to maintain) instead of switching to a hand-kept path list, anddotglobis exactly the missing bit:**/never descends into dot-dirs without it.Sweep totality — measured, not assumed
git ls-files '*.sh'in the worktree: 23 tracked scripts, including both.github/scripts/labels-reconcile.shand.github/scripts/release-lib.sh.files=(bin/* **/*.sh)underglobstar dotglobenumerates 24 files = those 23 plusbin/rig. Exact match, nothing missing, nothing spurious.bin/rigis the only extensionless shell script, sobin/*closes that side..shplanted in a brand-new dot-dir (.fakedot/new.sh) is now matched, so the fix covers the class of dot-dirs, not just.github/.The totality assertion (
.github/workflows/ci.yml:26-30)Compares one-way against
git ls-files '*.sh'— correct baseline (tracked files, immune to working-tree noise) and correct direction (the glob set may contain extras likebin/rig; every tracked.shmust be inside it). It runs beforeshellcheck, so a coverage hole fails on its own terms. Negative tests I ran:dotglob) + new assertion: fires and names exactly the two.github/scriptsfiles — the original defect can no longer land silently.git update-index --cacheinfo):globstarindeed declines to traverse it, and the assertion catches it (linkdir/hidden.shreported). The PR body's claim about this escape hatch is accurate.Residual drift surface is small and acknowledged by design: a tracked extensionless script outside
bin/(or a.bashfile) would be neither globbed nor asserted. That is the same scope the sweep always had, and the comment at.github/workflows/ci.yml:14documents thebin/*convention — not blocking, just the one thing the assertion does not fence.Do the newly-linted scripts pass?
Yes.
shellcheck -x0.10.0 on both.github/scripts/labels-reconcile.shand.github/scripts/release-lib.sh: clean, exit 0. The full 24-file CI block replayed verbatim (including thecommguard, underbash -eas Actions runs it): exit 0. No latent bug surfaced in the newly covered scripts — this is genuinely a net fix, not a bug fix in disguise, exactly as the PR body states..git/descentOn a real checkout
dotglobalso sends**into.git/; stock hooks are*.sampleso no.shmatches today, and the one-waycommis unaffected by extras. Worth knowing that a stray.shunder.git/would get shellchecked (and could theoretically fail the step), but that is a loud failure, not a silent gap — acceptable.Tests
bash test/cli.sh— 400 passed, 0 failedbash test/release.sh— 68 passed, 0 failedCHANGELOG.mdentry under## Unreleasedis accurate to the change.Sibling note: this follows the same shape as the box-side fix (dotglob + a
git ls-filestotality check) while enumerating rig's own file set (bin/* **/*.sh) rather than copying the patch verbatim, which is what #70 asked for. No material pattern divergence to flag.— automated review by
claude-bot-andresmgsl· heavy-duty-review-botVerdict: Approve — I agree with this as-is.
dotglob+ uncovered-setcommagainstgit ls-files '*.sh'closes the.github/scriptsblind spot (#70) includingrelease-lib.sh. CHANGELOG under Unreleased preserves## 0.2.0. CI green. No blockers.Verdict: I agree with everything and have no additional feedback.
Round passed — three approvals, no blockers. Thanks all.
Handing to the maintainer: requesting @danmt for the human round.
Rebased onto
main— the conflict was #79/#80/#83 landing their## Unreleasedentries at the same position this PR writes to.Resolution: both, neither dropped. The two sides were different sections —
### Changed(the role taxonomy and--root-doorentries now on main) and### Fixed(this PR's dotglob entry). They compose rather than compete, so the resolution was removing the markers and keeping both blocks in that order. No prose from either side was edited.The code half of the rebase was clean:
.github/workflows/ci.ymldid not conflict, and this PR's diff against the new main is unchanged in substance —2 files changed, 31 insertions(+), 2 deletions(-).Re-verified on the rebased head:
That shellcheck line is CI's exact block replayed locally (
shopt -s globstar; files=(bin/* **/*.sh)), which matters more than usual here — this PR exists to widen that glob, and main has grown files since it was written. 22 files, still clean. CI on the new head is green acrosscheck,scope,db-integrationandreconcile.Re-requesting all three bots. Your approvals were bound to the pre-rebase head, and the reconciler counts only head-current ones — leaving them stale would let this drift to @danmt looking reviewed when the tree it was reviewed against no longer exists. It is a rebase-only change, so the round should be short.
Verdict: Approve — I agree with this as-is.
Tip
26ff383is the same fix rebased onto main after #79/#80/#83 landed Unreleased entries. Prior approve was at173456d.What holds
shopt -s globstar dotglobis still the right minimal fix so**/descends into.github/and catchesrelease-lib.sh/labels-reconcile.sh.comm -23againstgit ls-files '*.sh'still fails the step on under-coverage before shellcheck, so the hole cannot reopen quietly (including symlink escapes globstar will not follow).### Changedtaxonomy /--root-doormaterial and this PR's### Fixeddotglob entry; shipped## 0.2.0intact. ci.yml half did not conflict.No remaining blockers. CI green on the rebased head.
Verdict: I agree with everything and have no additional feedback.
✅ Approved — I agree with all of this, no concerns.
Re-review of head
26ff383after the rebase onto main. The code half is byte-for-byte what I approved at173456d—shopt -s globstar dotglobplus thecomm -23coverage assertion in.github/workflows/ci.yml:23-31. The changelog conflict resolution is correct: the### Changedentries from #79/#80/#83 and this PR's### Fixedblock are distinct sections that compose; nothing was dropped or reworded. CI is green on the new head across all four jobs. Nothing new to add.— automated review by
claude-bot-andresmgsl· heavy-duty-review-bot