Skip to content

feat(actionlint): cross-repo input validation for reusable workflows - #19

Merged
blairham merged 2 commits into
mainfrom
feat-validate-reusable-inputs
Jun 8, 2026
Merged

feat(actionlint): cross-repo input validation for reusable workflows#19
blairham merged 2 commits into
mainfrom
feat-validate-reusable-inputs

Conversation

@blairham

@blairham blairham commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • New composite action actions/validate-reusable-inputs/ that diffs each caller with: block against the referenced pinpredict/.github reusable workflow's on.workflow_call.inputs map. Fails on unknown keys (with a did-you-mean hint) or missing required keys.
  • Wired as a sibling job in .github/workflows/actionlint.yml so every consumer that already uses: the central actionlint workflow (service-template's ci.yml + everything inheriting it) picks up the gate automatically.
  • README updated under ### Composite actions.

Closes pinpredict/platform-gitops#1045.

Why

Stock actionlint (wired in #944) only validates the local side of workflow_call contracts. It can't fetch remote reusable workflows, so a caller passing a removed or renamed with: key to pinpredict/.github/.github/workflows/<x>.yml@<ref> still surfaces only at runtime as startup_failure. The platform-gitops#1045 issue picked path B (custom action that fetches + diffs) — this PR implements it in ~120 lines of Python.

Behavior verified locally

  • Clean pass on self-CI (pinpredict/.github): 5 workflow file(s) scanned, all caller with: blocks match.
  • Clean pass on service-template: 2 workflow file(s) scanned, all caller with: blocks match (validates the real matrix: input on docker-release.yml@main).
  • Negative test (deliberately broken with: block):
    - job `release` passes unknown input `bogus_flag` to ...docker-release.yml@main (did you mean `matrix`?)
    - job `release` passes unknown input `matrixx` to ...docker-release.yml@main (did you mean `matrix`?)
    - job `release` is missing required input `matrix` for ...docker-release.yml@main
    exit=1
    

Test plan

  • Self-CI on this PR: actionlint job passes. The validate-reusable-inputs job will fail on this PR only — the composite action it uses: ...@main doesn't exist on main yet (chicken-and-egg with org-default action releases). Override required-checks for this PR; subsequent PRs pass cleanly.
  • Post-merge: open a throwaway PR in service-template that adds a bogus with: key on the actionlint uses: line; confirm CI fails on the caller with a useful message.
  • Post-merge: confirm a clean PR in service-template still goes green.

Open question (call out in review)

The action runs in the caller's job context, so it uses the caller's GITHUB_TOKEN. That token has contents: read on the caller's own repo but not necessarily on pinpredict/.github. If gh api fails on cross-repo reads in real callers, we'll need either (a) pinpredict/.github to be readable org-wide, or (b) a PAT/App token passed via github-token. The action already exposes the input — wiring is a one-line follow-up.

🤖 Generated with Claude Code

…reusable workflows

Closes platform-gitops#1045.

Stock `actionlint` (wired in #944) doesn't fetch remote reusable
workflows, so a caller passing a removed or renamed `with:` key to
`pinpredict/.github/.github/workflows/<x>.yml@<ref>` still surfaces
only at runtime as `startup_failure`.

New composite action `actions/validate-reusable-inputs/` scans the
caller's workflow files for `uses: pinpredict/.github/...@ref` lines,
fetches each referenced workflow at the pinned ref via `gh api`, and
diffs the caller `with:` block against the declared
`on.workflow_call.inputs` map. Fails on unknown keys (with a
did-you-mean hint) or missing required keys.

Wired as a sibling job in `actionlint.yml`, so every consumer that
already `uses:` the central actionlint workflow (service-template's
`ci.yml` and every repo inheriting it) inherits the gate
automatically.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@blairham
blairham marked this pull request as ready for review June 8, 2026 12:00
…te-reusable-inputs

Self-CI was failing because uses: pinpredict/.github/actions/...@main
resolved before the action existed on main. ./<path> in a reusable
workflow resolves against the *caller's* checkout, not this repo's,
so a relative path doesn't fix it either.

Check out pinpredict/.github at github.workflow_sha (the SHA of the
workflow file itself — PR head in self-CI, the @ref SHA when invoked
downstream) and reference the action locally. Removes the bootstrap
chicken-and-egg and guarantees the action source matches the workflow
file that's running it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@blairham
blairham merged commit 90bcd58 into main Jun 8, 2026
2 checks passed
@blairham
blairham deleted the feat-validate-reusable-inputs branch July 5, 2026 14:37
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.

1 participant