Skip to content

ci: keep runner selection on hosted pools - #48

Merged
ScriptedAlchemy merged 1 commit into
mainfrom
ci/runner-input-safety
Nov 3, 2026
Merged

ScriptedAlchemy merged 1 commit into
mainfrom
ci/runner-input-safety

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Nov 3, 2026 •

Copy link
Copy Markdown
Owner

Problem

Runner labels arrive from workflow YAML and flow into runs-on. Two places take them as data:

  • release-desktop.yml exposes runner and verify_runner as workflow_call string inputs and uses them directly in runs-on. Current callers pass literals (blacksmith-*, windows-*), but nothing stops a future caller from passing self-hosted or a private label, which would put a release build with signing secrets on hardware this repository does not control.
  • .github/workflows/*.yml has no static guard against selecting a private runner, so a pull request adding one is only caught by review.

Separately, the fork PR preview (mobile-eas-preview-fork.yml) pinned expo/expo-github-action/continuous-deploy-fingerprint at @main while running fork-authored code with EXPO_PREVIEW_TOKEN, so a new upstream commit on main would run there unreviewed.

Change

  • .github/workflows/release-desktop.yml: a new validate job on ubuntu-24.04 checks both inputs and fails closed. build and verify need it, so no job with a bad label is ever scheduled. Allowed labels are the hosted pools this workflow is built for: blacksmith-*, ubuntu-*, macos-*, windows-*; self-hosted and any *-runner label are rejected. Every current caller keeps working (blacksmith-12vcpu-macos-26, blacksmith-32vcpu-ubuntu-2404, blacksmith-16vcpu-ubuntu-2404-arm, ubuntu-24.04, ubuntu-24.04-arm, macos-26, windows-2025, windows-11-arm).
  • .github/scripts/check-runner-labels.py: scans .github/workflows/*.yml and fails on self-hosted or a private-runner label (linux-runner, windows-runner, macos-runner, bench-*), ignoring comments. The same script validates explicit labels (--runner, --verify-runner) so the predicate is defined once. .github/scripts/check-runner-labels.test.py covers both modes.
  • .github/workflows/ci.yml and .github/workflows/fork-ci.yml: run the checker and its unit tests in each workflow's changes job. It is the cheapest job that runs on every pull request and it runs before anything else, so a PR that introduces a private runner label fails before another job starts. ci.yml's sparse checkout now also takes .github/workflows/ and the script; fork-ci.yml already checks out the whole tree, and it is the workflow that runs in this repository while CI is disabled.
  • .github/workflows/mobile-eas-preview-fork.yml: pin the fingerprint deploy action to 9da89d3617bb88139671bad31b09c65714c2ee06 (the commit v9.0.1 tags) instead of the moving main ref.

No environment gates or repository settings are changed here; those are follow-ups (see below).

Scope and approval

Security hardening of existing CI configuration, no behavior change for current callers. Environment protection rules for the signing and Expo flows are deliberately not part of this change because they are repository settings, not workflow code.

Verification

  • ruby -ryaml parses the three changed workflow files.
  • python3 -B .github/scripts/check-runner-labels.test.py → 12 tests, OK.
  • python3 -B .github/scripts/check-runner-labels.py → passes on the tree; with runs-on: self-hosted planted in a copy it exits 1 and names the file and line; with bench-32vcpu it exits 1.
  • The extracted validate step run with each caller label: blacksmith-8vcpu-ubuntu-2404, ubuntu-24.04-arm + windows-11-arm, ubuntu-24.04, windows-11-arm → exit 0; self-hosted, bench-32vcpu, macos-26-runner, linux-runner, and an empty runner → exit 1.
  • actionlint on the three changed workflows reports exactly the same 12 pre-existing warnings as main (Blacksmith labels unknown to its default config, one existing SC2046 in the signing step); nothing new.
  • vp fmt --check equivalent (oxfmt 0.74, defaults) passes on .github/workflows/*.yml; Python is not formatted by the repo tooling, matching stage-preview-bundle.py.
  • git diff --check clean.
  • Not run: the full test suite, any EAS/desktop build, and vp check (dependencies are not installed in this checkout).

Residual risks and follow-ups

  • Fork PRs still consume owner Blacksmith capacity and still receive an OIDC token; this change only prevents selecting a private runner. The cloud trust policy must keep rejecting fork sub claims.
  • desktop-macos-preview-publish.yml still signs and notarizes with signing secrets behind a label and vouch-list gate rather than an environment with required reviewers. Adding that environment is a repository setting.
  • The Expo preview flows still rely on a maintainer label (and, for the trusted preview, the vouch list) rather than environment approval.
  • .github/workflows/mobile-eas-preview.yml (same-repo PRs) still uses continuous-deploy-fingerprint@main; same pin applies if desired.
  • The static scan is textual, so workflow YAML must not spell a private runner label anywhere outside a comment. That is intentional: the scan cannot miss a matrix value or an expression.

Model: DeepSeek V4.1 Flash 1017 via OpenCode

@changeset-bot

changeset-bot Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 10fb290

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

A reusable workflow cannot tell a hosted runner label from a private one:
release-desktop.yml's `runner` and `verify_runner` inputs go straight into
`runs-on`, so a future caller could point release jobs, which hold signing
secrets, at self-hosted hardware. Validate both inputs in a GitHub-hosted job
that build and verify need, rejecting `self-hosted`, `*-runner` labels, and
anything outside blacksmith-*, ubuntu-*, macos-*, and windows-*.

Add .github/scripts/check-runner-labels.py, which fails when any workflow
references `self-hosted` or a private-runner label, with unit tests, and run it
in the cheapest PR job so a pull request that introduces one fails before any
other job starts.

Pin expo/expo-github-action/continuous-deploy-fingerprint in the fork PR
preview to the commit v9.0.1 tags instead of `main`: that job runs
fork-authored code with EXPO_PREVIEW_TOKEN, so a moving ref would let a new
upstream commit run there unreviewed.
@ScriptedAlchemy
ScriptedAlchemy merged commit 4a4a480 into main Nov 3, 2026
2 of 3 checks passed
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