Repository navigation
ci: run our own code on our runners, forks stay on GitHub's - #157
Conversation
GitHub's hosted queue has gone down several times this quarter, and every required check here ran on ubuntu-latest, so each outage held every PR. Pushes, same-repo PRs, schedules and dispatches now run on browser-bridge-exec, the non-root one-job runners on our host. A fork's PR and a Dependabot PR run code nobody here wrote and stay on GitHub's runners, chosen per job by one runs-on expression. docker-build needs a Docker daemon, which browser-bridge-exec does not have. The image build and boot smoke run on GitHub's runners on every push to master and on a PR that changes what the image is built from (the Dockerfile, .dockerignore, the package files, the root .mjs files it copies, or build.yml); the required check runs here and passes only when that job passed or was not owed. fleet-status posts fleet/verify and fleet/review and runs only the default branch's script, so it moves for every event, as does the dispatch-only backfill. CodeQL gets 2G and two threads on our runner. scripts/fleet-status.test.mjs evaluates the expression in each workflow that uses it.
actionlint knows only GitHub's runner labels and failed the two workflows that name browser-bridge-exec literally.
sprayberry-redline
left a comment
There was a problem hiding this comment.
Verdict: request changes. The runner selection distinguishes fork and Dependabot PRs from same-repository events, and the Docker filter covers the current Dockerfile inputs. Remove the private deployment location from the new public comments before merging.
1. Blocking: .github/actionlint.yaml:3
# non-root ephemeral exec runners on our host (platform deploy/gha-runners, browser-bridge-exec-e1)
This comment publishes the internal runner deployment path and a specific runner instance name. The deployment path is also repeated in the new runner-selection comments across the workflows; neither detail is needed to declare the public runner label.
Suggested fix:
Remove the internal deployment path and instance name here and remove the deployment path from the repeated workflow comments. Keep the runner label and the explanation of which events use it.
rule:secret-exposure
…hosted docker-build-inputs listed changed paths with git diff --name-only, which reports only the destination of a rename. Moving a root module such as session-broker.mjs into a directory, out of the image's COPY *.mjs, read as no image input, so the image job was skipped and docker-build passed. The filter now uses --no-renames and -z, so a move lists both paths whole, and test/build-inputs.test.mjs runs the step's own script against scratch repositories, including that move. In a fork of this repository a push, schedule or dispatch carries no pull_request, so the runs-on expression chose browser-bridge-exec, which a fork does not have. The own-code expression now also sends any run outside askalf/browser-bridge to GitHub's runners, and fleet-status and its backfill use browser-bridge-exec only here.
CodeQL sizes itself to the machine, and the exec runners of every repo share one host, so concurrent analyses from several repos exhausted its memory. The analyze job runs on ubuntu-latest again, without the ram/threads caps it carried for our runner. scripts/fleet-status.test.mjs checks that codeql.yml stays on GitHub's runners.
sprayberry-redline
left a comment
There was a problem hiding this comment.
Verdict: approve. The runner expressions keep fork and Dependabot PR jobs on hosted runners while moving trusted jobs to the repository's runners. The Docker input filter matches the Dockerfile's sources, covers moves and non-ASCII paths in tests, and keeps the required check dependent on a successful image build when one is needed.
Docker reads a Dockerfile-specific ignore file, Dockerfile.dockerignore, in preference to .dockerignore. A PR that added one could drop the image's entry point while the input check said nothing changed, so the image smoke was skipped and the required docker-build passed.
sprayberry-redline
left a comment
There was a problem hiding this comment.
Verdict: approve. The runner expressions keep fork and Dependabot PR jobs on hosted runners, while the status jobs continue to check out the default branch. The image-input filter covers the Dockerfile's copied files and rename cases, and the required Docker check propagates input-check and image-build failures.
Minor:
scripts/fleet-status.test.mjs:21: This helper has no callers. The new runner tests inspect runs-on expressions directly, so the trigger-block parser adds unused code rather than test coverage.
Nothing calls it since the runner checks stopped reading triggers; CodeQL flagged it as an unused function.
sprayberry-redline
left a comment
There was a problem hiding this comment.
Verdict: approve. The runner expressions keep fork and Dependabot PR jobs hosted while routing same-repository work to the exec runners. The Docker input filter covers the Dockerfile's current inputs, including renames and its specific ignore file, with scratch-repository tests; the required check still fails if input detection or an owed image build fails.
Summary
Every required check here ran on GitHub's hosted runners, so each hosted outage held every PR. This follows askalf/dario#1531: our own code moves to browser-bridge-exec, the non-root one-job runners on our host. Fork PRs stay on GitHub's.
A single
runs-onexpression picks the runner per job. These resolve toubuntu-latest:Everything else (pushes, same-repo PRs, schedules, dispatches) runs on our runners.
Moved (own-code expression):
build.ymlunit-testsactionlinttruecopy-gate.ymlverify pinned tool surfacefleet-status-self-testhygienelabels.ymlmanifest matches the repofieldpass-ci.ymlfieldpass-testandfieldpass-test-node20Moved (this repository only, never runs PR code): these use browser-bridge-exec in askalf/browser-bridge and GitHub's runners in a fork repository.
fleet-status.yml, which postsfleet/verifyandfleet/reviewand runs only the default branch's script.fleet-status-backfill.yml, which runs only on dispatch..github/actionlint.yamldeclares thebrowser-bridge-execlabel, since actionlint knows only GitHub's runner labels.docker-build (required). The exec runners have no Docker daemon, because Docker on our host is root. The job is split three ways:
docker-build-inputs(our runner) checks which files changed. It lists paths withgit diff --no-renames -z, so moving a root*.mjsmodule into a directory, out of the Dockerfile'sCOPY *.mjs, counts as a change. A path with a non-ASCII byte is matched whole.docker-build-image(GitHub's runners) is the original build and boot smoke. It runs on every push to master. On a PR it runs only when a file the image is built from changed: the Dockerfile,.dockerignore,package.json/package-lock.json, the root*.mjsfiles the Dockerfile copies, orbuild.yml.docker-build, the required name, runs on our runner and passes only when the image job passed or was not owed.Left on GitHub's runners:
codeql.ymlanalyze (javascript-typescript): CodeQL sizes itself to the machine, and every repo's exec runners share one host, so concurrent analyses would exhaust its memory.stealth.yml: builds and runs the image (Docker), and is not a required check.fuzz.yml: not required, and CPU-heavy on a shared host. Same call as dario#1531.cflite.ymlandscorecard.yml: Docker-based actions.release.yml: multi-arch Docker build and push.fieldpass-publish.yml: npm trusted publishing (OIDC) needs a GitHub-hosted runner.pr-triage.yml(pull_request_target) andfleet-review-relay.yml(pull_request_review): they handle outside input. The relay only matters for fork PRs.Tests
scripts/fleet-status.test.mjsevaluates eachruns-onexpression with the evaluator that file already has. 216 pass locally.ubuntu-latest, as does every event in a fork repository. A same-repo PR, a push, a schedule and a dispatch resolve to browser-bridge-exec.fleet-status.ymlandfleet-status-backfill.ymluse it.codeql.ymlstays onubuntu-latest.test/build-inputs.test.mjs(new) runs thedocker-build-inputsstep's own script against scratch repositories. Each of these owes the image build:Modules in directories, tests and docs owe nothing. 7 pass locally. With
--no-renamesremoved from the step, the move test fails.scripts/check-hygiene.mjs --rangepasses on the commits.This PR's own checks run on browser-bridge-exec, since a same-repo PR uses its branch's workflow files. They are the live proof.