Traduction incomplète
Cette page n'est pas encore entièrement traduite : les passages non traduits sont affichés en anglais.
0040. Merge queue¶
Status: accepted
Date: 2026-11-08
Context¶
The main ruleset requires pull requests to be up to date with main before
they merge (ADR 0014). After every merge, each open pull request has to receive
a merge commit from main (« Update branch ») and run the whole CI again, one
after the other, before the next one can merge.
GitHub’s merge queue does that work itself. It is available in any public
repository owned by an organization. The maintainer queues pull requests whose
checks pass; for each one, GitHub builds a temporary branch
gh-readonly-queue/main/pr-<number>-<sha> that holds main, the pull requests
ahead in the queue and this one, runs the required checks on it through the
merge_group event, and merges the entries in order. A required check that
does not report on that branch blocks the queue; a job that is skipped counts
as passing.
Decision¶
main is merged through the merge queue. Every workflow that reports a required
check also runs on merge_group (types: [checks_requested]), and each check
keeps the same name, so the queue waits for the same thirteen checks as a pull
request. A test (tests/test_ci_scripts.py) fails when one of these workflows
loses the trigger.
What each check does on a queue entry:
Check |
On the queue entry |
|---|---|
|
Run as on a pull request, on the code of the entry: what lands on |
|
Runs on the workflows of the entry. The SARIF upload job stays limited to |
|
The action reads |
|
Builds, smoke-tests and scans the image of the entry. A vulnerability published while the pull request waited is found here. Its SARIF upload stays limited to |
|
Builds amd64 and arm64 again. It takes about three minutes, like |
|
Builds and checks the site of the entry. |
|
Reads the title of the queued pull request again from the API and checks it. A title edited after queuing, which becomes the squash commit message, is still checked before it reaches |
|
Reads the queued pull request from the API, fetches its head ( |
|
Same as |
|
A separate job of |
No check passes without checking. The four checks that read the pull request
find it from the name of the queue branch (scripts/merge_queue.py, and the
same pattern in cla.js), and fail when the branch is not a queue branch of
main or when the pull request is not open against main. Each queue entry
names one pull request and runs its own checks, so with the grouping that
requires every entry to pass (« only merge non-failing pull requests »), each
pull request of a group is checked on its own entry.
Code scanning. CodeQL runs in default setup, which analyzes pull requests,
pushes to main and a weekly schedule; it cannot run on merge_group. The
ruleset’s code_scanning rule is not a status check, and GitHub documents that
it does not apply to merge queue entries. A pull request is still analyzed and
gated before it can be queued, and main after each merge. What is lost is
the analysis of the exact combination before the merge; restoring it means an
advanced CodeQL workflow with a merge_group trigger, which replaces default
setup.
Settings of the queue (ruleset main, applied by the maintainer): squash,
as today; only non-failing pull requests are merged (ALLGREEN); up to five
entries built and merged at a time; no minimum group size; one hour for the
checks to report. The rule « branches must be up to date » is removed: the queue
tests every entry on the latest main.
Consequences¶
The maintainer no longer updates pull request branches after each merge. « Merge when ready » queues a pull request once its checks pass; GitHub merges it once the checks of its entry pass, or removes it from the queue and says why.
Every merge runs the required checks twice, once on the pull request and once on its entry: about three more minutes of waiting per entry (the longest jobs, the two image builds), on the free runners of a public repository.
Push nothing to a queued pull request: remove it from the queue first. The checks that read the pull request read its head when they run, and a push while it is queued is not a case this design relies on.
The commit on
mainis the squash commit created by the queue, as it was the squash commit created by the merge button.A new required check needs its workflow to run on
merge_groupbefore it is added to the ruleset, or the queue waits for it until the timeout.