From c7586c962a9465c80fca4d2d06d39bd5349fb165 Mon Sep 17 00:00:00 2001 From: Blair Hamilton Date: Sun, 7 Jun 2026 20:58:26 -0400 Subject: [PATCH 1/2] ci: lint workflows with actionlint Adds a reusable `actionlint.yml` that lints GitHub Actions workflow YAML at a pinned upstream version. It also self-runs on this repo when PRs/pushes touch `.github/workflows/**` or `actions/**/action.yml`, catching syntax / expression / shell / `workflow_call` input-contract errors before they hit a caller as a runtime `startup_failure`. Closes the residual acceptance item from platform-gitops#540: > Add `actionlint` (or equivalent) on the calling workflow to catch > the kind of input-contract regression that dis #131 fixed. Service-template's `ci.yml` will reuse this workflow in a follow-up so new repos forked from the template inherit the check automatically. Refs: platform-gitops#944 Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/actionlint.yml | 53 ++++++++++++++++++++++++++++++++ AGENTS.md | 2 +- README.md | 1 + 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/actionlint.yml diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml new file mode 100644 index 0000000..5056e9a --- /dev/null +++ b/.github/workflows/actionlint.yml @@ -0,0 +1,53 @@ +name: actionlint + +# Lints GitHub Actions workflow YAML. +# +# Two roles: +# 1. Self-CI for this repo — runs on PRs/pushes that touch +# `.github/workflows/**` or `actions/**/action.yml`. Catches +# input-contract regressions in the reusable workflows here +# before they reach a caller as `startup_failure` at runtime +# (platform-gitops#944). +# 2. Reusable entry point — callers (service-template's `ci.yml` +# and any onboarded repo) `uses:` this workflow to lint their +# own `.github/workflows/`. +# +# actionlint itself doesn't fetch remote reusable workflows, so it +# cannot cross-validate that a caller's input map matches this +# repo's `workflow_call.inputs:` block. It still catches the lion's +# share of input-drift incidents (typos, removed-input refs in the +# reusable workflow, expression and shell errors in both ends). + +on: + workflow_call: {} + pull_request: + branches: [main] + paths: + - '.github/workflows/**' + - 'actions/**/action.yml' + push: + branches: [main] + paths: + - '.github/workflows/**' + - 'actions/**/action.yml' + +# Pinned upstream release. Bump deliberately; actionlint occasionally +# tightens rules in a way that flags previously-passing workflows. +env: + ACTIONLINT_VERSION: "1.7.7" + +jobs: + actionlint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Install actionlint + run: | + set -euo pipefail + bash <(curl -fsSL \ + "https://raw.githubusercontent.com/rhysd/actionlint/v${ACTIONLINT_VERSION}/scripts/download-actionlint.bash") \ + "${ACTIONLINT_VERSION}" + + - name: Run actionlint + run: ./actionlint -color diff --git a/AGENTS.md b/AGENTS.md index 0fd0363..a5236c9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,7 +13,7 @@ Callers pin to `@main` (intentional — we own all consumers; pre-tagging adds o - `actions//action.yml` — composite actions. Used as `uses: pinpredict/.github/actions/@main`. - `.github/workflows/.yml` — reusable workflows. Used as `uses: pinpredict/.github/.github/workflows/.yml@main`. -There is no build, lint, or test step in this repo. Validate changes by running them against a real caller (open a draft PR in a service repo that points its `uses:` at your branch). +Static lint: `actionlint.yml` runs on every PR that touches `.github/workflows/**` or `actions/**/action.yml`, catching workflow syntax / expression / shell / `workflow_call` input-contract errors before they reach a caller as a runtime `startup_failure`. No build or test step — semantic changes still need a draft PR in a real caller pointing its `uses:` at your branch. ## Architectural contracts other repos depend on diff --git a/README.md b/README.md index 825eefc..3b58f6c 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Why `.github` and not a dedicated `github-actions` repo: `.github` is *the* GitH | `docker-release.yml` | Matrix-based image build + push to ECR; per-service `image//X.Y.Z` git tags; aggregated GitHub Release. Caller passes a `matrix` input in the standard `{include:[...]}` shape. | | `chart-release.yml` | Auto-discovers `charts/*/`, bumps versions, packages, pushes to ECR OCI, tags `chart//X.Y.Z`. No caller inputs. | | `tag-config.yml` | Tags merges to main that touch `.platform/services/.yaml` with `vX.Y.Z+` (per-service Kargo `-config` Warehouse freight), then dispatches `service-config-tag` to platform-gitops so missing pointer files get seeded. | +| `actionlint.yml` | Lints GitHub Actions workflow YAML with [`actionlint`](https://github.com/rhysd/actionlint) at a pinned version. Self-runs on this repo when PRs/pushes touch `.github/workflows/**` or `actions/**/action.yml`; callers reuse it via `uses: pinpredict/.github/.github/workflows/actionlint.yml@main`. | ### Composite actions (`actions/`) From 427eeb480ce1c50daf2104f9512fcbe7e48f8d2d Mon Sep 17 00:00:00 2001 From: Blair Hamilton Date: Sun, 7 Jun 2026 21:01:38 -0400 Subject: [PATCH 2/2] ci(actionlint): scope shellcheck to warning+ severity The first run flagged four shellcheck info/style findings (SC2295, SC2001, SC2129) in pre-existing `run:` blocks across chart-release, docker-release, and tag-config. They're real but minor stylistic nitpicks, not the input-contract regressions this gate is scoped to catch (platform-gitops#944). Pass `-S warning` via `SHELLCHECK_OPTS` so shellcheck still catches real bugs (quoting, word-splitting, exit-code mishandling) but stops blocking PRs on style. Tighten later by dropping the env var once the existing scripts are tidied up. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/actionlint.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml index 5056e9a..498694a 100644 --- a/.github/workflows/actionlint.yml +++ b/.github/workflows/actionlint.yml @@ -35,6 +35,12 @@ on: # tightens rules in a way that flags previously-passing workflows. env: ACTIONLINT_VERSION: "1.7.7" + # Scope shellcheck to warning+ severity. The gate's job is to catch + # input-contract regressions and real shell bugs (SC2086 quoting, + # SC2046 word-splitting, etc.); info/style nitpicks (SC2295, SC2001, + # SC2129) in long-standing `run:` blocks aren't worth blocking PRs + # over. Drop this once the existing scripts are tidied up. + SHELLCHECK_OPTS: "-S warning" jobs: actionlint: