ci(gitea): gate scripts/ with shellcheck and the selftests

This repo had eight scripts, six selftest suites and nothing that ran any of
them. It is a composite action consumed at `@v1` — a moving tag — by three
repos' CI, so a bad edit here reaches zemyna, emowheel and lublub at once and
is discovered by whichever of them builds next.

One job: `shellcheck -x --source-path=scripts scripts/*.sh`, then
`bash scripts/selftest.sh`. `-x` follows the `. cache-lib.sh` every script
sources, which is where most of the logic being checked lives; without it
shellcheck reports SC1091 and analyses each file with a hole in it.

The full suite, not `--fast`: `hardlink-clone-selftest.sh` and
`restore-mtimes-selftest.sh` are the two that drive a real Cargo rather than a
fixture, and selftest.sh's own header says `--fast` is for iterating, not for
signing off a change. So the job installs a stable toolchain. The scratch
workspaces they build use path dependencies only, so nothing reaches
crates.io, and the job references no credentials at all.

Modelled on daniel/gitdan's workflow, including the two clauses of the
draft-skip guard (`github.event_name != 'pull_request' ||
!github.event.pull_request.draft` — the first is what stops the second from
also skipping pushes to `main`, where there is no `pull_request` context) and
the per-commit concurrency group for `push`.

No `container.volumes:` entry, deliberately: the suites want a cold target dir
every run, since "did this rebuild?" is exactly what restore-mtimes-selftest
asks. This job takes no share of the shared CI cache disk budget.

Turning the gate on surfaced three pre-existing findings, all fixed here
rather than suppressed or waived:

- `restore-mtimes-selftest.sh` SC2038: `find | xargs touch` -> `-print0 | xargs -0`.
- `hardlink-clone-selftest.sh` SC2295: `${f#$base_fix/}` -> `${f#"$base_fix"/}`.
- `cache-lib.sh` SC2016: a per-line disable with the rationale. The single
  quotes are load-bearing — the program is for the inner shell, where `$f` is
  its loop variable, `$rc` its accumulator and `$$` its pid — so this is the
  documented-false-positive case, not a stopgap.
This commit is contained in:
2026-08-26 12:31:50 -05:00
parent 1aca90b461
commit f76789358d
5 changed files with 104 additions and 3 deletions
+3
View File
@@ -210,6 +210,9 @@ _unshare_files() {
# The inner shell propagates a failure of any individual copy-and-rename out
# through xargs (which exits 123 if any invocation exits 1-125), so a
# partially-unshared tree is reported rather than silently accepted.
# shellcheck disable=SC2016 # the quoted program is for the INNER shell: $f
# is its loop variable, $rc its accumulator and $$ its pid. Expanding any of
# them here is what the single quotes exist to prevent.
find "$@" -links +1 -print0 2>/dev/null |
xargs -0 -r -n 64 bash -c 'rc=0; for f; do cp -p -- "$f" "$f.unshare.$$" && mv -f -- "$f.unshare.$$" "$f" || rc=1; done; exit $rc' _
}
+1 -1
View File
@@ -133,7 +133,7 @@ hardlink_clone_into "$base_fix" "$clone_fix" "selftest" || fail "hardlink_clone_
# rebuild would prove nothing — the rebuild replaces those files anyway.
shared=0; unshared=0
while IFS= read -r f; do
rel="${f#$base_fix/}"
rel="${f#"$base_fix"/}"
[ -e "$clone_fix/$rel" ] || continue
if [ "$(stat -c '%i' "$f")" = "$(stat -c '%i' "$clone_fix/$rel")" ]; then
case "$rel" in
+1 -1
View File
@@ -122,7 +122,7 @@ assert_log_lacks() {
# every run — simulate that before each restore-mtimes.sh pass, exactly as
# CI would see it, so this test exercises the script the same way CI does.
stamp_checkout_now() {
find . -path ./.git -prune -o -type f -print | xargs touch
find . -path ./.git -prune -o -type f -print0 | xargs -0 touch
}
echo "=== building scratch workspace ==="