CI hosted-runner audit (2026-08-09)¶
Trigger: Owner directive that no repo in the spadoople org should run
GitHub Actions jobs on GitHub-hosted runners (ubuntu-latest,
windows-latest, macos-latest). spadoople/homelab is the reference
implementation for the correct pattern; this audit inventories every other
repo in the org against it as a precursor to a migration effort.
Method: Enumerated all 21 repositories in the spadoople org and read
every workflow file under .github/workflows/ in each (directory listing +
raw content fetch, not code search — GitHub code search does not reliably
index every file/repo in this org and undercounted matches when tried).
Reference pattern (from homelab)¶
homelab's workflows never hardcode a GitHub-hosted runner label. Instead:
- Job-level
runs-onis one of four repo/org variables, evaluated throughfromJSON: runs-on: ${{ fromJSON(vars.CI_RUNS_ON) }}— general-purpose jobsvars.DOCKER_RUNS_ON— jobs needing Docker/service containersvars.DEPLOY_RUNS_ON— deploy jobsvars.RUNNER_FLEET_MAINTENANCE_RUNS_ON— fleet-maintenance workflows- Each variable holds a JSON array of on-prem runner labels, so the actual label set can change per-repo/per-environment without editing workflow YAML.
- Shared logic lives in reusable workflows in
spadoople/ci-toolkit(secret-scan.yml,semgrep.yml,workflow-lint.yml,runner-health.yml,stale-run-watchdog.yml,library-ci.yml), invoked withuses: spadoople/ci-toolkit/.github/workflows/X.yml@<sha>and aruns_on_jsoninput threading the caller's runner labels through. - An explicit enforcement job (
no-hosted-runners, runningscripts/check-no-hosted-runners.sh) asserts zero occurrences ofubuntu-latest/windows-latest/macos-latestanywhere in the repo's workflows, so regressions fail CI instead of going unnoticed. - This isn't just a preference: infra hostnames the runners need to reach are LAN-only, so a GitHub-hosted runner is architecturally unable to do this work, not merely disallowed.
Summary¶
| Repo | Status | Detail |
|---|---|---|
homelab |
✅ Compliant | Reference implementation |
dnd_session_parser |
✅ Compliant | Uses vars.CI_RUNS_ON/DOCKER_RUNS_ON; wires runs_on_json into ci-toolkit calls |
Telegram |
✅ Compliant | — |
dnd_transcriber |
✅ Compliant | — |
tiktooker |
✅ Compliant | 6 workflows, all on vars.* |
aiProject |
✅ Compliant | — |
ci-toolkit |
✅ Fixed 2026-08-09 | stale-run-watchdog.yml now takes runs_on_json; see systemic finding below |
dependency-steward |
✅ Fixed 2026-08-09 | Runner fix + missing runs_on_json bug in security.yml |
nellis-auction-monitor |
❌ Non-compliant | All jobs on ubuntu-latest, no self-hosted pattern present |
rexit_evolved |
❌ Non-compliant | Worst offender — no self-hosted pattern adopted anywhere, plus a macOS/Windows/Linux release matrix |
github-actions (archived) |
⚠️ Likely affected | Duplicate copy of ci-toolkit workflows; low priority since archived |
| 7 repos with no workflows | N/A | nfs-file-monitoring, reddit_explorer, kangapoo_databutts, home_assistant, homelabPatching, openstackPatching, AI-Troubleshooter---700-Batch |
telegram_companion |
✅ Onboarded 2026-08-12 | PR #58 CI uses vars.CI_RUNS_ON / DOCKER_RUNS_ON; fleet instances telegram-companion-ci-01/-02 + telegram-companion-docker-01 |
pleskshared-BoxOfPlesk |
N/A | Empty repo, no commits |
Systemic finding: ci-toolkit's stale-run-watchdog.yml (highest leverage fix) — ✅ fixed 2026-08-09¶
Status: Fixed. ci-toolkit#5
added a required runs_on_json input and merged to main
(a4eb873f75413fffe82724fbebf431aa84b21fb8). All five caller repos
(dnd_session_parser, Telegram, dnd_transcriber, aiProject,
tiktooker) were repointed at that commit and now pass
runs_on_json: "${{ vars.CI_RUNS_ON }}". aiProject had been pinned to an
unmerged ci-toolkit feature-branch commit rather than main; that was
corrected in the same pass.
The reusable workflow spadoople/ci-toolkit/.github/workflows/stale-run-watchdog.yml
hardcoded runs-on: ubuntu-latest on its watchdog job. Every other
reusable workflow in ci-toolkit (secret-scan.yml, semgrep.yml,
workflow-lint.yml, runner-health.yml, library-ci.yml) correctly takes a
runs_on_json input — this one did not use it for its own job.
Because callers invoke this workflow via uses: rather than copying its
YAML, every repo that calls it silently runs a job on a GitHub-hosted
runner today, even though the calling repo itself looks fully compliant:
dnd_session_parserTelegramdnd_transcriberaiProjecttiktooker
Fixing runs-on in this one file (add a runs_on_json input, apply
fromJSON(inputs.runs_on_json), update the ~5 call sites to pass it) closes
the gap for all five repos in a single PR — the single highest-leverage fix
in this audit.
spadoople/github-actions (archived) holds a near-duplicate copy of these
same reusable workflows and likely has the same bug, but since it's archived
this is a one-line note rather than an action item.
dependency-steward: runner issue + separate correctness bug — ✅ fixed 2026-08-09¶
Status: Fixed. Three commits on main:
reconcile.yml,
test.yml,
security.yml.
Both workflow files now use runs-on: ${{ fromJSON(vars.CI_RUNS_ON) }}, and
security.yml now passes the required runs_on_json input to all three
ci-toolkit calls (also repointed at the latest ci-toolkit main commit).
This repo already had two general-class runners provisioned
(dependency-steward-ci-01/-02 in infra/github-runners/fleet.yml), so no
new runner capacity was needed — this was config-only.
reconcile.yml: wasruns-on: ubuntu-latesttest.yml: both jobs were onubuntu-latestsecurity.yml: called theci-toolkitreusable workflows but never passedruns_on_jsonat all. That input isrequired: trueon the reusable workflows it calls, so this call was likely failing outright, not merely running on the wrong runner.
nellis-auction-monitor¶
ci.yml — all three jobs (test, pipx-test, docker) run on
ubuntu-latest. No vars.*/self-hosted scaffolding exists in this repo at
all yet; migration starts from zero here.
rexit_evolved (biggest lift)¶
No self-hosted pattern adopted anywhere in this repo:
audit.yml:runs-on: ubuntu-latestci.yml: all 4 jobs onubuntu-latestcombine-prs.yml:runs-on: ubuntu-latestcd.yml: a release matrix acrossmacos-latest,ubuntu-latest,windows-latest, plus a separatepublish-cargojob onubuntu-latest
The cd.yml matrix is the one open design question in this audit: on-prem
runners are presumably Linux, so the macOS/Windows legs of that matrix have
no obvious on-prem equivalent. Migrating this repo will need an explicit
decision on what happens to cross-platform release builds (e.g., keep a
narrowly-scoped hosted-runner exception just for that matrix, use a
cross-compilation toolchain from a single Linux runner, or drop
non-Linux release artifacts) before the rest of the workflow can be ported
mechanically.
Fully compliant repos (no action needed)¶
homelab, dnd_session_parser, Telegram, dnd_transcriber, tiktooker,
aiProject — all use the vars.CI_RUNS_ON/DOCKER_RUNS_ON/DEPLOY_RUNS_ON +
fromJSON(...) pattern and wire runs_on_json into ci-toolkit reusable
workflow calls correctly. (Five of these six are still indirectly affected
by the stale-run-watchdog.yml bug above.)
Recommended remediation order¶
ci-toolkit:stale-run-watchdog.yml— fixes 5 repos in one PR, zero new runner-label plumbing needed since the callers already pass compliant labels elsewhere.dependency-steward— fixruns-oninreconcile.yml/test.ymland add the missingruns_on_jsoninput to thesecurity.ymlreusable-workflow calls in the same PR.rexit_evolved— largest single-repo lift: needs runner-label variables created from scratch, all 4 workflows rewritten, and an explicit decision on thecd.ymlcross-platform matrix before that file can be finished.nellis-auction-monitor— same from-zero setup asrexit_evolvedbut only one workflow file, no cross-platform matrix complication.
Open question before remediation starts¶
Whether CI_RUNS_ON / DOCKER_RUNS_ON / DEPLOY_RUNS_ON are defined as
org-level variables (auto-inherited by every repo including new ones) or
per-repo. This wasn't checked as part of this audit — the GitHub MCP toolset
used here didn't expose a direct "list repo/org variables" call. If they're
per-repo only, each remediated repo needs these variables created before its
workflow changes will actually resolve to a runner.
Related¶
- Komodo reliability audit — prior audit using the same "read everything, then triangulate a remediation order" approach.