Skip to content

ci: build on Node 22 to match deploy-docs and translations - #118

Merged
os-elon merged 2 commits into
mainfrom
claude/issue-103-ci-node-version
Aug 18, 2026
Merged

ci: build on Node 22 to match deploy-docs and translations#118
os-elon merged 2 commits into
mainfrom
claude/issue-103-ci-node-version

Conversation

@os-elon

Copy link
Copy Markdown
Collaborator

Fixes#103

What changed

One line in .github/workflows/ci.yml:

- node-version: 20+ node-version: 22

ci.yml is the repository's only required check and it now gates a merge queue. It pinned
the toolchain Node at 20 while deploy-docs.yml and translations.yml both pin 22, so the
gating check validated the site on a Node the deploy never uses — the wrong way round for a
check whose job is to catch what deploy would hit. Node 20 also reached end-of-life on
2026-04-30.

Deliberately untouched in this file: the action versions (actions/checkout@v7,
pnpm/action-setup@v4, actions/setup-node@v4), the merge_group: trigger, and the job
steps. Nothing here changes what runs, only which Node runs it.

Not folded in: the action-runtime deprecation

CI logs a separate warning that actions/setup-node@v4 and pnpm/action-setup@v4 "are
being forced to run on Node.js 24". That is the Node the action code itself executes on,
chosen by GitHub from the action's major version — a different thing from the node-version:
input, which selects the toolchain Node that runs pnpm, turbo and next. This PR changes only
the second. Bumping the action majors is proposed separately in #58 and #56 and is not
addressed here.

The engines question, answered

The card asked whether root package.json"engines": { "node": ">=20" } should move too,
and framed the argument as "track the product this repository documents". I checked, and the
answer is yes — but for a stronger and more local reason than the product floor.

The product floor is real but does not by itself bind this repo: content/docs/resources/changelog.mdx
states engines.node is >=22.0.0 across every published @objectstack/* package since 17.0,
and quickstart.mdx and resources/faq.mdx tell readers Node 22 or newer. This repository,
however, depends on no @objectstack/* package at all — it is a Fumadocs site. So the
product's floor is a documentation fact here, not a runtime constraint, exactly as the card
suspected.

This repository's own dependency tree is the binding evidence. Three packages already in
pnpm-lock.yaml declare engines: {node: '>=22.0.0'}:

PackageHow it gets here
wrangler@4.95.0direct devDependency of apps/docs
miniflare@4.20260526.0via wrangler
@cloudflare/kv-asset-handler@0.5.0via wrangler

and next@16.2.6 declares >=20.9.0. So ">=20" is false twice over on this repo's own
terms: it admits Node 20.0.0 through 20.8.x, which Next 16 rejects, and it admits all of
Node 20, which the wrangler that builds and deploys the site rejects.

Four declarations still say 20 and none of them are in this diff:

DeclarationValue
package.jsonengines.node>=20
apps/docs/package.jsonengines.node>=20.0.0
tools/ci-scripts/package.jsonengines.node>=20.0.0
.node-version20

.node-version is the one nobody had noticed. It is inert in CI — all three workflows pass an
explicit node-version:, so setup-node never reads the file — but version managers (fnm,
nvm, asdf) do read it, so a contributor following the repo lands on the exact Node its own
pnpm install is about to reject.

Why none of that is in this PR. These are one coherent unit and this card's declared file
surface is the node-version line. Bumping root engines alone leaves apps/docs — the
package that actually holds wrangler — still declaring >=20.0.0, which is the half-fix that
reads as done. Bumping engines without .node-version tells a contributor's version manager
to install a Node the install step then refuses. So the alignment is filed as its own issue with
this evidence rather than ridden in here. tools/ci-scripts is the one that should arguably
stay at >=20.0.0: it has no dependencies and runs plain node run-self-tests.mjs, so its
floor is not false.

Verification

Local union at 13a1da6, on Node v22.22.2 — the version this PR moves CI to:

GateExit
pnpm install --frozen-lockfile0
pnpm turbo run type-check --continue --force0
pnpm turbo run build --force0
pnpm turbo run test --force0
node .github/scripts/check-translations.mjs0
check-translation-ownership.mjs --actor os-elon --files changed.txt0
check-translation-output.mjs --files changed.txt0

--force is load-bearing, not habit. The first cached run replayed
@objectos/docs:type-check logs from /home/user/objectos-issue-107/apps/docs — a sibling
agent's worktree
, the shared-cache hazard AGENTS.md documents. Every result above is
cache bypass, force executing with paths in this PR's own tree.

A local green is weaker evidence than the real thing regardless, since it exercises Node 22
without exercising this file. The claim this PR actually has to make is that the build job
resolves 22 in its own CI run; that is checked on the run this PR triggers, not locally.

Generated by Claude Code


Generated by Claude Code

`ci.yml` pinned `node-version: 20` while `deploy-docs.yml` and
`translations.yml` both use 22, so the repository's only required check
validated the site on a Node the deploy never uses. Node 20 also reached
end-of-life on 2026-04-30.
Only the `node-version` line changes. Action versions, `merge_group:` and
the job steps are deliberately untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Yaqu7kmKZM3tRPd9Y4xivo
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CI builds the docs site on Node 20 while the product requires Node 22 and the other two workflows use 22

2 participants

@os-elon@claude