fix: improve old CLI conflict detection in preinstall hook#588
fix: improve old CLI conflict detection in preinstall hook#588
Conversation
Replace brittle PATH-based detection with direct package-manager queries (pip list, pipx list, uv tool list) and keep the old PATH check as a fallback. Exit non-zero so npm always surfaces the error. Add AGENTCORE_SKIP_CONFLICT_CHECK env var bypass. Update README to show all three uninstall methods before the install command. Closes #587
src/cli/external-requirements/__tests__/check-old-cli.test.ts
Dismissed
Show dismissed
Hide dismissed
Coverage Report
|
Add platform parameter to probePath() so it uses `where agentcore` on Windows and `command -v agentcore` elsewhere, matching the original behavior. Add tests for both platforms.
Override @aws-sdk/xml-builder to 3.972.14 which sets maxTotalExpansions: Infinity when creating its XMLParser. The previous fast-xml-parser 5.5.7 override (from PR #577) introduced a default limit of 1000 entities, but large CloudFormation responses for container stacks exceed this (1175 entities), causing CDK deploy to fail with "Entity expansion limit exceeded". Also add deploy retry logic for container e2e tests (up to 3 attempts with 30s delay) and include stdout in error assertions since the CLI returns errors as JSON on stdout when using --json.
Let customers know that npm install will fail with a clear error if the old toolkit is detected, and mention the AGENTCORE_SKIP_CONFLICT_CHECK env var for CI environments.
notgitika
left a comment
There was a problem hiding this comment.
scripts/check-old-cli.lib.mjs:20 — Substring match is too broad
output.includes('bedrock-agentcore-starter-toolkit') will match any package whose name contains that string (e.g., a hypothetical bedrock-agentcore-starter-toolkit-extra). pip list output is columnar — matching against a regex like /^bedrock-agentcore-starter-toolkit\s/m would be more precise and avoid false positives.
scripts/check-old-cli.mjs:5 — process.exit(1) in a preinstall hook is a breaking change for existing users
If someone has the old toolkit installed and runs npm install in a project that depends on @aws/agentcore (not globally), the install will hard-fail. The old behavior was a warning. The README and PR description acknowledge this, but the severity should be called out: this can break CI pipelines and automated environments that previously worked. Consider whether a --force or grace period would be
appropriate, or at minimum ensure the error message is highly visible and actionable (it currently goes to console.error, which npm may suppress depending on log level).
scripts/check-old-cli.lib.mjs:47-51 — PATH fallback false positive risk
agentcore --version failing is used as a signal for the old CLI, but any broken install of the new CLI (corrupted node_modules, missing deps) would also fail --version. This could falsely block reinstallation of the new CLI. Consider checking the binary's shebang or location (e.g., is it in a Python site-packages path?) for a stronger signal.
src/cli/external-requirements/tests/check-old-cli.test.ts:234-252 — Subprocess integration test is environment-dependent
The "exits 1 with error when old toolkit is detected" test behaves differently depending on whether the old toolkit is installed on the test machine. The test silently passes in both cases (toolkit present → checks stderr, toolkit absent → does nothing). This means the exit-1 path is never verified in clean CI. Consider mocking PATH or using a fixture to guarantee the detection path is exercised.
- Use regex /^bedrock-agentcore-starter-toolkit\s/m instead of includes() to avoid matching superstring package names - Skip node_modules-installed binaries in PATH fallback to prevent false positives from broken new CLI installs - Replace environment-dependent integration test with deterministic stub that always exercises the exit-1 path
|
Addressed 3 of 4 items from the review, pushing back on one: 1. Substring match too broad -- Fixed. Switched to 2. process.exit(1) is a breaking change -- Intentional and the reason for this PR. The old 3. PATH fallback false positive risk -- Fixed. 4. Environment-dependent integration test -- Fixed. Replaced the environment-dependent test with a deterministic stub script that always injects |
| ].join('\n') | ||
| ); | ||
| try { | ||
| execSync(`node ${wrapperPath}`, { stdio: 'pipe', encoding: 'utf-8' }); |
Check warning
Code scanning / CodeQL
Shell command built from environment values Medium test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 26 minutes ago
In general, the fix is to avoid constructing a shell command string that embeds an environment-derived path and is executed via a shell. Instead, call Node with the script path as a separate argument, using an API that does not invoke a shell or that accepts an argument array so quoting is handled safely.
In this specific test, we can change the execSync call at line 268 from using a single template-string command to using the argument-array form: execSync('node', [wrapperPath], { ... }). In Node, when execSync is called with a non-empty argument array, it runs the given file directly (without going through a shell), and passes each array entry as a separate argument, preventing shell interpretation of wrapperPath. No behavior change is intended: we still run node with the same script, with the same options, and still capture stdout/stderr for assertions. No new imports are required and all changes are confined to src/cli/external-requirements/__tests__/check-old-cli.test.ts.
Concretely:
- Locate the
execSynccall inside the"exits 1 with actionable error when old toolkit is detected"test. - Replace
execSync(\node ${wrapperPath}`, { stdio: 'pipe', encoding: 'utf-8' });withexecSync('node', [wrapperPath], { stdio: 'pipe', encoding: 'utf-8' });`. - Leave the surrounding try/catch/finally and assertions unchanged.
| @@ -265,7 +265,7 @@ | ||
| ].join('\n') | ||
| ); | ||
| try { | ||
| execSync(`node ${wrapperPath}`, { stdio: 'pipe', encoding: 'utf-8' }); | ||
| execSync('node', [wrapperPath], { stdio: 'pipe', encoding: 'utf-8' }); | ||
| expect.unreachable('Should have exited with code 1'); | ||
| } catch (err: any) { | ||
| expect(err.status).toBe(1); |
Summary
Closes #587
command -v agentcore+--versionheuristic) with direct package-manager queries (pip list,pipx list,uv tool list) that check for thebedrock-agentcore-starter-toolkitPython package specificallycommand -v agentcore/agentcore --version) as a fallback when no package manager reports the toolkitprocess.exit(1)) so npm 8+ always surfaces the error instead of swallowing it on exit 0AGENTCORE_SKIP_CONFLICT_CHECK=1env var bypass for CI and edge casesTest plan
npm testpasses (20 unit tests coveringprobeInstaller,probePath,detectOldToolkit,formatErrorMessage, and subprocess integration)npm packproduces a tarball that includes bothscripts/check-old-cli.lib.mjsandscripts/check-old-cli.mjsnpm install <tarball>on a machine with the old toolkit installed exits 1 with a visible error showingpip uninstall bedrock-agentcore-starter-toolkitAGENTCORE_SKIP_CONFLICT_CHECK=1 npm install <tarball>installs successfully, bypassing the check