.github/.github/workflows/ci.yaml
Adam Moussa 5889d52333
ci: enable actionlint's shellcheck integration in self-CI
The self-CI gate ran `./actionlint -shellcheck=`, and the empty value
silently disabled the shell-linting half of the check — so every `run:`
body in the reusable workflows this repo publishes was unlinted, on the
exact path that deploys to AWS.

Measured against the pinned actionlint 1.7.12 and the shellcheck the
ubuntu-latest runner ships (0.9.0-1), the real backlog was 5 findings,
not the 4 the old comment claimed. Three were genuine and are fixed in
the shell:

- cd-cdk.yaml "Publish .NET project" (SC2046): the project path was
  interpolated inline and `$(dirname ...)` was unquoted, so a path
  containing whitespace split into several arguments. Now passed via
  env indirection and quoted, which also removes the last inline
  expression interpolation from that step.
- cd-cdk.yaml / ci-python-sam.yaml "Install Python dependencies"
  (SC2044 x2): `for req in $(find ...)` word-split and globbed every
  path found. Replaced with a NUL-delimited `while read` loop.

Two are deliberate and are suppressed per-line, with the reasoning in a
comment directly above:

- cd-sam.yaml `sam deploy ... $PARAMS` and cd-cdk.yaml
  `cdk deploy $STACKS` (SC2086 x2) rely on word-splitting so multiple
  parameter overrides / stack selectors reach the CLI as separate argv
  entries. Quoting them would collapse each into a single argument and
  break every parameterised or multi-stack deploy, so they keep the
  unquoted expansion and carry a scoped `# shellcheck disable=SC2086`.

The gate now runs plain `./actionlint` (shellcheck defaults to the
binary on PATH) and prints `shellcheck --version` first, so the check
fails loudly if a future runner image drops it instead of quietly
linting less.
2026-07-28 12:46:17 -04:00

70 lines
3 KiB
YAML

name: ci
# Self-CI for this org `.github` repo.
#
# The org ruleset "main branch protection" requires the `ci / ci` status check on
# every repo. Consumer repos satisfy it via a short caller workflow that invokes
# the reusable workflows here. This repo only HOUSES those reusable workflows
# (all `workflow_call`-only), so nothing emitted `ci / ci` and every PR sat
# permanently "Expected — Waiting for status to be reported" and could not merge.
#
# This workflow produces that check by linting the workflow files with actionlint
# — genuinely useful CI for a repo whose whole product is GitHub Actions YAML.
#
# Naming is load-bearing: the ruleset matches the required status check against
# the JOB's check-run name, NOT "workflow / job". For a normal (non-reusable) job
# the check-run name IS the job name, so the job must be named literally "ci / ci"
# to emit that exact context. (A job named "ci" emits the context "ci" — which the
# PR UI cosmetically *displays* as "ci / ci" but does NOT satisfy the requirement.)
# This mirrors the org's aggregator-job convention.
#
# actionlint is pinned to a tagged release and installed by downloading the
# release tarball and verifying its SHA256 — not `curl | bash` — to keep the
# supply-chain surface auditable. Bump ACTIONLINT_VERSION + ACTIONLINT_SHA256
# together (checksum from the release's *_checksums.txt).
#
# actionlint's shellcheck integration is ON (it defaults to the `shellcheck` on
# PATH; the ubuntu-latest runner image ships shellcheck 0.9.0, so nothing extra
# is installed). Do NOT re-add `-shellcheck=` — the empty value silently turns
# the whole shell-linting half of this gate back off.
#
# Two run-steps carry a narrowly-scoped `# shellcheck disable=SC2086` on the
# single line above the command, because the unquoted expansion there is the
# point: `sam deploy … $PARAMS` (cd-sam.yaml) and `cdk deploy $STACKS`
# (cd-cdk.yaml) rely on word-splitting to turn one variable into several argv
# entries. Quoting them would collapse multiple parameter overrides or stack
# selectors into one argument and break those deploys. Every other finding was
# fixed in the shell rather than suppressed. Suppressions stay per-line and
# commented — never file-wide, and never by weakening this invocation.
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
ci:
name: ci / ci
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Install actionlint
env:
ACTIONLINT_VERSION: 1.7.12
ACTIONLINT_SHA256: 8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8
run: |
curl -fsSL -o actionlint.tar.gz \
"https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
echo "${ACTIONLINT_SHA256} actionlint.tar.gz" | sha256sum -c -
tar -xzf actionlint.tar.gz actionlint
shell: bash
- name: Lint workflows
run: |
shellcheck --version
./actionlint -color
shell: bash