ci: gate the release on shell lint, from one shared script
Lint / hadolint (push) Successful in 10s
Lint / actionlint (push) Successful in 17s
Publish Docker Image / lint-gate (push) Successful in 15s
Publish Docker Image / resolve-versions (push) Successful in 9s
Publish Docker Image / base-decide (push) Successful in 16s
Publish Docker Image / build-base (push) Has been skipped
Publish Docker Image / smoke-studio (push) Successful in 5m9s
Publish Docker Image / smoke (push) Successful in 7m39s
Publish Docker Image / build-variant-studio (push) Successful in 17m15s
Publish Docker Image / build-variant (push) Successful in 17m58s
Publish Docker Image / update-description (push) Successful in 8s
Publish Docker Image / promote-base-latest (push) Successful in 12s
Lint / hadolint (push) Successful in 10s
Lint / actionlint (push) Successful in 17s
Publish Docker Image / lint-gate (push) Successful in 15s
Publish Docker Image / resolve-versions (push) Successful in 9s
Publish Docker Image / base-decide (push) Successful in 16s
Publish Docker Image / build-base (push) Has been skipped
Publish Docker Image / smoke-studio (push) Successful in 5m9s
Publish Docker Image / smoke (push) Successful in 7m39s
Publish Docker Image / build-variant-studio (push) Successful in 17m15s
Publish Docker Image / build-variant (push) Successful in 17m58s
Publish Docker Image / update-description (push) Successful in 8s
Publish Docker Image / promote-base-latest (push) Successful in 12s
v1.8.14's first attempt spent ~46 minutes building a base image for a tree whose own lint had been failing for 24 hours. shellcheck had already flagged the defect (SC2289, severity error) on the push that introduced it; the lint workflow went red at run 186 and nobody read it. lint.yml deliberately skips tag pushes and its reasoning is sound -- the tagged tree was already linted on main, and a tag-ref lint run sorts above the publish run, making a release look finished before anything ships. The missing invariant was never "lint the tag". It was "do not RELEASE a tree whose lint failed", and only a job inside the publish workflow can enforce that. So: extract the shell-lint logic from lint.yml into scripts/lint-shell.sh and call it from both places, then add a lint-gate job that resolve-versions depends on. resolve-versions is the graph root, so gating it gates everything. Cost is ~40 s at the front of a release; the alternative already cost fifty minutes. Extracted rather than copied on purpose. A second copy of a check is the drift this repo keeps paying for -- the same evening produced a skillset mirror that had sat 9579 B behind its upstream through two consecutive edits. The script adds one behaviour the inline version lacked: if shellcheck is not installed it exits 2 rather than silently finding nothing, inheriting the existing "a gate that cannot run must not pass" rule from hooks/pre-commit in the skillset repo. Without that, reordering the install step away would turn the gate into a green tick over zero checks. Verified locally with a stubbed shellcheck (the real binary is not in the devbox), five cases, each with its expectation stated first: absent shellcheck -> rc=2; stub pass -> rc=0 and a non-zero file count; stub fail -> rc=1; a deliberately unterminated `if` planted in scripts/ -> rc=1 via the bash -n half, naming the file; removal -> rc=0 again. Discovery cross-checks against CI's own number: the inline version reported 12 files, the extracted one reports 13, the difference being lint-shell.sh itself. YAML re-parsed (10 jobs, was 9) with an assertion that resolve-versions needs lint-gate, and the repo's check-workflow-shell.sh guard still passes.
This commit is contained in:
@@ -157,7 +157,41 @@ jobs:
|
||||
# buildcache silently reuses the layer from whatever pi version was
|
||||
# current when the cache was first populated. Same class of bug as
|
||||
# pi-devbox v0.74.0..v0.75.5 (fixed in v0.75.5b 2026-05-23).
|
||||
# ── release gate ──────────────────────────────────────────────
|
||||
# Refuse to spend a base build on a tree whose own shell scripts do not lint.
|
||||
#
|
||||
# v1.8.14's first attempt is why this exists. smoke and smoke-studio both failed
|
||||
# at scripts/smoke-test.sh:770 AFTER build-base had already spent ~46 minutes,
|
||||
# on a defect shellcheck had flagged as SC2289 (severity error) a day earlier:
|
||||
# the lint workflow went red on the very push that introduced it (run 186) and
|
||||
# stayed red for runs 187 and 188, unread.
|
||||
#
|
||||
# lint.yml deliberately does not run on tag pushes, and its reasoning is sound
|
||||
# (the tagged tree was already linted on main; a tag-ref lint run sorts above
|
||||
# the publish run and makes a release look finished before anything ships). The
|
||||
# missing invariant was never "lint the tag" -- it was "do not RELEASE a tree
|
||||
# whose lint failed", and only a job inside THIS workflow can enforce that.
|
||||
#
|
||||
# ~40 s, ahead of everything expensive, and it runs scripts/lint-shell.sh --
|
||||
# the same file lint.yml calls, not a second copy that drifts.
|
||||
lint-gate:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install shellcheck
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends shellcheck
|
||||
|
||||
- name: "Shellcheck + syntax-check repository scripts (severity: error)"
|
||||
run: bash scripts/lint-shell.sh
|
||||
|
||||
resolve-versions:
|
||||
# Gated: a defective tree must not reach a 46-minute base build.
|
||||
needs: [lint-gate]
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
|
||||
Reference in New Issue
Block a user