Skip to content

ci: read the Node version from .nvmrc and share one setup action#3753

Merged
cixzhang merged 1 commit into
facebook:mainfrom
Han5991:chore/nvmrc-node-version
Jul 10, 2026
Merged

ci: read the Node version from .nvmrc and share one setup action#3753
cixzhang merged 1 commit into
facebook:mainfrom
Han5991:chore/nvmrc-node-version

Conversation

@Han5991

@Han5991 Han5991 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

What

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.md said "Node.js 22+" while every CI job ran 24.

Two changes:

  1. .nvmrc (24) is now the single source. Every workflow reads it via node-version-file. nvm, fnm and mise all read the same file, so nvm install with no argument gives a contributor exactly what CI runs.

  2. One composite action, .github/actions/setup. The repeated three-step preamble — pnpm/action-setupsetup-nodepnpm install --frozen-lockfile — collapses into a single uses:.

Net: 96 insertions, 134 deletions.

Composite action inputs

input why
install ci.yml's pr-a11y installs later, together with the Playwright browser, because it has to download an artifact first. It passes install: 'false'.
registry-url release.yml's publish / canary jobs need setup-node to write the auth config npm publish reads for OIDC trusted publishing.

pnpm/action-setup stays first inside the composite — pnpm must be on PATH before setup-node runs, or cache: pnpm cannot resolve the store path.

Two jobs deliberately left alone

ci.yml's pr-comment and pr-comment-update keep a bare setup-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

# Node version managers (.nvmrc is the single source — CI reads it via
# node-version-file). Ignored so a personal asdf/mise/nodenv file cannot drift
# from it. If the team ever moves to one of these, delete the matching line
# here first — otherwise the new file is silently left out of the commit.
.node-version
.tool-versions
mise.toml

This mirrors the existing stanza that ignores package-lock.json / yarn.lock with the reason spelled out. The caveat in the comment is the important half: .gitignore does not prevent a commit, it silently drops one, so an intentional future migration has to remove the line first.

Verification

  • All 15 workflow + action YAML files parse.
  • Every one of the 16 jobs runs actions/checkout before its setup step. This is the load-bearing precondition: node-version-file and a local uses: ./.github/actions/setup are 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.
  • No node-version: literal remains anywhere under .github/.
  • The OIDC rationale that used to sit on release.yml's node-version: 24 # bundles npm >= 11.5.1 line is preserved — it now lives on the .nvmrc line inside the composite action and next to the registry-url input in release.yml.

Follow-ups (not in this PR)

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/.
@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
astryx Ready Ready Preview, Comment Jul 10, 2026 11:07am

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Jul 10, 2026

@cixzhang cixzhang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks this is good cleanup and helps coordinate our node versions.

@cixzhang
cixzhang merged commit 37c19d3 into facebook:main Jul 10, 2026
15 checks passed
@Han5991
Han5991 deleted the chore/nvmrc-node-version branch July 11, 2026 00:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants