rig/.github/workflows/ci.yml
dan-claude-bot 26ff38390b fix: lint .github/scripts with dotglob, and assert the sweep is total
CI's shellcheck step set `globstar` and globbed `bin/* **/*.sh`. Globs do
not match dot-prefixed names without `dotglob`, so `**/` never descended
into `.github/` and two tracked scripts were linted by nothing:
`.github/scripts/labels-reconcile.sh` and `.github/scripts/release-lib.sh`.

release-lib.sh is the one that matters: it holds `changelog_section`, which
release.yml sources to build the published release body and which
test/release.sh's `changelog_armed` guard calls to decide whether main is
armed. The script that decides both what ships and whether the changelog is
safe was the script CI never read.

Measured rather than assumed: `dotglob` adds exactly those two files to
rig's line and nothing else, and `**` descending into `.git/` matches no
`.sh` on a checkout. Both files already pass `shellcheck -x`, so this
closes a hole in the net rather than fixing a defect behind it.

Paired with a class check — `comm` against `git ls-files '*.sh'` — that
fails the step naming any tracked script outside the globbed set, so the
gap cannot reopen quietly. It also covers an escape `dotglob` does not:
`globstar` declines to traverse symlinked directories.

Refs #70
2026-07-20 12:07:20 +00:00

48 lines
2.1 KiB
YAML

name: ci
on:
push:
branches: [main]
pull_request:
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: shellcheck
# -x follows the `source=SCRIPTDIR/...` directives into commands/lib/.
# globstar so a script in a new subdirectory is linted without anyone
# remembering to edit this list; bin/* covers the extensionless entrypoints.
# dotglob because globs skip dot-prefixed names: without it `**/` never
# descends into `.github/`, so `.github/scripts/*.sh` — release-lib.sh
# among them — was swept up by nothing (#70). It also makes `**`
# descend into `.git/`, which holds no tracked `.sh` on a checkout.
# The file list is printed so under-coverage shows up in the log, and
# the comm below turns under-coverage into a failure rather than a
# thing someone has to notice: every tracked `.sh` must be in the set.
run: |
shopt -s globstar dotglob
files=(bin/* **/*.sh)
printf 'shellcheck: %s\n' "${files[@]}"
uncovered=$(comm -23 <(git ls-files '*.sh' | sort) <(printf '%s\n' "${files[@]}" | sort))
if [ -n "$uncovered" ]; then
printf 'tracked .sh files the glob does not lint:\n%s\n' "$uncovered" >&2
exit 1
fi
shellcheck -x "${files[@]}"
- name: cli tests
run: bash test/cli.sh
- name: release-flow tests
run: bash test/release.sh
# Kept SEPARATE from `check` on purpose: this job pulls a Postgres image and
# stands up throwaway containers, and a slow image pull must never delay the
# fast shellcheck + cli.sh feedback above. ubuntu-latest ships Docker running
# and passwordless sudo, so test/db-integration.sh EXECUTES here (it only
# skips where Docker is absent). It is the automated proof that dump/restore
# actually round-trips, not just that the args parse.
db-integration:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: db dump/restore round-trip
run: bash test/db-integration.sh