ci(security): add actionlint check to PR static-analysis job (#89) #92

Merged
james merged 1 commit from 89-actionlint-ci into main 2026-06-18 01:42:36 +00:00
Owner

Closes #89. Sister to #88 (lefthook pre-commit hook, already merged).

What this does

Adds two sibling steps inside the existing static-analysis job in pr.yml:

  • Install actionlint — curl the pinned tarball from the upstream release URL, extract into /usr/local/bin/. Same install pattern as OSV_SCANNER_VERSION and TRIVY_VERSION use below.
  • Lint workflows (actionlint) — runs actionlint .forgejo/workflows/*.yml. Exit code is non-zero on any finding; the human-readable text output renders cleanly in the run UI.

ACTIONLINT_VERSION: 1.7.12 lives in the workflow's env: block alongside the other tool pins, chosen to match what brew install actionlint currently installs — so the pre-commit hook from #88 and CI run the same version on a fresh dev setup.

The job itself is renamed from "Static analysis (Semgrep)" to just "Static analysis" since it now covers both tools.

Scope choice

  • Sibling step in static-analysis vs. dedicated job: sibling. The runner image is already the right one, and a dedicated job would burn ~30s on container spin-up for ~5s of actual work.
  • Lint scope: every workflow vs. diff-touched only: every workflow. The set is tiny (4 files today), and a rule-pack bump can expose findings on files no PR happens to touch. Catching those proactively is cheap.
  • Step summary: skipped. actionlint's default text output is already terse and contextual (line + column + the offending snippet). The security-summary.mjs helper exists to normalize JSON output from heterogeneous scanners; actionlint is a single tool with one output shape, so a custom summarizer would be pure overhead.

Test plan

  • actionlint .forgejo/workflows/*.yml against this branch passes clean — the pr.yml content lints itself green.
  • Pre-commit hook from #88 (now on main) fires on this commit's staged pr.yml; both gitleaks and actionlint pass before push, demonstrating the dual-layer in practice.
  • CI on this PR runs the new "Lint workflows (actionlint)" step green.
  • Negative confirmation: a future PR that introduces a workflow regression (e.g. ${{ github.no_such_context }}) fails this step with the same line+column message the local hook produces.

Coordination

#88 has merged; this PR closes the dual-layer loop. After this lands, the README "Setup" section and ADR-0011-style "dual triggers" rationale carry over naturally to the actionlint pair — no further docs change needed for the immediate cycle.

Closes #89. Sister to #88 (lefthook pre-commit hook, already merged). ## What this does Adds two sibling steps inside the existing `static-analysis` job in `pr.yml`: - **Install actionlint** — curl the pinned tarball from the upstream release URL, extract into `/usr/local/bin/`. Same install pattern as `OSV_SCANNER_VERSION` and `TRIVY_VERSION` use below. - **Lint workflows (actionlint)** — runs `actionlint .forgejo/workflows/*.yml`. Exit code is non-zero on any finding; the human-readable text output renders cleanly in the run UI. `ACTIONLINT_VERSION: 1.7.12` lives in the workflow's `env:` block alongside the other tool pins, chosen to match what `brew install actionlint` currently installs — so the pre-commit hook from #88 and CI run the same version on a fresh dev setup. The job itself is renamed from "Static analysis (Semgrep)" to just "Static analysis" since it now covers both tools. ## Scope choice - **Sibling step in static-analysis vs. dedicated job**: sibling. The runner image is already the right one, and a dedicated job would burn ~30s on container spin-up for ~5s of actual work. - **Lint scope: every workflow vs. diff-touched only**: every workflow. The set is tiny (4 files today), and a rule-pack bump can expose findings on files no PR happens to touch. Catching those proactively is cheap. - **Step summary**: skipped. `actionlint`'s default text output is already terse and contextual (line + column + the offending snippet). The `security-summary.mjs` helper exists to *normalize* JSON output from heterogeneous scanners; actionlint is a single tool with one output shape, so a custom summarizer would be pure overhead. ## Test plan - [x] `actionlint .forgejo/workflows/*.yml` against this branch passes clean — the pr.yml content lints itself green. - [x] Pre-commit hook from #88 (now on `main`) fires on this commit's staged pr.yml; both gitleaks and actionlint pass before push, demonstrating the dual-layer in practice. - [ ] CI on this PR runs the new "Lint workflows (actionlint)" step green. - [ ] Negative confirmation: a future PR that introduces a workflow regression (e.g. `${{ github.no_such_context }}`) fails this step with the same line+column message the local hook produces. ## Coordination #88 has merged; this PR closes the dual-layer loop. After this lands, the README "Setup" section and `ADR-0011`-style "dual triggers" rationale carry over naturally to the actionlint pair — no further docs change needed for the immediate cycle.
ci(security): add actionlint check to PR static-analysis job (#89)
All checks were successful
Secrets / gitleaks (pull_request) Successful in 18s
PR / OSV-Scanner (pull_request) Successful in 33s
PR / Static analysis (pull_request) Successful in 42s
PR / Trivy (image) (pull_request) Successful in 1m7s
PR / Test (postgres) (pull_request) Successful in 1m29s
PR / npm audit (pull_request) Successful in 3m8s
PR / Typecheck (pull_request) Successful in 3m20s
PR / Lint (pull_request) Successful in 3m41s
PR / Test (sqlite) (pull_request) Successful in 3m44s
PR / Build (pull_request) Successful in 4m33s
0155422ef6
CI-side enforcement for the workflow linter that #88 added as a local
pre-commit hook. Catches the cases the local hook can't: contributors
without actionlint installed, `git commit --no-verify`, force-push to a
long-lived branch.

Wired as two sibling steps inside the existing `static-analysis` job
in pr.yml, after Semgrep. Reuses the same runner image — no extra
job-spinup overhead. Job renamed from "Static analysis (Semgrep)" to
just "Static analysis" since it now covers both tools.

Install pattern matches OSV-scanner / Trivy below: curl the pinned
tarball, extract the binary, run it. ACTIONLINT_VERSION (1.7.12) lives
in the workflow's env: block alongside OSV_SCANNER_VERSION /
TRIVY_VERSION, chosen to match what `brew install actionlint`
currently lays down — README's pre-commit hook + CI step see the same
version on a fresh dev setup.

Scope: every `.forgejo/workflows/*.yml` file in the repo, not just
diff-touched files. The set is tiny; a rule-pack bump can expose
findings on files no PR touches.

Verified locally: `actionlint .forgejo/workflows/*.yml` against this
branch passes clean (the new pr.yml content lints itself green).

Closes #89.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
james merged commit 1b82c7fcb3 into main 2026-06-18 01:42:36 +00:00
Sign in to join this conversation.
No description provided.