Skip to content

feat(aws/ecs): environmentFiles on the shared task-definition surface - #904

Open
sam-goodwin wants to merge 8 commits into
mainfrom
claude/ecs-service-parity
Open

feat(aws/ecs): environmentFiles on the shared task-definition surface#904
sam-goodwin wants to merge 8 commits into
mainfrom
claude/ecs-service-parity

Conversation

@sam-goodwin

Copy link
Copy Markdown
Contributor

Based on claude/ecs-taskdef-hygiene-1 (#902); merge that first.

SST-parity leftover: environmentFiles on the primary container, on the shared TaskDefinitionConfig surface so it works for both AWS.ECS.Task and the image-owning AWS.ECS.Service.

const task = yield* Task("ApiTask", {
  main: import.meta.url,
  environmentFiles: [
    { value: "arn:aws:s3:::my-config-bucket/app.env", type: "s3" },
  ],
});
  • The execution role is automatically granted s3:GetObject on the referenced objects and s3:GetBucketLocation on their buckets (alchemy-environment-files inline policy, removed when the prop is cleared and swept on delete).
  • Live-tested on both paths: a Task deploy asserts the registered revision carries the file and the role policy grants read; the image-owning Service platform test asserts the same through the Service synthesis path.

The containers?: ecs.ContainerDefinition[] full-shape escape hatch from the audit is intentionally NOT added: the synthesized primary container owns the built/mirrored image, and container (full ContainerDefinition overrides for the primary) + sidecars (full ContainerDefinition entries) already span the multi-container surface — a verbatim containers list would conflict with both. dev mode is out of scope per the audit follow-up.

Known pre-existing CI failures unrelated to this change: the check job's mysql2/promise tsc errors from Planetscale MySQLMigrations, and the docs build's /kubernetes broken link (until #892).

🤖 Generated with Claude Code

sam-goodwin and others added 2 commits July 21, 2026 01:42
… + both roles' inline policies on delete

Every reconcile of AWS.ECS.Task and the image-owning AWS.ECS.Service
registers a NEW task-definition revision, but delete only deregistered the
single revision recorded in output.taskDefinitionArn — superseded revisions
accumulated ACTIVE forever. This is exactly how the "legacy inline ingress
is reaped" migration test leaked one task-definition family per run
(deploy → rev 1, redeploy → rev 2, destroy removed rev 2 only).

- reconcile: after registering a new revision, deregister + hard-delete the
  superseded one (same-family guarded so a BYO `task:` reference from a
  prior Service config is never touched); wired in Task and both Service
  reconcile paths
- delete: deleteTaskDefinitionInfrastructure now sweeps EVERY remaining
  ACTIVE revision of the family, not just the recorded one
- delete: inline role policies are deleted for BOTH the task role and the
  execution role — the Service secrets-read policy lives on the execution
  role, so destroying a secrets-using Service failed with
  DeleteConflictException and stranded the role
- live tests: Task deploy → redeploy(env change) → superseded revision
  reaped, destroy → zero ACTIVE revisions; migration test now asserts the
  reap and an empty family after destroy

The 4 leaked families were deregistered + deleted from the testing account.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SST-parity leftover: `environmentFiles` on the primary container for both
AWS.ECS.Task and the image-owning AWS.ECS.Service (shared
TaskDefinitionConfig surface). The execution role is automatically granted
s3:GetObject on the referenced objects and s3:GetBucketLocation on their
buckets via the `alchemy-environment-files` inline policy (removed when the
prop is cleared; swept on delete by the both-roles inline-policy sweep).

Live-tested: Task deploy asserts the registered revision carries the file
and the execution role policy grants read; the image-owning Service
platform test asserts the same through the Service synthesis path.

The `containers` full-shape escape hatch is intentionally NOT added: the
synthesized primary container owns the built/mirrored image, and
`container` (full ContainerDefinition overrides) + `sidecars` (full
ContainerDefinition entries) already span the multi-container surface — a
verbatim `containers` list would conflict with both.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@alchemy-version-bot

alchemy-version-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Install the packages built from this commit:

alchemy

bun add alchemy@https://pkg.ing/alchemy/f02b8ee

@alchemy.run/better-auth

bun add @alchemy.run/better-auth@https://pkg.ing/@alchemy.run/better-auth/f02b8ee

@alchemy.run/pr-package

bun add @alchemy.run/pr-package@https://pkg.ing/@alchemy.run/pr-package/f02b8ee

sam-goodwin and others added 5 commits July 21, 2026 15:19
Review feedback: single-shot list calls miss pages beyond the first.
listTaskDefinitions/listRolePolicies/listAttachedRolePolicies now go
through .items(...) streams (Stream.filter/mapEffect/runDrain), which
auto-paginate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review feedback: a nonexistent family is an empty list, not an error —
the catch defended nothing and turned real client errors into a silent
empty sweep, leaking revisions. List failures now propagate so the
engine retains state and the delete retries. The deregister/delete
catches stay: ClientException is how ECS reports already-inactive /
not-found there, which is genuine delete idempotency.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Base automatically changed from claude/ecs-taskdef-hygiene-1 to main July 21, 2026 22:29
…rity

# Conflicts:
#	packages/alchemy/test/AWS/ECS/Task.test.ts
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.

1 participant