Skip to content

Merging and releasing

How a change reaches develop, and how a release decides that a commit on develop is fit to ship. Testing covers what the checks themselves run.

The CI workflows

Workflow Runs on What it does
ci.yml Pull requests, merge queue entries, dispatch The build, unit tests and Biome; the pre-commit hooks; the editor tests; the automated reviewer on pull requests; and the "Can enqueue" and "Can merge" checks the merge queue requires.
secret-scan.yml Pull requests, merge queue entries, pushes to develop, dispatch gitleaks over the history.
codeql-analysis.yml Pull requests, merge queue entries, pushes, weekly CodeQL.
release.yml A v* tag, dispatch The release, as below.

On a pull request, ci.yml's jobs skip a change that touches only review marks, and skip a pull request from a fork altogether. A merge queue entry skips nothing.

Merging

Pull requests merge through a merge queue on develop, not with the merge button directly. A pull request can be queued once its "Can enqueue" check passes. The queue then runs ci.yml again on the commit it is about to merge, and merges only when "Can merge" passes, which requires every job to have run and succeeded. That second run is the one a release counts (see below), so a change merged around the queue, by an administrator bypassing it, cannot be released until a later commit has gone through the queue.

One thing does go around the queue routinely: prune-reviews.yml pushes its daily "Prune and import review marks." commit straight to develop as shakenfist-bot, which is why that bot stays on the ruleset's bypass list. Those commits change only REVIEWS.md and the weAudit files under .vscode/, and ci.yml does not run on them. The release gate steps across exactly such a commit to its first parent, so a tag on one that sits on top of a queue merge is judged by that merge's queue run.

The queue builds one entry at a time (max_entries_to_build: 1 in the develop ruleset). The concurrency groups in ci.yml, secret-scan.yml and codeql-analysis.yml depend on that: a queue run shares a group with whatever the queue builds next, so that a superseded run is cancelled. Raising the setting would make each entry cancel the one ahead of it, so revisit those groups first.

A new job in ci.yml goes in the needs: of both "Can enqueue" and "Can merge", or the queue will merge without it.

Enqueueing a fork's pull request is the point at which its code first runs here. On the pull request itself ci.yml skips a fork, but a merge queue entry is not a pull request event: the queue builds the commit on a branch of this repository, from the workflow files in that commit. So the queued commit runs all of its own code -- workflows, package-lock.json, the pre-commit config and the tests -- on the persistent static runners, and it runs with this repository's secrets. Among them is DEPENDENCIES_TOKEN, which prune-reviews.yml uses to push to develop as shakenfist-bot, around the queue. A workflow the fork edits can also ask for any GITHUB_TOKEN permission the repository allows. Treat a fork's change under .github/workflows/ as a request for those credentials, and review the whole diff with that in mind before enqueueing it; changes under .github/ and tools/ deserve the closest reading.

Releasing

Pushing a v* tag triggers release.yml. Its first job, ci-gate, checks that ci.yml succeeded on the tagged commit in its merge queue run, and nothing is built or published unless it did. How the gate decides is documented with the action, as require-ci-success. It has no override.

So tag the merge commit the queue made, after its ci.yml run has finished. A run started by hand, as pr-retest.yml does with workflow_dispatch, does not count. A queue run that fails, flaky or not, ejects its entry, so the commit it built never reaches develop and re-running that run cannot make any tag pass: re-queue the pull request and tag the commit that merges. Re-running a queue run helps only when the tagged commit's run was re-run after it merged and that later attempt failed, because the gate judges each run at its latest attempt; then use "Re-run jobs" on it, then re-run ci-gate. A gate failure means nothing was published, so every failure is recoverable; the template's recovery table says what to do for each cause.

Running release.yml by hand with workflow_dispatch skips the gate and the publishing jobs, and only builds the .vsix.

The one-time setup, the jobs after the gate, and how to cut a release step by step are in RELEASE-SETUP.md.

📝 Report an issue with this page