ci: read the Node version from .nvmrc and share one setup action#3753
Merged
Conversation
The Node version was hardcoded as `node-version: 24` in 16 places across 8
workflows, and nothing declared it for local development at all — CONTRIBUTING
said "Node.js 22+" while CI ran 24.
Add .nvmrc (24) and point every workflow at it via `node-version-file`. nvm,
fnm and mise all read the same file, so `nvm install` with no argument now
gives a contributor exactly what CI runs.
Collapse the repeated three-step preamble (pnpm/action-setup -> setup-node ->
`pnpm install --frozen-lockfile`) into .github/actions/setup, a composite
action with two inputs:
- install: skip the install for jobs that install later (ci.yml's
pr-a11y installs together with the Playwright browser,
after downloading an artifact).
- registry-url: release.yml's publish/canary jobs need setup-node to write
the auth config `npm publish` reads for OIDC.
ci.yml's pr-comment and pr-comment-update keep a bare setup-node. They read a
downloaded artifact and never install workspace deps, so pulling them into the
composite would restore the pnpm store cache for nothing.
Also git-ignore .node-version, .tool-versions and mise.toml, mirroring the
existing stanza that ignores package-lock.json / yarn.lock. A personal asdf or
mise file can now never drift from .nvmrc. The comment there spells out that
the line must be removed first if the team ever migrates, otherwise the new
file would be silently left out of the commit.
Verified:
- All 15 workflow/action YAML files parse.
- Every one of the 16 jobs runs actions/checkout before its setup step, so
.nvmrc and the local composite action are on disk when they are read.
- No `node-version:` literal remains anywhere under .github/.
Han5991
requested review from
cixzhang,
ejhammond,
imdreamrunner,
josephfarina,
marie-lucas,
nynexman4464,
thedjpetersen and
vjeux
as code owners
July 10, 2026 11:04
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
cixzhang
approved these changes
Jul 10, 2026
cixzhang
left a comment
Contributor
There was a problem hiding this comment.
Thanks this is good cleanup and helps coordinate our node versions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The Node version was hardcoded as
node-version: 24in 16 places across 8 workflows, and nothing declared it for local development at all —CONTRIBUTING.mdsaid "Node.js 22+" while every CI job ran 24.Two changes:
.nvmrc(24) is now the single source. Every workflow reads it vianode-version-file. nvm, fnm and mise all read the same file, sonvm installwith no argument gives a contributor exactly what CI runs.One composite action,
.github/actions/setup. The repeated three-step preamble —pnpm/action-setup→setup-node→pnpm install --frozen-lockfile— collapses into a singleuses:.Net: 96 insertions, 134 deletions.
Composite action inputs
installci.yml'spr-a11yinstalls later, together with the Playwright browser, because it has to download an artifact first. It passesinstall: 'false'.registry-urlrelease.yml'spublish/canaryjobs needsetup-nodeto write the auth confignpm publishreads for OIDC trusted publishing.pnpm/action-setupstays first inside the composite — pnpm must be onPATHbeforesetup-noderuns, orcache: pnpmcannot resolve the store path.Two jobs deliberately left alone
ci.yml'spr-commentandpr-comment-updatekeep a baresetup-node. They read a downloaded artifact and post a comment; they never install workspace deps. Routing them through the composite would restore the pnpm store cache for nothing. They still read.nvmrc, so the version stays in one place. A comment in the workflow explains this.Competing version files are now git-ignored
This mirrors the existing stanza that ignores
package-lock.json/yarn.lockwith the reason spelled out. The caveat in the comment is the important half:.gitignoredoes not prevent a commit, it silently drops one, so an intentional future migration has to remove the line first.Verification
actions/checkoutbefore its setup step. This is the load-bearing precondition:node-version-fileand a localuses: ./.github/actions/setupare both read from the checked-out tree, so a job that set up Node before checking out would break. Asserted by walking the parsed step lists rather than by eye.node-version:literal remains anywhere under.github/.release.yml'snode-version: 24 # bundles npm >= 11.5.1line is preserved — it now lives on the.nvmrcline inside the composite action and next to theregistry-urlinput inrelease.yml.Follow-ups (not in this PR)
packageManageranddevEngines.packageManagercoexist, so pnpm 11 prints"packageManager" will be ignoredon every command.CONTRIBUTING.md'spnpm --version # 10.x.xsnippet is stale; chore: drop the dead .npmrc and fix the stale pnpm version in CONTRIBUTING #3750 fixes it.