chore: declare a supported Node.js range on the published packages - #445
Open
AmaadMartin wants to merge 2 commits into
Open
chore: declare a supported Node.js range on the published packages#445AmaadMartin wants to merge 2 commits into
AmaadMartin wants to merge 2 commits into
Conversation
added 2 commits
August 1, 2026 02:21
Declare "engines": {"node": ">=22.12.0"} on the three published
manifests (@google/adk, @google/adk-devtools, @google/adk-integrations)
so npm tells a consumer at install time whether their runtime is
supported. Previously no manifest declared engines, so installing on an
end-of-life Node produced no EBADENGINE warning at all.
22.12.0 is the policy floor: the technical minimum across the production
dependency closure is 20.6.0 (@opentelemetry/* declare
"^18.19.0 || >=20.6.0"), but Node 20 reaches end of life on 2026-04-30,
and warning people off EOL runtimes is the point of the change. Within
the supported LTS lines 22.12.0 is the lowest release the repository's
own toolchain accepts (vite@7.3.5 declares "^20.19.0 || >=22.12.0").
The lockfile hunk is the matching engines entry npm writes for each
workspace; no dependency versions change.
… drift The same range is now written in three files, so the realistic way this rots is one manifest being bumped and the other two left behind. Assert all three published workspaces declare the identical range. Lives under tests/integration/ because it spans all three workspaces rather than belonging to any one of them, following the tests/integration/build_setup/ precedent of resolving fixture paths off process.cwd().
This was referenced Aug 1, 2026
Open
Open
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.
Please ensure you have read the contribution guide before creating a pull request.
Link to Issue or Description of Change
N/A — no existing issue.
Problem: None of the three published manifests declares an
enginesfield. Verified on this checkout:Without
engines.node, npm has no machine-readable minimum Node version for@google/adk,@google/adk-devtoolsor@google/adk-integrations, so installing on an end-of-life Node release produces noEBADENGINEwarning at all. The packages are silently accepted on runtimes they are neither built nor tested against.Solution: Declare
"engines": {"node": ">=22.12.0"}on exactly those three manifests, plus one guard test that keeps the three values from drifting apart. This is consumer-facing metadata only — it adds no executable code path, no runtime Node-version check and no startup assertion.Why
>=22.12.0. Two separate numbers matter here and they are not the same:package-lock.json, the two binding constraints are@opentelemetry/*(27 packages) at^18.19.0 || >=20.6.0and@google/genai/@azure/*/@typespec/ts-http-runtime(13 packages) at>=20.0.0. Their intersection is Node >= 20.6.0.vite@7.3.5(pulled in byvitest) declaresengines.node: "^20.19.0 || >=22.12.0", whose 22-line floor is exactly 22.12.0. Verified from the lockfile, not from memory.This is a visible behaviour change, called out deliberately so it can be accepted or rejected consciously. Node 18 and Node 20 consumers will now see an
EBADENGINEwarning where they previously saw nothing. That is the intended outcome, not an oversight. It is not breaking on the default install path —enginesis advisory and the install still succeeds (demonstrated below, exit code 0). It is breaking only for a consumer who has explicitly opted intoengine-strict=trueand is on an out-of-range Node, which is the documented semantics of that opt-in. No exported symbol, type or signature moves. If a maintainer would rather keep Node 20 alive, the fix is a one-token change to"^20.19.0 || >=22.12.0"in the three manifests plus the test constant.CI is unaffected. Both
.github/workflows/validation.yamland.github/workflows/cross-language-integration.ymlinvokeactions/setup-node@v6with nowith:block at all.node-version-filehas no default, so the action does not readpackage.json,.nvmrcor.node-versionon its own; addingengines.nodecannot change which Node version CI runs on. This change deliberately does not repoint the workflows and does not add a.nvmrc.Scope. Deliberately limited to the three published manifests. Not touched: the root
package.json(workspace container, not published),README.md,.nvmrc/.node-version, either CI workflow,.npmrc(introducingengine-strictwould turn a warning into a hard failure for everyone),dev/src/cli/deploy/deploy_utils.ts(FROM node:lts-alpinealready resolves to a satisfying version), and thetests/**/package.jsonfixtures. Thepackage-lock.jsonhunk is only the threeenginesentries npm writes for the workspace packages — no dependency versions change.Collision check. Per process, open PRs on this fork were checked before writing any code:
This found #133 "Feat: Establish Node.js version source of truth (.nvmrc, engines.node, CI pins)" (branch
feat/node-version-source-of-truth), which overlaps: it also addsenginesto these three manifests. It differs in three ways that matter. (a) It declares>=22.0.0, which blesses 22.0–22.11 — releases the repository's own toolchain cannot run on, sincevite@7.3.5needs>=22.12.0on the 22 line. (b) It has no drift guard. (c) It also creates.nvmrc, repoints both workflows atnode-version-file: .nvmrc, and addsenginesto the root workspace container — all three explicitly out of scope here, and (b)/(c) are why this is not simply stacked on that branch. These two PRs conflict and only one should land. If #133 is preferred, this PR should be closed and #133 amended to>=22.12.0plus the guard test; if this one is preferred, #133 should be narrowed to its CI/.nvmrchalf. Adjacent PRs checked and cleared by file list: #443 (README.mdonly), #428/#416/#406 (CI workflow Node pinning only).Testing Plan
Please describe the tests that you ran to verify your changes. This is required for all PRs that are not small documentation or typo fixes.
Unit Tests:
[x] I have added or updated unit tests for my change.
[x] All unit tests pass locally.
One new file,
tests/integration/package_manifests/engines_test.ts, guarding the invariant that all three published manifests declare the same range — three copies of one value across three files is the realistic way this rots. It lives undertests/integration/because it spans all three workspaces rather than belonging to any one of them, following thetests/integration/build_setup/precedent of resolving paths offprocess.cwd().No existing test was modified, skipped or deleted. The change adds zero lines of production source (
core/src/,dev/src/,integrations/src/are untouched), so thevitest.config.tscoverage thresholds are unaffected — itscoverage.includeonly covers those three source trees. Noany, no@ts-expect-error, noeslint-disable, no coverage suppression was added anywhere in this diff.Proving the guard test can fail (two mutations). Both were run against the real runner and both failed as intended; the manifest was restored and re-verified green afterwards.
enginesblock fromdev/package.jsononly:dev/package.jsonto>=22.0.0while the other two stayed at>=22.12.0. This is the mutation that matters, because divergence (not absence) is the invariant being guarded, and a test that only caught a missing field would pass on a drifted one:Regression check — the existing build-setup suite, which really runs
npm install(andnpm run build) against these manifests throughfile:links, still passes:Manual End-to-End (E2E) Tests:
Machine used for all of the below:
node -v→v22.22.2,npm -v→9.2.0.node -vhere is 22.22.2, i.e. inside the declared range, so the correct result is noEBADENGINEline naming our packages:core/package.jsonwas temporarily set to>=99.0.0(a range the current runtime genuinely violates) and the same fixture install was re-run. This proves npm actually reads and reports the field from these manifests end to end:The install still exited 0 — confirming the warning is advisory and the default install path is not broken.
core/package.jsonwas then restored to>=22.12.0, re-verified withnpm pkg get, and the fixture'snode_modules/package-lock.jsonremoved; that temporary edit is not part of this diff.lint-stagedpre-commit hook (which runsprettier --writeover staged**/*.{json,md}), so the committed hunks do not get reshaped on someone else's commit:npm run ts:checkreports pre-existing errors acrosscore/test/**and twotests/integration/**files on this checkout; that is unchanged by this PR, andgrep package_manifestsover its output returns zero — the new file contributes no type errors.filesarray already listspackage.json):git diff --statagainst the branch base is exactly the intended files:Note on the PR title. It is intentionally prefixed
chore:rather thanfeat:/fix:, so that release-please does not cut a release for a metadata-only change. Since this repository squash-merges, the PR title becomes the commit message, so please preserve the prefix when merging.Checklist
[x] I have read the CONTRIBUTING.md document.
[x] I have performed a self-review of my own code.
[x] I have commented my code, particularly in hard-to-understand areas.
[x] I have added tests that prove my fix is effective or that my feature works.
[x] New and existing unit tests pass locally with my changes.