11# Commit queue
22
3- _ tl;dr: You can land pull requests by adding the ` commit-queue ` label to it._
3+ _ tl;dr: You can ask the queue to land pull requests by adding the
4+ ` commit-queue ` label to them._
45
56Commit Queue is a feature for the project which simplifies the
67landing process by automating it via GitHub Actions. With it, collaborators can
7- land pull requests by adding the ` commit-queue ` label to a PR. All
8- checks will run via ` @node-core/utils ` , and if the pull request is ready to
9- land, the Action will rebase it and push to ` main ` .
8+ queue pull requests for landing by adding the ` commit-queue ` label to a PR. The
9+ selector checks readiness with ` @node-core/utils ` . If the pull request is only
10+ blocked on a deferrable condition, currently wait time, the queue leaves the
11+ label in place and retries later. Other failures continue to the existing
12+ landing and failure-reporting path.
1013
1114This document gives an overview of how the Commit Queue works, as well as
1215implementation details, reasoning for design choices, and current limitations.
1316
1417## Overview
1518
16- From a high-level, the Commit Queue works as follow:
17-
18- 1 . Collaborators will add ` commit-queue ` label to pull requests ready to land
19- 2 . Every five minutes the queue will do the following for each mergeable pull request
20- with the label:
21- 1 . Check if the PR also has a ` request-ci ` label (if it has, skip this PR
19+ From a high-level, the Commit Queue works as follows:
20+
21+ 1 . Collaborators will add ` commit-queue ` label to pull requests they want the
22+ queue to land. The label can be added before the pull request has completed
23+ its wait time, or before requested CI has finished. Required approvals must
24+ already be in place. The commit queue does not request CI on its own.
25+ 2 . On each scheduled run, the queue builds a candidate list from open pull
26+ requests with the ` commit-queue ` label and without the ` blocked ` label. The
27+ workflow uses a five-minute cron, but GitHub Actions scheduled workflows are
28+ not guaranteed to run exactly every five minutes. For each candidate, the
29+ queue will:
30+ 1 . Run a metadata-only readiness check that uses ` @node-core/utils ` without
31+ checking out the repository
32+ 2 . If the metadata check exits with a deferrable readiness code, meaning
33+ the PR is only blocked on wait time, keep the ` commit-queue ` label and
34+ skip this PR until a later queue run
35+ 3 . Check if the PR also has a ` request-ci ` label (if it has, skip this PR
2236 since it's pending a CI run)
23- 2 . Check if the last Jenkins CI is finished running (if it is not, skip this
24- PR)
25- 3 . Remove the ` commit-queue ` label
26- 4 . Run ` git node land <pr> --oneCommitMax `
27- 5 . If it fails:
28- 1 . Abort ` git node land ` session
29- 2 . Add ` commit-queue-failed ` label to the PR
30- 3 . Leave a comment on the PR with the output from ` git node land `
31- 4 . Skip next steps, go to next PR in the queue
32- 6 . If it succeeds:
33- 1 . Push the changes to nodejs/node
37+ 4 . Check whether GitHub checks are still running (if they are, skip this PR)
38+ 5 . Remove the ` commit-queue ` label and run ` git node land `
39+ 6 . If it fails:
40+ 1 . Add the ` commit-queue-failed ` label to the PR
41+ 2 . Leave a comment on the PR with the output from ` git node land `
42+ 3 . Abort the ` git node land ` session. If the abort succeeds, continue to
43+ the next PR; otherwise, stop the queue in an unknown state
44+ 7 . If it succeeds:
45+ 1 . Push or merge the changes into nodejs/node
3446 2 . Leave a comment on the PR with ` Landed in ... `
3547 3 . Close the PR
3648 4 . Go to next PR in the queue
@@ -51,18 +63,23 @@ of the commit queue:
5163 guidelines or be a valid [ ` fixup! ` ] ( https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---fixupamendrewordltcommitgt )
5264 commit that will be correctly handled by the [ ` --autosquash ` ] ( https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---autosquash )
5365 option
54- 2 . A CI must've ran and succeeded since the last change on the PR
66+ 2 . A CI must have run and succeeded since the last change on the PR
55673 . A collaborator must have approved the PR since the last change
56684 . Only Jenkins CI and GitHub Actions are checked (V8 CI and CITGM are ignored)
57695 . The PR must target the ` main ` branch (PRs opened against other branches, such
5870 as backport PRs, are ignored)
5971
6072## Implementation
6173
62- The [ action] ( ../../.github/workflows/commit-queue.yml ) will run on scheduler
63- events every five minutes. Five minutes is the smallest number accepted by
64- the scheduler. The scheduler is not guaranteed to run every five minutes, it
65- might take longer between runs.
74+ The [ action] ( ../../.github/workflows/commit-queue.yml ) runs on scheduled events.
75+ It uses a five-minute cron because that is the smallest interval accepted by
76+ GitHub Actions. Scheduled workflows are not guaranteed to run exactly at that
77+ cadence and might take longer between runs.
78+
79+ The workflow also uses a concurrency group so only one commit queue run can be
80+ active at a time. If a scheduled run starts while a previous run is still
81+ running, GitHub Actions keeps at most one pending run for the same concurrency
82+ group. A newer pending run replaces an older pending run.
6683
6784Using the scheduler is preferable over using pull\_ request\_ target for two
6885reasons:
@@ -76,41 +93,78 @@ reasons:
7693 commit, meaning we wouldn't be able to use it for already opened PRs
7794 without rebasing them first.
7895
79- ` @node-core/utils ` is configured with a personal token and
80- a Jenkins token from
81- [ @nodejs-github-bot ] ( https://github.com/nodejs/github-bot ) .
82- ` octokit/graphql-action ` is used to fetch all pull requests with the
83- ` commit-queue ` label. The output is a JSON payload, so ` jq ` is used to turn
84- that into a list of PR ids we can pass as arguments to
85- [ ` commit-queue.sh ` ] ( ../../tools/actions/commit-queue.sh ) .
86-
87- > The personal token only needs permission for public repositories and to read
88- > profiles, we can use the GITHUB\_ TOKEN for write operations. Jenkins token is
96+ ` @node-core/utils ` is configured with a personal token and a Jenkins token from
97+ [ @nodejs-github-bot ] ( https://github.com/nodejs/github-bot ) . The workflow starts
98+ with a small selector step that uses GitHub CLI to fetch pull requests with the
99+ ` commit-queue ` label. It first fetches the same age-based and fast-track buckets
100+ the queue used before accepting early queue requests, then fetches the broader
101+ queue and de-duplicates the result. This keeps not-yet-ready PRs from crowding
102+ out PRs that the previous query would have selected if GitHub paginates or caps
103+ a query result.
104+
105+ If there are candidate PRs, the selector installs ` @node-core/utils ` , downloads
106+ the target branch's README without checking out the repository, and runs
107+ ` git node metadata --readme --json ` for each candidate. This uses the same
108+ ` @node-core/utils ` PR readiness checks as ` git node land ` , but does not clone,
109+ fetch, or merge the PR. The selector consumes the structured metadata result
110+ and its exit code instead of matching human-readable output:
111+
112+ * exit code ` 0 ` : the PR is ready and is passed to
113+ [ ` commit-queue.sh ` ] ( ../../tools/actions/commit-queue.sh )
114+ * exit codes ` 20 ` -` 29 ` : the PR is not ready for a deferrable metadata reason,
115+ currently wait time, so it keeps the ` commit-queue ` label and is retried
116+ later
117+ * exit codes ` 40 ` -` 49 ` : the PR has a hard or mixed metadata readiness failure
118+ and is passed to [ ` commit-queue.sh ` ] ( ../../tools/actions/commit-queue.sh )
119+
120+ The ` 20 ` -` 29 ` exit code range is reserved by ` @node-core/utils ` for deferrable
121+ metadata readiness states, and ` 40 ` -` 49 ` is reserved for hard metadata failure
122+ states. Unknown selector failures fail the workflow before starting the landing
123+ job and leave PR labels unchanged so the queue can retry on a later scheduled
124+ run. PRs passed through with exit code ` 40 ` -` 49 ` continue through
125+ ` commit-queue.sh ` . The script still applies its existing ` request-ci ` and
126+ pending-check deferrals before removing the queue label and reporting a hard
127+ failure.
128+
129+ > The personal token needs permission for public repositories and to read
130+ > profiles. It is used by ` @node-core/utils ` and by the landing job for
131+ > checkout, label and comment updates, merging, and pushing. Jenkins token is
89132> required to check CI status.
90133
91134` commit-queue.sh ` receives the following positional arguments:
92135
931361 . The repository owner
941372 . The repository name
95- 3 . The Action GITHUB\_ TOKEN
96- 4 . Every positional argument starting at this one will be a pull request ID of
138+ 3 . Every positional argument starting at this one will be a pull request ID of
97139 a pull request with commit-queue set.
98140
99- The script will iterate over the pull requests. ` ncu-ci ` is used to check if
100- the last CI is still pending, and calls to the GitHub API are used to check if
101- the PR is waiting for CI to start (` request-ci ` label). The PR is skipped if CI
102- is pending. No other CI validation is done here since ` git node land ` will fail
103- if the last CI failed.
104-
105- The script removes the ` commit-queue ` label. It then runs ` git node land ` ,
106- forwarding stdout and stderr to a file. If any errors happen,
107- ` git node land --abort ` is run, and then a ` commit-queue-failed ` label is added
108- to the PR, as well as a comment with the output of ` git node land ` .
109-
110- If no errors happen during ` git node land ` , the script will use the
111- ` GITHUB_TOKEN ` to push the changes to ` main ` , and then will leave a
112- ` Landed in ... ` comment in the PR, and then will close it. Iteration continues
113- until all PRs have done the steps above.
141+ The script will iterate over the pull requests. GitHub CLI is used to check if
142+ the PR is waiting for CI to start (` request-ci ` label) or still has pending
143+ GitHub checks. The PR is skipped if CI is pending. No other CI validation is
144+ done here since ` git node land ` will fail if the last CI failed.
145+
146+ The script removes the ` commit-queue ` label, then runs ` git node land ` ,
147+ forwarding stdout and stderr to a file. PRs that are only blocked on wait time
148+ should have already been filtered by the metadata check. If a hard readiness
149+ failure appears between the selector job and ` git node land ` , the landing job
150+ adds a ` commit-queue-failed ` label to the PR, leaves a comment with the output
151+ of ` git node land ` , and then aborts the landing session. If the abort fails,
152+ the queue stops instead of continuing in an unknown state.
153+
154+ Fast-tracked PRs use the metadata check before the landing job. If the
155+ fast-track request has not yet received enough collaborator thumbs-up, the queue
156+ keeps the ` commit-queue ` label and retries until either the fast-track request
157+ is approved or the PR becomes landable through the regular wait-time rules. The
158+ commit queue does not create the fast-track request comment; that is handled
159+ when the ` fast-track ` label is added. If that comment is missing, the queue
160+ reports the failure instead of keeping the PR queued.
161+
162+ If no errors happen during ` git node land ` , the script either pushes the direct
163+ rebase landing to ` main ` or uses GitHub's squash merge API for single-commit and
164+ fixup landings. It then leaves a ` Landed in ... ` comment in the PR. GitHub
165+ closes PRs merged through the merge API automatically; for direct pushes, the
166+ script closes the PR. Iteration continues until all PRs have done the steps
167+ above.
114168
115169## Reverting broken commits
116170
0 commit comments