diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml new file mode 100644 index 0000000..498694a --- /dev/null +++ b/.github/workflows/actionlint.yml @@ -0,0 +1,59 @@ +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" + # 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: + 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/`)