Check every repeated tool version against one source - #1043
Open
necolas wants to merge 6 commits into
Open
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Vercel offers only major Node versions (24.x, 22.x, 20.x) and rolls out minor and patch updates itself, so `engines.node` selects the major and overrides the Project Settings value. Without the field, a build follows whatever default Vercel currently ships, which moves when Vercel changes it. Write the major, not an exact version: an exact pin reads as a promise Vercel cannot keep, and it would need an edit on every patch bump in .prototools. `24.x` tracks the pinned node major and nothing narrower.
…tools Rename assert-pnpm-version.ts to check-pnpm-binary.ts. Every other check script in this repo uses the `check-` prefix (check-licenses, check-tool-pins), and the new name says what the script inspects: the pnpm binary on PATH, not a file. That is the whole distinction from the file-comparing check added next. Renames the moon task with it. The script also parsed the pnpm pin out of .prototools and then compared it to a hard-coded '11.9.0'. So the file whose job is to catch drift was itself a third place the version lived, and a bump had to touch it. Compare the running pnpm against the parsed pin instead. That drops one check — pin against constant — which only ever fired when someone bumped .prototools without editing this file. Extract the .prototools reader into scripts/prototools.ts so the check added next shares it. The reader scans only the implicit top-level TOML table, so a key inside [plugins] or [settings] cannot be read as a tool pin. Also take the last version-shaped line of `pnpm --version` rather than the whole buffer: proto's shim prepends a notice when it has to resolve a version first, and prints it as NDJSON under AGENT=1. That made the guard report an unreadable version, and would have failed a publish outright on a correct pin. Committed with --no-verify: .oxlintrc.json ignores scripts/**, so lint-staged's oxlint lane errors when every staged JS/TS file is a script. root:format-check and root:lint were run by hand instead.
.prototools is the source of truth for every tool version, but four places cannot use proto and must repeat one: - versionConstraint in .moon/workspace.yml - the @moonrepo/cli catalog entry, for Vercel builders without proto - packageManager in the root package.json - .node-version Nothing enforced the sync, and moon drift was the expensive case: CI runs the proto-installed moon and never runs the npm one, so a stale @moonrepo/cli passed CI and then failed the Vercel deploy after merge, where moon rejects a versionConstraint mismatch. This check moves that failure onto the pull request and names every file to change. engines.node is checked by major only, because Vercel resolves the field to a major and picks the patch itself. The .node-version check exists because moon cannot generate that file here: syncVersionManagerConfig applies only when .moon/toolchains.yml sets an explicit node.version, which this repo omits on purpose so the version stays in .prototools alone. Recorded that in toolchains.yml. Committed with --no-verify: .oxlintrc.json ignores scripts/**, so lint-staged's oxlint lane errors when every staged JS/TS file is a script. root:format-check and root:lint were run by hand instead.
The skill told a reader to keep three moon pins in sync by hand. That is what drifted, and root:check-tool-pins now does it. Point at the command instead of restating the procedure. Keep the two facts a reader outside the company needs: .prototools is the only file to edit, and @moonrepo/cli must not be deleted as a duplicate version, because it is how moon reaches Vercel. This repo is public, so it cannot point at an internal skill for the rest.
Two versions in this repo were stated in two places with nothing keeping the copies together. Both are the same class as the moon pin that root:check-tool-pins already covers, so both go in the same script. - @types/bun in the pnpm-workspace.yaml catalog must match the bun pin in .prototools. Bun publishes the runtime and the types under one version, so a bun bump that skips the catalog gives types that do not match the runtime. A canary pin has no types of its own, so compare against the release it precedes. - The playwright@<version> argument in .github/workflows/ci.yml must match the @playwright/test catalog entry, or CI installs a browser the test runner does not drive. Here the catalog is the source, not .prototools, so the script now states which source each version answers to. Adds a catalogVersion() reader, which replaces the inline @moonrepo/cli regex, and reports a missing catalog entry rather than skipping the check. ci.yml joins the task inputs so the moon cache invalidates when it changes. Committed with --no-verify: .oxlintrc.json ignores scripts/**, so lint-staged's oxlint lane errors when every staged JS/TS file is a script. root:format-check and root:lint were run by hand instead.
necolas
force-pushed
the
toolchain-pin-consolidation
branch
from
August 1, 2026 05:20
548fbe7 to
dc8f502
Compare
The check compared seven copies against their source, but never read
.moon/toolchains.yml and did not list it as a task input. So a tool bump
that reintroduced node.version or pnpm.version there would pass. moon
runs on the explicit version, every copy stays consistent with every
other copy, and the check reports nothing — which is the drift it exists
to catch.
Reproduced before the fix: node.version '22.0.0' against a .prototools
node pin of 24.11.0 exited 0, and the success line printed "node
24.11.0" while moon would have run node 22.
This file needs a different rule from the rest. Everywhere else the
question is "does this copy match its source". Here the answer must be
that no copy exists, because a pin that agrees with .prototools is the
silent case — nothing is stale, so no comparison has anything to fail
on. So report presence and never compare a value. The block form and
the inline form `node: { version: '...' }` both count.
.moon/toolchains.yml joins the task inputs, or the moon cache replays a
stale pass.
Found by codex review on the port of this script to
pierredotco/monorepo, and fixed there first in 7a4cad70.
Committed with --no-verify: .oxlintrc.json ignores scripts/**, so
lint-staged's oxlint lane errors when every staged JS/TS file is a
script. root:format-check and root:lint were run by hand instead.
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.
.prototoolsis the one place to change a tool version. This adds aroot:check-tool-pinstask that compares every copy of a version against its source, and runs it in CI on every pull request.What it checks
versionConstraintin.moon/workspace.yml.prototoolsmoon@moonrepo/clicatalog entry.prototoolsmoonpackageManagerin the rootpackage.json.prototoolspnpm.node-version.prototoolsnodeengines.node, major only.prototoolsnode@types/buncatalog entry.prototoolsbunplaywright@<version>inci.yml@playwright/testcatalogOne file is checked the other way round.
.moon/toolchains.ymlmust pin noversion, because moon reads each one from
.prototools. Aversionthere is asecond pin that no comparison can catch: moon runs on it, so every copy stays
consistent with every other copy and nothing looks stale. So that file is checked
for absence.
Each failure names the file and the exact edit.
Why
A tool version lived in three places and nothing kept them together. moon is the expensive case. CI runs the proto moon and never the npm one, so a stale
@moonrepo/clipasses CI and then fails the Vercel deploy after merge. The check moves that failure onto the pull request.Also here
engines.nodeis now set, as24.x. Vercel honors only a major version and selects the patch itself, so an exact pin would need an edit on every patch bump and still not be honored.assert-pnpm-version.tsis nowcheck-pnpm-binary.ts. It reads the pin instead of repeating it. The name matches the other check scripts and says what it inspects: the binary on PATH, not a file.tooling-and-dependenciesskill andAGENTS.mdname the command instead of describing a manual sync.Two things not to "fix"
@moonrepo/climust stay. Vercel build containers have no proto, soapps/demo,apps/docs, andapps/diffshubeach callmoonfromnode_modules/.bin. It is not a duplicate to clean up..node-versionhere.syncVersionManagerConfigapplies only when.moon/toolchains.ymlsets an explicitnode.version, which this repo omits so the version stays in.prototools.Notes
format-check,lint,check-licenses,:typecheck, anddiffs:test(1478 tests) all pass, on each commit.--no-verify..oxlintrc.jsonignoresscripts/**, so lint-staged's oxlint lane errors when every staged JS/TS file is a script.root:format-checkandroot:lintwere run by hand. The same gap means no lint task covers these scripts.git-lfs. It is not installed and not pinned in.prototools. No commit here touches an LFS-tracked path.