Repository navigation
feat(workflows): every reusable workflow takes a runner input - #15
Merged
Merged
Conversation
All six `workflow_call` workflows hardcoded `runs-on: ubuntu-latest`, so a
caller could not run them anywhere else. They now take `runner`, a string
defaulting to `ubuntu-latest` — every existing caller is unchanged.
A GitHub-hosted job does not fail when an account's hosted minutes are
unavailable. It never runs, and reports that as `conclusion: failure` with
`steps: 0` and an empty `runner_name` — no log, no annotation, nothing
naming the cause. In vaam-apps/vaam-apps that hit four jobs from 2026-09-19
onward, two of them named "title is a conventional commit" and "the squash
message parses as a conventional commit", so a billing problem read as a
malformed PR title on every open PR for days.
That repo could move its own four jobs to its ARC pool. It could not move
`lint`, `trivy` or `issue-governance`, because those are `uses:` calls into
this repository and `runs-on` lives here. And the half-move is worse than no
move: with the caller's own gate job green again, the two quality jobs it
gates went from `skipping` — neutral, and quietly hiding the dependency — to
dispatched and refused. Adopting a self-hosted pool halfway relocates the
outage into this org's checks rather than removing it.
A single label, not a JSON array. `runs-on: ${{ inputs.runner }}` takes one
label; `[self-hosted, macOS, ARM64]` would need `fromJSON`, which changes
the expression for every caller including the ones passing a plain string.
No caller needs it, so it is not done — the README says so rather than
leaving it to be discovered.
README gains "Choosing a runner", including the part that is not uniform
across the six: `lint` and `trivy` both run container actions
(super-linter/slim, trivy-action) and therefore need Docker and a Linux
runner, `sast` downloads the CodeQL bundle per run, and the three governance
workflows need `gh`, `jq` and — for sync-repo-settings — `python3`. Nothing
here tests any of them on a self-hosted runner and this change adds no such
test, because the default is unchanged and the org's own CI still exercises
only the hosted path. That is stated in the README too.
Verified: `actionlint` clean over all eight workflows, and a YAML assertion
that each callable workflow declares the input with the right type, default
and `required: false`, and that all seven jobs across them now read
`${{ inputs.runner }}`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
1. Summary
This PR changes:
runnerinput (type: string,required: false,default: ubuntu-latest) to allsix
workflow_callworkflows:lint.yml,trivy.yml,sast.yml,issue-governance.yml,pr-governance.yml,sync-repo-settings.yml.runs-on:lines across them to${{ inputs.runner }}.README.md, including what each workflow needsfrom a non-hosted runner — which is not uniform across the six.
It solves:
jobs onto its ARC pool and could not move
lint,trivyorissue-governance,because those are
uses:calls into this repository andruns-onlives here.Every existing caller is unchanged: the default is the value that was hardcoded.
2. Intent
The intent of this PR is:
3. Scope
In Scope
workflow_callworkflows and their seven jobs.README.md: a new "Choosing a runner" section.Out of Scope
pr-title.ymlandrelease-please.ymlin this repository. They arepull_requestandpushtriggered — this repo's own CI, not callable — so arunnerinput would meannothing for them. They are still on
ubuntu-latestand would still be dead in the sameoutage; deciding whether this repo's own CI should leave hosted runners is a separate
question from giving callers a choice, and I have not pre-empted it.
vaam-apps/vaam-appswill passrunner:and re-pin in its own PRonce this is released.
runs-on: ${{ inputs.runner }}takes one label;[self-hosted, macOS, ARM64]would needfromJSON, which changes the expression forevery caller including those passing a plain string. No caller needs it today. The README
says so rather than leaving it to be discovered.
4. Verification
I verified this change by:
Commands run:
Results:
The assertion checks, per callable workflow, that
runnerexists withtype: string,required: false,default: ubuntu-latest, and that every job'sruns-onis exactly${{ inputs.runner }}— read out of the parsed YAML rather thaneyeballed from the diff, so a job left behind cannot pass.
What is not verified, and the honest limit of this PR: nothing here runs any of these
on a self-hosted runner, and this change adds no such test. The default is unchanged, so
this repo's own CI still exercises only the hosted path. The first real evidence that a
given pool satisfies a given workflow is a caller switching it over. The README says this
and says to switch one at a time.
5. Screenshots / Evidence
The requirement table added to the README, which is the part most likely to bite and was
read out of the workflows rather than assumed:
lint.ymlsuper-linter/super-linter/slimis a container actiontrivy.ymlaquasecurity/trivy-action, likewisesast.ymlgithub/codeql-actiondownloads the CodeQL bundle per runissue-governance.ymlgh,jqgh apicallspr-governance.ymlgh,jqgh apicallssync-repo-settings.ymlgh,jq,python3The two container actions are the ones to notice: a pool without Docker, or a non-Linux
one, fails
lintandtrivyfor a reason that has nothing to do with capacity.6. Risk Assessment
Risk level:
Potential risks:
lintortrivyon a pool without Docker. Mitigated by the README table above, and bounded: itaffects only the repo that opted in, and reverts by deleting one
with:line.the interface. Six reusable workflows gain a public input, and release-please will cut a
minor for it.
runners is choosing to run App-authenticated jobs on its own infrastructure. That is the
caller's decision to make and this PR only makes it expressible — worth naming, since
lint.ymlpushes fix commits andsync-repo-settings.ymlwrites rulesets.Mitigation:
self-hosted runner here.
7. AI Usage Declaration
AI was used for:
Human verification:
The per-workflow requirement table was derived by reading each workflow's
uses:steps atthe pinned shas, not inferred from workflow names — the two container actions are the
finding that table exists for.
8. Reviewer Focus
Please focus your review on:
Specifically:
sync-repo-settings.ymlshould have been included. It is manual andadministrative rather than a PR check, so it is not part of the outage this PR responds
to. I included it for uniformity — six callable workflows, one rule — on the view that a
list of "the ones that take a runner" which is a subset of "the ones that are callable"
is the kind of split that goes stale. Easy to drop if you disagree.
self-hosted target sooner than I think.
runnerreads well at the call site (with: runner: ubuntu-amd-x64);runs-onwould mirror the Actions key but shadows a reserved-looking name.🤖 Generated with Claude Code