Fix: declare dotenv in the workspace root manifest - #602
Open
AmaadMartin wants to merge 1 commit into
Open
Conversation
Twenty of the 26 suites under tests/e2e statically import dotenv, but tests/ belongs to no workspace, so the import resolves only as a side effect of npm hoisting the dev workspace's dependency to the repository root. If @google/adk-devtools ever drops dotenv, npm prunes the hoisted copy and those suites stop resolving their import at collection time -- before their credential skipIf gates can run. Declare dotenv in the root devDependencies at the same range the dev workspace uses, so the manifest that owns tests/ states the requirement it actually has. The installed tree is unchanged: dotenv@17.4.2 already satisfies ^17.2.3 and stays the single root copy.
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
Link to an existing issue (if applicable):
No existing issue.
Or, if no issue exists, describe the change:
Problem:
tests/importsdotenvbut no manifest that ownstests/declares it.20 of the 26 suites under
tests/e2e/**contain a top-levelimport * as dotenv from 'dotenv'.tests/is not in the rootworkspacesarray (["core", "dev", "integrations"]), so those bare specifiers resolve by Node's upwardnode_moduleswalk, terminating at the repository-rootnode_modules. Nothing in the rootpackage.jsonasks fordotenv— the only declaration in the repo isdev/package.json:67("dotenv": "^17.2.3", a real runtime dependency of@google/adk-devtools, used bydev/src/cli/cli.tsanddev/src/cli/cli_create.ts). npm hoists a single copy to the root and the e2e tree picks it up incidentally.So the e2e tree depends on an undeclared, incidental property of another workspace's dependency list. If
devever drops or renamesdotenv, npm prunes the hoisted root copy and 20 suites stop resolving. Two details make that worse than it sounds:describe.skipIf(!hasAKey)), but thedotenvimport is a static top-level import. Resolution happens at collection time, before any skip logic runs — so the failure would hit even on machines with no API keys, including CI..github/workflows/validation.yamlrunsnpm installthennpm run test:coverage, which includes--project e2e.Solution: declare
dotenvin the rootdevDependenciesat the same range thedevworkspace uses (^17.2.3), and record it in the lockfile.This is manifest hygiene, not a behaviour change.
devDependencies(notdependencies) because the root package is the monorepo shell anddotenvis needed only to run tests from it. The range is byte-identical todev/package.jsondeliberately — a different range is how you end up with two installed copies later.Precedent: commit
00f37755, "fix(deps): hoist@google/genai2.x to the workspace root" (google#564), solved this exact class of problem the same way — one line added to rootdevDependenciesplus the lock refresh. That commit is why this uses rootdevDependenciesrather than inventing atests/package.jsonor adding atestsworkspace entry.Deliberately out of scope: four other packages that
tests/imports rely on the same hoisting (zod,@google-cloud/vertexai,openapi-types,@modelcontextprotocol/sdk). They are tracked separately so this diff stays a one-line manifest change.Overlapping-PR disclosure (collision check): before implementing I checked all 500 open PRs on the fork and diff-checked 13 plausibly adjacent manifest/lockfile PRs (#250, #338, #345, #415, #425, #467, #507, #541, #544, #549, #575, #170) for an added
dotenvline. One overlaps: #360 ("Fix: declare the root workspace's test dependencies in package.json") inserts this identical line, bundled with four other packages (@google-cloud/vertexai,@mikro-orm/core,openapi-types,zod) in the same commit. At the time of writing #360 is open, unreviewed, andCONFLICTINGagainstmain. These two PRs will textually conflict on this one line — whichever lands first, the other should drop thedotenvhunk (or be closed as redundant). This PR is the narrow, single-purpose version; #360 is the bundled one. Flagging it so a maintainer picks deliberately rather than discovering the conflict at merge time.Diff — exactly two files, two insertions:
The lock delta is the matching key in
packages[""].devDependencies. No package was added, removed, or re-resolved, and no"dev"flag changed anywhere in the lock.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.
No unit test accompanies this change, deliberately. It adds zero executable lines, so line/branch coverage over it is undefined rather than 100% — there is nothing for a unit test to execute, and a test asserting the contents of
package.jsonwould be a tautology with no precedent in this repo (the@google/genaiequivalent, google#564, shipped with no test). Coverage thresholds invitest.config.tsare untouched and unaffected: the coverageincludeglobs cover onlycore/src,dev/src, andintegrations/src.What replaces coverage is a falsification experiment — the "prove the test can fail" analogue. It is what demonstrates this line is load-bearing rather than cosmetic, and a reviewer cannot reconstruct it from the diff.
Falsification experiment (run in a throwaway directory outside the worktree, since deleted). Both arms simulate the refactor this change defends against:
"dotenv": "^17.2.3"deleted from the copieddev/package.json, thennpm install --package-lock-only --ignore-scripts. The arms differ only in whether the root manifest declaresdotenv.dotenv?packages["node_modules/dotenv"]node_modules/@mikro-orm/core/node_modules/dotenv@17.3.1survives, which files undertests/cannot resolve. This is the bug.17.4.2, still present. This is the fix.The control arm is the failure this change prevents: in a real install,
tests/e2e/**would fail withCannot find package 'dotenv'at collection time.Unit Tests:
[x] I have added or updated unit tests for my change. — N/A, justified above: the change adds no executable code. The falsification experiment above is the substitute evidence.
[x] All unit tests pass locally. — no test outcome changes; see the e2e before/after below.
Manual End-to-End (E2E) Tests:
Please provide instructions on how to manually test your changes, including any necessary setup or configuration.
From the repository root:
npm install --registry=https://registry.npmjs.org— succeeded; left the lock at the single-line diff above (git diff --statre-verified after the full install: still 2 insertions, no churn).npm ls dotenv—dotenv@17.4.2is now a direct dependency of the rootadk@1.5.0, and still exactly one copy at the root:Before the change the root entry was absent;
dotenvappeared only under thedevworkspace and, nested, under@mikro-orm/core.npm run build, thennpx vitest run --project e2e— all 26 e2e files collect, with zero module-resolution errors (grep forCannot find package/ERR_MODULE_NOT_FOUND/Failed to resolve importreturns 0 hits).Outcome is identical before and after the change, which is the point — this is a manifest-hygiene change with no behavioural delta:
git stash)To be explicit about what those failures are, since it matters for reading this PR: they are credential failures on a machine with no
GEMINI_API_KEY/GOOGLE_GENAI_API_KEY/GOOGLE_CLOUD_PROJECT(e.g.Error: API key must be provided via constructor or GOOGLE_GENAI_API_KEY or GEMINI_API_KEY environment variablefromcore/src/models/google_llm.ts:119). They are pre-existing and unrelated — identical on the base commit — and are collection-clean, which is the property this change is about. Suites whose credential gate is evaluated insidedescribe.skipIfreport as skipped; suites that construct a model in the describe body surface the credential error instead. Neither is a resolution error.npm run ts:check— unchanged from the base commit. Its pre-existing errors (incore/test/utils/live_connection_utils_test.ts,tests/integration/agents/agent_with_sandbox_executor_test.ts,tests/integration/sessions/vertex_ai_session_service_test.ts) are untouched by a manifest-only change.Per the repo's guidance I did not run the full
npm test/npm run test:coverage; the targeted commands above are the relevant ones.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. — N/A, JSON manifests only.
[x] I have added tests that prove my fix is effective or that my feature works. — see the falsification experiment; no executable code is added, so no unit test.
[x] New and existing unit tests pass locally with my changes.