Run CI on Node 24 and raise the engines floor - #214
Open
francoisferrand wants to merge 5 commits into
Open
francoisferrand wants to merge 5 commits into
francoisferrand wants to merge 5 commits into
Conversation
Node 20 reached end-of-life in April 2026, so the manifest advertised support for a runtime nobody tests. Node 24 is Active LTS until April 2028 and is where we are heading. Both versions stay in the matrix while consumers straddle them; the floor moves to 24 in a later pass, once they have all moved over. Issue: GDL-16
markdownlint 0.31.1 dragged in markdown-it 13, and with it the linkify-it ReDoS advisory - the one audit finding every consumer of this package inherited. 0.38.0 drops markdown-it altogether, so the chain disappears from their trees rather than being pinned forward: an override here would not have reached them anyway. It ships as ESM, so the sync API now comes from markdownlint/sync. require() handles that natively, and the package stays CommonJS so consumers requiring the config are unaffected. MD059 is new in this release and fires on links that already exist in backbeat and cloudserver, so it is off until those are reworded. eslint and prettier move up to versions that support 24, and brace-expansion and flatted get re-resolved - the lockfile was holding them below what their ranges already allowed. Issue: GDL-16
require() only loads an ES module from Node 22.12 onwards, and utapi still pins 22.11.0 in CI, so it would hit ERR_REQUIRE_ESM the moment it picks up this release. A dynamic import keeps the script CommonJS and works on anything from Node 12 up, which costs nothing here and saves coordinating a runtime bump in another repo first. Issue: GDL-16
index.js requires globals but nothing declared it: installs only worked because eslint happens to pull it in transitively and yarn hoists it to the top level. Under pnpm, or if eslint ever drops that path, every consumer breaks at once. Pinning ^14 matches what is already resolved, so no second copy appears. eslint and prettier stay out of dependencies - consumers run their own, and a second eslint in the tree is how plugins end up loaded twice - but they belong in peerDependencies so a mismatch is visible at install rather than halfway through a lint run. prettier is optional: the config and prettier-diff are opt-in, and three consumers do not use them. Issue: GDL-16
commander.parse(process.argv).args is process.argv.slice(2) for every way this tool is called - no options are declared, and all four consumers invoke it as a bare file list. What it added was an auto-generated help screen with nothing in it, and rejection of unknown flags, at the cost of a runtime dependency shipped into every consumer's tree. Filenames starting with a dash used to be rejected as unknown options; they now lint like any other file. Issue: GDL-16
Contributor
Hello francoisferrand,My role is to assist you with the merge of this Available options
Available commands
Status report is not available. |
francoisferrand
requested review from
a team,
SylvainSenechal and
benzekrimaha
September 16, 2026 17:40
Contributor
Waiting for approvalThe following approvals are needed before I can proceed with the merge:
|
benzekrimaha
approved these changes
Sep 17, 2026
SylvainSenechal
approved these changes
Sep 17, 2026
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.
Node 20 went end-of-life in April 2026, but the manifest still advertised
>=20— support for a runtime nobody tests. CI now runs a 22/24 matrix andengines.nodemoves to>=22.Deliberately not
>=24: this config is consumed by repos still on 22, and raising the floor before they move would break their installs. That drops to>=24in a later pass, or when Node 22 goes EOL in April 2027.Dependencies
Went looking for bumps worth taking alongside the runtime change, and found more than expected.
markdownlint 0.31.1 → 0.38.0. 0.31.1 pinned markdown-it 13, which carries the linkify-it ReDoS advisory plus two of its own — the audit findings every consumer of this package inherited. 0.38.0 drops markdown-it entirely, so the chain leaves their trees rather than being pinned forward. An override here wouldn't have helped:
resolutionsare root-only and never ship to consumers.yarn auditgoes 26 → 0.Worth knowing: the replacement pulls in micromark + katex, so the production closure grows from 9 to 46 packages (~8.8MB, mostly a LaTeX renderer for
$math$support). That only lands in CI installs — every consumer has this as a devDependency — but expect a sloweryarn installand don't attribute it to whatever else lands that week.markdownlint is ESM-only now, so the sync API is imported dynamically. That keeps
mdlintCommonJS and working on older Node 22 patches whererequire()can't load an ES module — utapi still pins 22.11.0. MD059 is new in this release and fires on links that already exist in backbeat and cloudserver, so it's off until those are reworded.globalswas never declared.index.jsrequires it, and nothing in the manifest mentioned it — installs only worked because eslint happens to pull it in transitively and yarn hoists it to the top level. Under pnpm, or if eslint ever drops that path, every consumer breaks at once. Pinned^14.0.0to match what's already resolved, so no second copy appears.eslint and prettier are now peers. They stay out of
dependencies— consumers run their own, and a second eslint in the tree is how plugins end up loaded twice — but the package was shipping lint rules while declaring no opinion at all about what it plugs into. Scoped to^9rather than>=9since eslint 10 is untested here. prettier is optional: three consumers don't use it.commander dropped.
commander.parse(process.argv).argsisprocess.argv.slice(2)when no options are declared, and every consumer callsmdlintwith a bare file list. It bought an empty help screen.Verification
yarn install --frozen-lockfile,yarn testandyarn auditall clean on Node 22.11.0, 22.23.2 and 24.21.0. 22.11.0 is in there on purpose — it's utapi's pin and the one version where the dynamic import matters.Ships as 8.3.3, which is in the manifest but was never tagged.
Issue: GDL-16