Skip to content

feat(templates): support Container builds in the strands-http-python template - #2137

Closed
Hweinstock wants to merge 3 commits into
aws:refactorfrom
Hweinstock:feat/strands-container-support
Closed

feat(templates): support Container builds in the strands-http-python template#2137
Hweinstock wants to merge 3 commits into
aws:refactorfrom
Hweinstock:feat/strands-container-support

Conversation

@Hweinstock

Copy link
Copy Markdown
Contributor

Problem

On the refactor branch, the strands-http-python template does not support Container builds. agentcore project create --template strands-python --build Container --name p errored with "--template and --build are mutually exclusive", and even with that guard relaxed the template shipped no Dockerfile.

Solution

  1. Guard relaxation + shortcuts extraction (adapted from fix(scaffold): allow certain template values to be overriden #2130): moved RUNTIME_TEMPLATE_SHORTCUTS into a new src/handlers/project/shortcuts.ts and added resolveRuntimeTemplateShortcut, which lets compatible flags (--build, --model-provider, --memory, --api-key, --runtime-name) override a --template. Only --language/--framework remain locked. Applied to both project create and project add runtime. (I deliberately did not port fix(scaffold): allow certain template values to be overriden #2130's strands "CodeZip-only" guard, since the goal here is the opposite.)
  2. Descendant filtering in FsTreeNode.fromAssetSource (adapted from feat(templates): wire in memory to the runtime templates #2116): the resolver now accepts an object-shaped signature with a filter(name, isDir) option (and transformContent). The filter receives the rendered name (post-renderName).
  3. Conditional Dockerfile rendering: the strands resolver passes filter: (name) => isContainer || (name !== "Dockerfile" && name !== ".dockerignore"), so the Dockerfile/.dockerignore are scaffolded only for Container builds. buildRuntimeSpec already stamps dockerfile: "Dockerfile" for Container builds.
  4. Template assets: added Dockerfile.template + dockerignore.template to strands-http-python (byte-identical to the working hello-world-python-container versions).

Why Dockerfile.template and not Dockerfile

bun build names embedded assets [name].[ext], so an extensionless Dockerfile embeds into the compiled binary as Dockerfile. (trailing dot). A project scaffolded from the binary would then get a Dockerfile. that project deploy/project dev can't find. This is a pre-existing latent bug — the existing hello-world-python-container/Dockerfile had the same problem in compiled binaries.

Fix: reuse the existing dotfile-template convention (gitignore.template.gitignore). renderName now maps Dockerfile.templateDockerfile. Both Dockerfiles (strands + hello-world) use the suffix, which also fixes the hello-world binary bug. Scaffold output is byte-identical, so no snapshot churn.

The renderName change is intentionally targeted rather than a generic .template strip: a generic strip would break env.local.template.env.local (a dotfile mapping).

Definition of Done — evidence

All commands below were run against a binary built with bun run compile:linux-x64.

1. Container agent from the strands template ✅

$ agentcore project create --template strands-python --build Container --name p --skip-install --skip-git
Created project 'p' in ./p
$ cat p/agentcore/agentcore.json | jq '.runtimes[0].build'
"Container"
$ cat p/agentcore/agentcore.json | jq '.runtimes[0].dockerfile'
"Dockerfile"
$ ls -A p/app/strands_agent | grep -i docker
.dockerignore
Dockerfile

Note: the value is "Container" (capitalized) — that's the canonical BuildTypeSchema value; the DoD's lowercase "container" was casing-loose.

3. Default CodeZip still works, no Dockerfile ✅

$ agentcore project create --template strands-python --name pzip --skip-install --skip-git
$ cat pzip/agentcore/agentcore.json | jq '.runtimes[0].build'
"CodeZip"
$ ls -A pzip/app/strands_agent | grep -i docker || echo "(no docker files)"
(no docker files)

2. Local dev / invoke returns real data ✅

agentcore project dev is an interactive TUI (needs a live terminal), so I validated the runtime the same way its container path does under the hood — build + run the scaffolded image, then curl POST /invocations (the AgentCore HTTP contract):

$ (cd p/app/strands_agent && docker build -f Dockerfile -t strands-ctr-test .)   # succeeds
$ docker run -d --name strands-ctr -p 8080:8080 -e AWS_REGION=<REGION> \
    -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_SESSION_TOKEN strands-ctr-test
$ curl -s localhost:8080/ping            # -> 200
$ curl -s -X POST localhost:8080/invocations -H 'Content-Type: application/json' \
    -d '{"prompt":"Reply with exactly one short sentence and nothing else: what is 2+2?"}'
... data: {"event":{"contentBlockDelta":{"delta":{"text":"2+2 equals 4."}...

4. Deployable + invokable ✅ (CodeZip) / ⚠️ (Container — account infra blocker)

CodeZip — deployed and invoked end-to-end:

$ agentcore project deploy --target default
Deployed project 'zip' to target 'default'
...RuntimeArnOutput...: arn:aws:bedrock-agentcore:<REGION>:<ACCOUNT>:runtime/zip_strands_agent-<ID>

$ aws bedrock-agentcore invoke-agent-runtime --cli-binary-format raw-in-base64-out \
    --agent-runtime-arn <ARN> --runtime-session-id <33+char-session> \
    --payload '{"prompt":"Reply with one short sentence: what is 7 times 6?"}' \
    --content-type application/json --accept application/json out.txt
{ "statusCode": 200, ... }
$ grep text out.txt   # -> "7 times 6 equals 42."

Container — the image builds and runs (see #2 above), but project deploy fails at the CloudFormation stage with:

ApplicationAgent...EcrRepository  AWS::ECR::Repository  CREATE_FAILED
  KMS exception: ...cdk-hnb659fds-cfn-exec-role... is not authorized to perform:
  kms:CreateGrant on ...key/... (the construct-created "ECR encryption key")

This is a template-independent account/bootstrap issue: the AgentCore CDK construct creates a customer-managed KMS key for the ECR repo, but the scoped CDK exec role (AgentCoreCdkBootstrapExecution) lacks kms:CreateGrant. It would block any container deploy in this account (including hello-world-python-container) and is unrelated to this change. Resolving it needs an account-level IAM/bootstrap fix (grant kms:CreateGrant to the exec role, or bootstrap with a broader exec policy) — out of scope here. Filing/looping in for a follow-up on the bootstrap policy is recommended.

Reproduce

git fetch && git checkout feat/strands-container-support
bun install
bun run typecheck && bun test          # 2170 pass, 0 fail
bun run compile:linux-x64              # or your platform target
BIN=dist/bin/agentcore-linux-x64
"$BIN" project create --template strands-python --build Container --name p --skip-install --skip-git
cat p/agentcore/agentcore.json | jq '.runtimes[0].build'   # "Container"
ls -A p/app/strands_agent | grep -i docker                 # Dockerfile + .dockerignore
"$BIN" project create --template strands-python --name pzip --skip-install --skip-git
ls -A pzip/app/strands_agent | grep -i docker || echo "(none, as expected)"

Notes for reviewers

@github-actions github-actions Bot added the size/l PR size: L label Aug 27, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added agentcore-harness-reviewing AgentCore Harness review in progress claude-security-reviewing Claude Code /security-review in progress labels Aug 27, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 27, 2026
@Hweinstock Hweinstock closed this Aug 27, 2026
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.20%. Comparing base (fadad5a) to head (7271ebc).

Additional details and impacted files
@@            Coverage Diff            @@
##           refactor    #2137   +/-   ##
=========================================
  Coverage     97.19%   97.20%           
=========================================
  Files           471      472    +1     
  Lines         28731    28790   +59     
=========================================
+ Hits          27925    27984   +59     
  Misses          806      806           

☔ 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

Copy link
Copy Markdown
Contributor

Thanks for the report, @codecov-commenter — feedback like this is exactly
how we catch the things we missed. Because this PR is already
closed, the team won't see follow-up comments here.

Would you mind opening a new issue so we can track it properly?
https://github.com/aws/agentcore-cli/issues/new/choose

If this is a security issue, please report it privately via
https://aws.amazon.com/security/vulnerability-reporting/ instead
of a public issue.

@agentcore-devx-automation agentcore-devx-automation Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

AgentCore Harness Review

Verdict: Looks good

Nice refactor. The filter/transformContent split in FsTreeNode.fromAssetSource, the .template suffix convention (with clear rationale in the docstring for the extensionless-file bundling quirk), and the extraction of template shortcut resolution into resolveRuntimeTemplateShortcut all read well. Delegating validation to ScaffoldRuntimeInputSchema.superRefine/refine (e.g., API key + Bedrock, runtimeVersion + build) is the right move — errors like "API keys are not compatible with Bedrock model providers" fall out naturally instead of being open-coded in the handler.

The new e2e-ish tests in project.test.ts and add/runtime/index.test.ts exercise real project scaffolding against temp directories and assert file existence for Dockerfile/.dockerignore under the correct build modes, which is the right level of coverage for this feature.

A few small observations, non-blocking:

  • In both handlers/project/create/index.ts and handlers/project/add/runtime/index.ts, presentScaffoldingFlags/isCustom are still computed even when isTemplate is true. Since the ternary checks isTemplate first, the isCustom branch is unreachable in that case, so this is inert but slightly confusing. Optional cleanup.
  • fsTree.ts filter semantics: rejecting a name via continue assetPaths correctly prevents new subtrees from being added, but if an earlier sibling path already created a directory node that the filter would now reject, the directory stays. In practice your filters are deterministic on names, so this doesn't bite — worth being aware of if filters ever become path-dependent.
  • No new telemetry for --template selection or overrides. There's no existing instrumentation in these handlers either, so this is consistent with the surrounding code; flagging only in case template-adoption metrics are wanted.

@agentcore-devx-automation agentcore-devx-automation Bot removed the agentcore-harness-reviewing AgentCore Harness review in progress label Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/l PR size: L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants