Skip to content

ci: fix all actionlint/shellcheck findings, wire actionlint into Lint (#247) - #271

Merged
cdeust merged 1 commit into
mainfrom
fix-actionlint-wiring-247
Jul 30, 2026
Merged

ci: fix all actionlint/shellcheck findings, wire actionlint into Lint (#247)#271
cdeust merged 1 commit into
mainfrom
fix-actionlint-wiring-247

Conversation

@cdeust

@cdeust cdeust commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #247. actionlint was never wired into any CI gate, so its findings
across .github/workflows/ were latent and undetected. This PR fixes every
finding actionlint reports against the repo (15 total across 4 files — the
issue quoted 5; the other 10 were already present in ci.yml and
publish-ccplugins.yml at the commit the issue cites, f606c37, confirmed by
re-running actionlint against that exact commit) and wires actionlint
into ci.yml's Lint job so this class of defect is gated from now on.

Acceptance criteria (issue #247):

  1. Each of the 5 findings named in the issue is fixed (not documented-away) — done.
  2. SC2034 (i appears unused) in release.yml resolved by wiring the
    counter into a real use (attempt-count message + a fail-loud exit when the
    retry loop exhausts every attempt), not by renaming it to _ — done.
  3. actionlint runs clean on .github/workflows/ — verified locally
    (actionlint exit 0) and gated in CI going forward.
  4. actionlint wired into CI's Lint job, pinned version — done
    (ci.yml, "Install actionlint (pinned, checksum-verified)" +
    "Check workflow files (actionlint + shellcheck)").
  5. release.yml changes proven by one real run, not by reading the YAML —
    see below.

What changed and why (root cause, not a band-aid)

  • SC2015 (×3) + SC2034 (×1), ci.yml + release.yml postgres-setup step
    (byte-identical block in both files): the cmd && break || sleep N retry
    idiom is not just ambiguous shell (shellcheck's literal complaint) — it
    silently swallows exhaustion. If all N attempts fail, the for loop's last
    executed statement is sleep, which succeeds, so the loop's own exit status
    is 0 and the real failure only surfaces later as an unrelated, confusing
    error further down the step. Rewritten as explicit if/then/break with the
    attempt counter now read (an echo on each failed attempt) and an explicit
    exit 1 with a message when a loop exhausts every attempt — matching this
    repo's own established retry-with-backoff-fail-loudly convention (used for
    the HF/FlashRank model pre-download steps in ci.yml). Verified with a
    standalone bash harness exercising both the success and full-exhaustion
    paths under set -euo pipefail.
  • SC2086, release.yml changelog step: ${PREV_TAG}..HEAD unquoted →
    quoted.
  • SC2086 (×3), publish-ccplugins.yml: $UPSTREAM unquoted at three call
    sites → quoted.
  • SC2046, publish-ccplugins.yml: unquoted $(date ...) embedded in
    echo manual-$(date ...) → quoted.
  • SC2129 (×2), publish-ccplugins.yml + sync-ccplugins-fork.yml
    Summary steps
    : individual >> "$GITHUB_STEP_SUMMARY" redirects per
    echo → one { ...; } >> file block.

Wiring (criterion 4)

ci.yml's Lint job downloads actionlint 1.7.12 from its GitHub Release,
verifies the download against the release's own linux_amd64 sha256 (recomputed
locally from the published checksums.txt, not trusted blindly — see the
# source: comment at the install step), then runs actionlint -color.
Pinned by release tag + checksum rather than go install @latest or
curl | bash, consistent with this repo's existing supply-chain-hardening
stance (release.yml's attestation/SBOM/hash-pinning discipline).

Verification (criterion 5)

The postgres-setup fix is identical in ci.yml and release.yml; ci.yml's
test job runs it on every push/PR (this PR's own CI run proves it, across
the Python 3.10–3.13 matrix) — a real execution, not a YAML read.

release.yml's own test job only triggers on a v* tag push, so this PR
adds workflow_dispatch purely as a verification lane: every
release-producing job (github-release, build, sbom, publish-pypi) is
now gated to startsWith(github.ref, 'refs/tags/'), so a dispatched run
executes test (proving the fix) and stops there — it cannot cut a release,
publish to PyPI, or trigger the downstream ccplugins-fork publish workflow.
I dispatched a run on this branch after opening this PR; see the Actions tab
for release.yml / workflow_dispatch on fix-actionlint-wiring-247.

Completion Ledger

Path introduced Test / evidence
ci.yml postgres retry: apt-get update loop, structured if/break Bash harness success+exhaustion paths (both pass); real execution via this PR's own test job (4-way matrix)
ci.yml postgres retry: apt-get install loop, structured if/break Same as above
ci.yml postgres retry: pg_isready loop, structured if/break Same as above
Exhaustion exit 1 arm (×3 loops, ×2 files) Bash harness test_exhaustion_path — asserts non-empty $ok guard triggers the message + exit 1 under set -euo pipefail
ci.yml actionlint install + checksum verify Reproduced locally: downloaded the same release asset, sha256sum -c against the hardcoded checksum passes, tar extracts a valid linux/amd64 ELF
ci.yml actionlint run step actionlint exit 0 on the fixed tree (was exit 1, 15 findings, before this PR)
release.yml changelog SC2086 fix Bash equivalent executed locally: TAG=manual-<date> — single well-formed token, regex-verified
publish-ccplugins.yml SC2046/SC2086/SC2129 fixes actionlint clean; SC2129 redirect-block behavior verified with a standalone bash snippet (writes the same lines to a temp file)
sync-ccplugins-fork.yml SC2129 fix Same redirect-block verification
release.yml workflow_dispatch + tag-only gates on 4 jobs actionlint clean (no expression/job-graph errors); dispatched run on this branch (see Actions tab) — test runs, the 4 gated jobs report skipped

Boy-scout check

No other defects were seen in the touched files beyond the 15 actionlint
findings themselves, which are exactly what this PR closes.

actionlint (which shells out to shellcheck per `run:` block) had never been
run in any CI gate, so 15 findings across ci.yml, release.yml,
publish-ccplugins.yml and sync-ccplugins-fork.yml were latent and undetected
(the issue's own reproduction quoted 5 of them; the other 10 were already
present in ci.yml and publish-ccplugins.yml at the same commit — verified by
re-running actionlint against f606c37 directly). actionlint's acceptance
criterion 3 ("runs clean on .github/workflows/") requires all of them fixed,
not only the 5 named in the title, since the newly-wired gate would fail on
day one otherwise.

Root cause and fix per finding:

- SC2015 (3x, ci.yml + release.yml postgres-setup step) and SC2034 (1x each):
  the `A && break || sleep` retry idiom is ambiguous AND silently swallows
  exhaustion (all N attempts fail -> the for loop still exits 0, since the
  last statement executed was `sleep`, and the failure surfaces later as an
  opaque, unrelated error). Rewritten as explicit `if/then/break` with an
  attempt counter that both loops now read (fixing SC2034 by *using* the
  counter, not renaming it) and an explicit `exit 1` with a message when a
  loop exhausts every attempt (matching this repo's existing retry-with-
  backoff-fail-loudly convention used for the HF/FlashRank model downloads).
  Verified with a bash harness exercising both the success and exhaustion
  paths, and under `set -euo pipefail`.

- SC2086 (release.yml changelog step): `${PREV_TAG}..HEAD` unquoted ->
  quoted. Same class of finding (release.yml, publish-ccplugins.yml
  `$UPSTREAM`) fixed by quoting each occurrence.

- SC2046 (publish-ccplugins.yml): unquoted `$(date ...)` embedded in
  `echo manual-$(date ...)` -> quoted.

- SC2129 (publish-ccplugins.yml + sync-ccplugins-fork.yml Summary steps):
  individual `>> "$GITHUB_STEP_SUMMARY"` redirects per echo -> one
  `{ ...; } >> file` block.

Wiring: ci.yml's Lint job now downloads actionlint 1.7.12 from its GitHub
Release, verifies it against the release's own linux_amd64 sha256 (recomputed
locally against the published checksums.txt, not trusted blindly), and runs
`actionlint -color` — pinned by tag + checksum rather than `go install
@latest` / curl|bash, matching this repo's supply-chain-hardening stance.

Verification (criterion 5): the runner-local PostgreSQL retry-loop fix is
identical in ci.yml and release.yml; ci.yml's `test` job exercises it on
every push/PR, giving a real (not just YAML-read) execution. release.yml's
own `test` job only runs on a `v*` tag push, so `workflow_dispatch` was added
as a verification-only trigger; the release-producing jobs (github-release,
build, sbom, publish-pypi) are gated to `startsWith(github.ref,
'refs/tags/')` so a dispatched run cannot cut a release, publish to PyPI, or
fire the ccplugins-fork publish workflow — it only proves `test` passes.

Fixes #247

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u
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.

ci: 5 actionlint/shellcheck findings in release.yml + sync-ccplugins-fork.yml, and actionlint is not wired into the Lint job

1 participant