Skip to content

feat: implement project deploy command - #2001

Open
notgitika wants to merge 3 commits into
refactorfrom
feat/project-deploy
Open

feat: implement project deploy command#2001
notgitika wants to merge 3 commits into
refactorfrom
feat/project-deploy

Conversation

@notgitika

@notgitika notgitika commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

This PR adds the project deploy command. it resolves the deployment target, synthesizes, bootstraps that target's environment, then deploys its stack. Synth reuses build's code path, so build's dependency check runs before anything touches AWS.

stack environments come from agentcore/aws-targets.json, which create() scaffolds empty. An empty list is an error naming the file to fill in, rather than an account resolved from the active credentials, deploy shouldn't guess where the user's infra belongs. It's checked before synth, so an empty list fails without running anything.

one deploy ships one target. --target selects it and defaults to default, so a project with a staging and a prod target can't reach prod by accident. The target is resolved before synth, so a misspelled --target costs no build and the error lists the configured names. which stack belongs to the target comes from the synthesized manifest, matched on the agentcore:target-name tag the generated CDK app already writes, so the CLI never has to reproduce that app's stack-naming convention.

bootstrap is idempotent, so it runs every deploy instead of probing CloudFormation first; --skip-bootstrap opts out. It passes createCustomerMasterKey: true to match the original CLI, so a default deploy provisions a KMS key for the staging bucket. Worth knowing when reviewing: that means a deploy can alter an existing CDKToolkit that was bootstrapped without a CMK.

Not in this PR:

This is the synth -> bootstrap -> deploy spine. Everything else the existing deploy does is deliberately left out for now:

  • No credential preflight. Nothing checks that credentials exist or that their account matches the target's, so a wrong-account credential surfaces as a CDK error partway through rather than an upfront message naming both accounts. Follow-up.
  • An empty aws-targets.json is an error, not auto-populated. Filling it in from the active credentials is a follow-up.
  • Nothing is written to deployed-state.json. The existing CLI provisions identity, OAuth, and payment credential providers before synth and records their ARNs there, and the generated CDK app reads them back to build its stacks. Because this PR writes none of it, a project declaring payments fails during synth asking for credential providers that nothing creates; projects needing no pre-deploy identity are unaffected. Follow-up.
  • No post-deploy steps. No knowledge-base ingestion, dataset upload, observability setup, or online-eval config, and no deployment state recorded for later commands to read. Stack outputs still reach the user — the toolkit prints them itself. Follow-up.
  • No --diff. Follow-up. No --dry-run either, by design: build already synthesizes without touching AWS, which is what a dry run is here.

Deploys a project by synthesizing it, bootstrapping each target environment,
then deploying every stack. Synthesis reuses build's code path so what deploys
is what was just synthesized, and build's dependency check runs before anything
touches AWS.

Stack environments come from agentcore/aws-targets.json, which create()
scaffolds empty. An empty list is an error naming the file to fill in rather
than an account resolved from the active credentials, which would let deploy
guess where the user's infrastructure belongs.

Bootstrap is idempotent and no-ops quickly on a current environment, so it runs
every deploy instead of probing CloudFormation first; --skip-bootstrap opts out.
Targets sharing an environment are bootstrapped once.

Bootstrap and deploy drive @aws-cdk/toolkit-lib in-process rather than shelling
out to npx cdk, so progress arrives as structured messages and failures as
typed errors instead of scraped stdout. src/io/cdk.ts adapts the toolkit's
push-based IIoHost to the generator the manager pulls from, and is injectable so
tests exercise deploy without reaching AWS. Deploy reads the cdk.out assembly
synthesis just wrote instead of re-synthesizing, which both avoids a second
synth and makes "deploy exactly what was synthesized" structural.

A deploy runs for minutes, so those messages stream to stderr as they arrive:
ProjectEvent gains an output variant carrying the toolkit's own wording, with
debug and trace levels left in the debug log rather than on screen.
deploy shipped every stack in the assembly, so a project with a staging
and a prod target reached both at once. It now takes --target, defaulting
to "default", and bootstraps and deploys only that target.

The target is resolved from aws-targets.json before synthesizing, so a
misspelled --target costs no build and the error lists the configured
names.

Which stack belongs to the target comes from the synthesized manifest,
matched on the agentcore:target-name tag the generated CDK app writes,
rather than from the CLI reproducing that app's naming convention. The
lookup runs before bootstrap so a mismatch fails in seconds, and the
toolkit selects with PATTERN_MUST_MATCH so a name the assembly does not
contain fails loudly instead of deploying nothing.
@github-actions github-actions Bot added the agentcore-harness-reviewing AgentCore Harness review in progress label Aug 14, 2026
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.36842% with 63 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.69%. Comparing base (4d183dc) to head (30a3a12).
⚠️ Report is 1 commits behind head on refactor.

Files with missing lines Patch % Lines
src/io/cdk.ts 3.12% 62 Missing ⚠️
src/core/project/assembly.ts 97.36% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           refactor    #2001      +/-   ##
============================================
- Coverage     96.96%   96.69%   -0.27%     
============================================
  Files           364      367       +3     
  Lines         20758    20977     +219     
============================================
+ Hits          20127    20283     +156     
- Misses          631      694      +63     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions Bot removed the agentcore-harness-reviewing AgentCore Harness review in progress label Aug 14, 2026
Comment thread src/io/cdk.ts Outdated
Comment on lines +4 to +13
import {
BaseCredentials,
BootstrapEnvironments,
BootstrapStackParameters,
StackSelectionStrategy,
Toolkit,
type IIoHost,
type IoMessageLevel,
} from "@aws-cdk/toolkit-lib";

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this static import is increasing the binary size and startup latency by a lot. looking into solutions

@notgitika

Copy link
Copy Markdown
Contributor Author

increasing test coverage

The toolkit is the heaviest dependency in the CLI and src/io/index.ts
re-exports runCdk, so a static import made every command load it:
agentcore --help ran in 2.7s from source, 0.7s once the import moved
inside the function that builds the toolkit. The compiled binary is
unchanged either way, since --compile embeds the module regardless.

src/io/cdk.ts splits into the three things a run does -- load the
toolkit, perform one operation with it, bridge its reporting to a
generator -- so each is reachable from a test. src/io/cdk.test.ts covers
them against the real toolkit package: constructing a Toolkit and its
BootstrapEnvironments, BootstrapStackParameters, and
StackSelectionStrategy helpers resolves no credentials and calls no API,
so the arguments a deploy passes are asserted against the values the
toolkit itself defines rather than stand-ins. What the tests assert
includes the ones a caller cannot see and a fake cannot check: that
messages are yielded while the operation is still running, that a
failure surfaces only after the output explaining it, that a request is
answered with its suggested default rather than prompting, and that
createCustomerMasterKey and PATTERN_MUST_MATCH reach the toolkit.

The fake in TestCoreClient still buffers rather than streams; it now
says so, and names the test that covers the real behaviour.

ProjectEvent becomes a discriminated union. It documented that exactly
one of step and output is set while typing both optional, which allowed
{} and both-at-once and spread `if (event.output)` checks through three
handlers. Both variants carry `message`, so a consumer that only writes
text needs no switch, and those checks are gone.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants