Rendered from docs/process/explanations/where-a-ci-job-runs.md in the Headwater
corpus. Every document on this half of the site is typed by the taxonomy
the descriptor names: corpus.json.
Where a CI job runs
Scope
This page states how .github/workflows/ci.yml chooses a runner for each job of one run, and which runs it cancels. It is for a contributor who reads a CI result and wants to know where the job ran and why. It states no new rule. Each fact comes from one of the eight process decisions that it draws on, or from ci.yml itself. Where this page and ci.yml disagree, ci.yml is correct and this page is stale.
The runner containers are out of scope. They are provisioned outside this repository (HW-PD-0019).
How it works
A run has three jobs: route, engine and headwater. The workflow starts on a push to any branch, on a pull request and on a merge queue run (merge_group). A push to a gh-readonly-queue/ branch of the merge queue starts no run (HW-PD-0020).
routemeasures the pool. It always runs onubuntu-latest. For any other event, it outputsoverflow=true. For a push or amerge_grouprun, it counts the queued and running jobs that ask for theheadwaterlabel. When no job waits, and the running jobs and the two jobs of this run fit inCI_SELF_HOSTED_SLOTS, it outputsoverflow=false. Otherwise it outputsoverflow=true. The default pool size is 3. Two errors are possible. When the script sees an error, it outputsoverflow=false. A failed API call and aCI_SELF_HOSTED_SLOTSthat is not a count are errors of this type. When the step fails in a way that the script cannot see, or passes its limit of two minutes, the output is empty. In both cases the run routes byCI_RUNNERalone. The router never fails the run (HW-PD-0018).- The job-level
if:removes a duplicate run.engineandheadwaterskip thepull_requestrun for a branch of this repository, because thepushrun on the same commit already gives the result. A pull request from a fork keeps its run (HW-PD-0015). runs-onchooses the runner. The expression is the same forengineandheadwater. A job gets the labels invars.CI_RUNNERonly when three conditions are true. The event is apushor amerge_group, the router did not outputoverflow=true, andCI_RUNNERis set. In every other case the job getsubuntu-latest(HW-PD-0013, HW-PD-0016).- A newer run cancels an older one on the same ref. The concurrency group is the workflow and
github.ref. A newer run cancels the older run on every ref exceptrefs/heads/main. Onmain, every run completes (HW-PD-0017).
The table gives the result for each case.
| event | CI_RUNNER |
router output | runner of engine and headwater |
|---|---|---|---|
push to any branch, or merge_group |
set | overflow=false or empty |
the labels in CI_RUNNER |
push to any branch, or merge_group |
set | overflow=true |
ubuntu-latest |
push to any branch, or merge_group |
not set | any | ubuntu-latest |
| pull request from a branch of this repository | any | any | skipped, and the push run answers |
| pull request from a fork | any | any | ubuntu-latest, after a maintainer approves the run |
A step that must act differently on the two runners reads runner.environment. No self-hosted job gets engine/target from an earlier job (HW-PD-0019).
To find which runner a push or a merge_group run uses today, run gh variable list and read CI_RUNNER.
Why it is this way
Each record below holds the reasons for one part of the design. This page does not repeat them.
- Only the event decides eligibility, so no field that the author of a pull request can change reaches
runs-on(HW-PD-0013). - The approval setting for outside contributors is the boundary against a fork, and no expression in
ci.ymlis (HW-PD-0014). - A condition may take work away and never grant it (HW-PD-0015).
- The self-hosted pool is an opt-in, and the default is a hosted runner (HW-PD-0016).
- Runs on
mainnever cancel, because each merge needs its own result (HW-PD-0017). - The router can send a job away from a full pool and never fails a run (HW-PD-0018).
- Only actions from the
actionsorganization run, and no job carries build state to the next (HW-PD-0019). - Every merge goes through the merge queue, so a
merge_grouprun is the check on each group of pull requests before it lands (HW-PD-0020).
When one of these records is superseded or withdrawn, headwater check reports this page through its draws_on edge, and this page must change with it.