feat: manage vended CDK project dependencies with minor-version pinning#1777
feat: manage vended CDK project dependencies with minor-version pinning#1777jesseturner21 wants to merge 13 commits into
Conversation
Package TarballHow to installgh release download pr-1777-tarball --repo aws/agentcore-cli --pattern "*.tgz" --dir /tmp/pr-tarball
npm install -g /tmp/pr-tarball/aws-agentcore-0.24.1.tgz |
|
Claude Security Review: no high-confidence findings. (run) |
Coverage Report
|
483e37e to
d2eca29
Compare
|
Claude Security Review: no high-confidence findings. (run) |
d2eca29 to
360aca0
Compare
|
Claude Security Review: no high-confidence findings. (run) |
|
Claude Security Review: no high-confidence findings. (run) |
|
Claude Security Review: no high-confidence findings. (run) |
|
Claude Security Review: no high-confidence findings. (run) |
|
Claude Security Review: no high-confidence findings. (run) |
|
Claude Security Review: no high-confidence findings. (run) |
Hweinstock
left a comment
There was a problem hiding this comment.
nice work! I like how most of the functionality was isolated to the module. Few questions, but overall makes sense to me. I do think this is worth bug-bashing since it feels pretty high-risk.
|
Claude Security Review: no high-confidence findings. (run) |
|
Claude Security Review: no high-confidence findings. (run) |
Pin managed dependencies in the vended agentcore/cdk/package.json to tilde ranges (exact for @aws/agentcore-cdk), sync them to the CLI's tested versions during deploy preflight, auto-migrate pre-pinning projects, and error with an upgrade hint when the project was updated by a newer CLI. Opt-out via 'agentcore config disableDependencyManagement true'.
…ve sync, preview/teardown safety, surfaced warnings, dev notices, failure telemetry - messages.ts: drop the wrong @aws/agentcore-cli package name; install command is now threaded in from the CLI adapter (getDistroConfig().installCommand) with a standalone default, keeping src/lib free of src/cli imports - index.ts: never delete node_modules or the lockfile — npm reconciles the edited package.json incrementally; install also runs when node_modules is missing so a previously failed install self-heals on retry - skew error now names the skewed deps, uses the distro-aware install command, and points at the disableDependencyManagement opt-out for intentional manual bumps - mode: 'check' computes the plan/warnings/notice (future tense) without writing or installing; deploy --dry-run/--diff (CLI + TUI diffMode) run check-only, and teardown deploys downgrade skew to a warning so it can't block destroying a stack - dep-sync warnings now reach the terminal via postDeployWarnings (CLI) and a rendered warnings block (DeployScreen); dev flows print/render the notice + warnings too (runCliDeploy console output, useDevDeploy reads them from the deploy result instead of multiplexing onNotice, DevScreen renders them in their own block) - depSync hoisted out of the try in handleDeploy and carried on the failure result; command.tsx records dep_sync_* attrs regardless of success via a shared toDepSyncAttrs() also used by useDeployFlow - shared SYNC_CDK_DEPENDENCIES_STEP constant; sync skipped under AGENTCORE_SKIP_INSTALL
…mational warnings don't exit 2 The bundled-tarball override (file:bundled-agentcore-cdk.tgz) always yields a 'left unmanaged' dep-sync warning in e2e/dev builds. Merging depSync.warnings into postDeployWarnings made every such deploy exit 2, failing e2e deploy assertions. Dep-sync warnings are informational: print them from printDeployResult (headless CLI) instead — the TUI and dev paths already render result.dependencySync.warnings separately.
…ilure, persist dev notice, print warnings on failed deploys, never block teardown Round-2 review fixes for managed dependency pinning (#1540): - handleDeploy: every explicit failure return after the sync step now carries dependencySync, so dep_sync_* telemetry and the rewrite notice survive deploys that fail at a later step (not just the catch path). - syncManagedDependencies: on npm install failure, restore the pre-rewrite package.json before throwing DependencySyncError — otherwise a retry sees a matching manifest plus a stale node_modules, skips the install, and deploys against the old installed tree (the exact skew the feature prevents). Tests pin restore + successful retry. - Dev TUI: the dep-sync notice/warnings arrived in the same render batch that transitioned out of 'deploying', so they were visible for at most one frame. They now persist into the post-deploy modes (harness chooser / InvokeScreen) and print to the normal buffer on the browser-launch path. - Headless CLI: the non-JSON failure branch now prints dependencySync.notice/warnings to stderr, so a failed deploy still explains the package.json rewrite and any downgraded skew. - Teardown: DependencySyncError (write/install failure) at both sync call sites is downgraded to a warning via teardownSyncFailureResult and the teardown proceeds — extending the treatSkewAsWarning invariant that nothing about pinning may block destroying a cost-incurring stack.
…ady printed it live
… suite - policy.ts -> plan.ts, index.ts -> sync.ts with thin re-export barrel - DependencySyncResult.migrated -> migratedFromCaret (consistent with SyncPlan) - SKIPPED_SYNC_RESULT -> NO_OP_SYNC_RESULT via noOpSyncResult factory - DeployResult.dependencySync -> dependencySyncResult; depSync local -> shorthand - move toDepSyncAttrs into operations/deploy/dependency-sync.ts - readManifest 'what' param -> fileDescription; install -> installResult - drop #1540 issue references from comments - add e2e-tests/dependency-sync.test.ts (migration+real deploy, upgrade, skew failure, opt-out warning)
- add outcome discriminant to DependencySyncResult (synced/check-only/ opted-out/skipped/failure-suppressed/failed) + dep_sync_outcome telemetry - attach failedSyncResult on sync-step failures so telemetry records them - reset dep-sync state in startPreflight retries - accept v-prefixed semver versions (v1.2.3, ~v1.2.3) - replace NO_OP_SYNC_RESULT singleton with per-call factory - warn (not success) on suppressed teardown sync failure in headless progress - PreflightResult.dependencySync -> dependencySyncResult - export PackageManifest from barrel; drop redundant type re-exports - delete dead formatVersion; fix orphaned doc comment - retitle e2e opt-out test to what it actually proves; drop stale #1540 ref
- TUI logs warn (not success) for suppressed teardown sync failure - dedupe suppressed-failure warning line in deploy log - e2e: add 'failed' outcome to DepSyncJson union + assert it in skew test - add unit tests for CLI-layer sync factories, skip branch, toDepSyncAttrs, and disabled-wins-over-check outcome precedence - fix outcome doc overclaim; rename endStep error param to message
…hanges Addresses review feedback on whether the section is guaranteed to exist. It is (a change is only planned for a section findDeclared located), but ??= keeps the write safe if that invariant ever changes and matches the restored-deps loop below.
Per review: node-semver covers parsing and full-precedence comparison (incl. prerelease ordering), so use it for that and delete the hand-rolled comparator. Keep the thin prefix classifier — node-semver's Range API expands ~/^ to comparators and erases the specifier kind the sync policy needs. semver was already in the transitive tree; the CLI bundle absorbs it.
Per review: threading dependencySyncResult through every failure return
was fragile. fail(error) closes over logger and the current
dependencySyncResult and performs the shared ritual (finalize log, attach
logPath + sync result), collapsing 11 failure sites — forgetting the
dep_sync_* telemetry field is no longer possible. endStep('error') and
per-site logging stay at call sites; success returns stay explicit.
plan.ts imports node-semver's compare directly; parseVersion stays as it carries real policy (strict mode keeps wildcards/ranges unsupported, and normalizes the uppercase-V form npm accepts but node-semver rejects).
npm audit fix — resolves the js-yaml quadratic-CPU advisories (GHSA-h67p-54hq-rp68 / GHSA-52cp-r559-cp3m, 4.1.1 -> 4.3.0) and the @babel/core sourceMappingURL file-read advisory; remaining findings are moderate/low, below the security:audit --audit-level=high gate.
|
Claude Security Review: no high-confidence findings. (run) |
Description
Problem
The CLI vends projects with caret (
^) dependency ranges inagentcore/cdk/package.json, so any upstream minor release (@aws/agentcore-cdk,aws-cdk-lib, …) can land in a customer's project without any CLI change — breaking template code or the CLI's ability to interact with the project. The CLI needs to own these versions on the customer's behalf while staying out of the way of dependencies the customer added themselves.Solution
Pin to minor, let patches float. Managed dependencies are written as tilde ranges (
~x.y.z);@aws/agentcore-cdk(L3 constructs, prerelease-versioned) is pinned exactly. A dependency is "managed" iff its name appears in the CLI's vended assetsrc/assets/cdk/package.json— that asset, baked into each CLI release, is the single source of truth for both the managed list and the known-good versions. Everything else in the customer's file is never touched.On every
agentcore deploy(headless and TUI), a preflight step:npm install -g @aws/agentcore@latestand retry." Higher patch is fine — that's the point of tilde.file:bundled-tarball overrides,git:, …) are skipped with a warning, never rewritten.npm installinagentcore/cdk/that reconciles the edited package.json against the existing lockfile;node_modulesand the lockfile are never deleted, so user-added deps keep their resolved versions and installs stay warm. The install also runs as a self-heal whennode_modulesis missing even with a matching manifest. If the install fails, the pre-rewrite package.json is restored so a retry recomputes the same plan and re-attempts the install. No-op deploys stay fast.old → newper dep, plus "Dependencies you added yourself were not changed.").Preview modes:
--dry-runand--diffrun the sync in check-only mode — the notice reports what a real deploy would change (future tense), but nothing is written and no install runs.Teardown: teardown deploys are never blocked by the sync — both skew and write/install failures downgrade to warnings, so dependency pinning can never prevent destroying a cost-incurring stack.
Opt-out:
agentcore config disableDependencyManagement trueskips migrate/sync/reinstall entirely; the skew check still runs but downgrades to a warning and deploy proceeds — an escape hatch if we ever ship a bad pin, and support for teams with their own dependency tooling.High-level implementation
src/lib/dependency-management/(new, self-contained) — one deep module behind a single function,syncManagedDependencies({vendedPackageJsonPath, projectDir, disabled}), designed to lift into the planned CLI refactor unchanged (zero imports fromsrc/cli/). All notices/warnings are returned as data on the result; callers only display them, so the headless and TUI paths can't drift.src/assets/cdk/package.jsonconverted to the policy form, with tilde bases bumped to what today's caret ranges actually resolve to (e.g.constructs ^10.0.0→~10.7.0, not~10.0.0) so migration never silently downgrades an existing project.ensureManagedDependencies()adapter (src/cli/operations/deploy/dependency-sync.ts) resolves the vended asset viagetTemplatePathand reads the global-config opt-out; called as a "Sync CDK dependencies" step between validate and build inhandleDeploy(actions.ts) and TUI preflight (useCdkPreflight.ts).disableDependencyManagementadded toGlobalConfigSchemaStrict; the genericagentcore config <key> <value>command handles it with no command changes.Related Issue
Closes #1540
Documentation PR
N/A — behavior is self-describing via the deploy-time notices; can follow up in agent-docs if wanted.
Type of Change
Testing
40 new unit tests in
src/lib/dependency-management/__tests__/(16 sync + 12 policy + 12 semver), plus newuseDevDeploycases for the dev-flow notice/warnings:npm run test:unitandnpm run test:integ(unit: 411 files / 5956 tests pass; integ not run — no integ coverage touched)npm run typechecknpm run lintsrc/assets/, I rannpm run test:update-snapshotsand committed the updated snapshotsChecklist
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the
terms of your choice.