Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 30 additions & 22 deletions .github/workflows/integ.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ on:
types: [completed]
workflow_dispatch: {}

# Only one integ run at a time against the shared account — overlapping deploys
# would collide on the single hardcoded `backgroundagent-integ` stack name.
# Only one integ run at a time against the shared account. Stack names are now
# run-unique (`int-<sha>`, see the integ job) so this is no longer a correctness
# requirement — it bounds shared-account cost/parallelism rather than preventing
# a name collision.
concurrency:
group: cdk-integ
cancel-in-progress: false
Expand Down Expand Up @@ -217,30 +219,36 @@ jobs:
- name: Install dependencies
run: yarn install --immutable

- name: Run integ tests (deploy → assert → destroy)
run: mise //cdk:integ

# Safety net: integ-runner forces teardown on success and failure, but if
# the run is cancelled or crashes mid-deploy the stack can be stranded in
# the shared account. Delete it directly via CloudFormation so we never
# leak billable resources.
# Teardown: integ-runner forces `cdk destroy` on success and failure (via
# the CDK bootstrap roles, which hold the CloudFormation permissions). No
# raw-CLI safety net here: the GitHub OIDC role has no
# cloudformation:DeleteStack/DescribeStacks, so such a step can only fail
# (#566). If a cancelled/crashed run strands the stack, reclaim is the
# stranded-stack cleanup owned by #400/#72 — and the run-unique name below
# means a stranded stack never blocks a later run.
#
# NOTE: `cdk destroy backgroundagent-integ` would NOT work here — it
# synthesizes the main app (src/main.ts), which does not contain the integ
# stack, so it exits 0 having deleted nothing. Target the stack by its
# literal CloudFormation name instead. delete-stack is idempotent (no-op if
# already gone), so `|| true` only guards transient API errors.
- name: Ensure stack torn down
if: always()
# INTEG_STACK_NAME maps the stack to the commit under test plus the run
# number (`int-<sha>-<run>`), replacing the single fixed
# `backgroundagent-integ` name. The run number makes same-commit re-runs
# distinct; the `int-` prefix marks it ephemeral for #72's sweep. HEAD_SHA
# is the resolved PR head (or dispatched ref) and RUN_NUMBER is a positive
# integer, so both are already lowercase-alphanumeric — no sanitizing
# needed to stay CloudFormation-valid.
- name: Run integ tests (deploy → assert → destroy)
env:
AWS_REGION: ${{ vars.AWS_REGION || 'us-east-1' }}
AWS_DEFAULT_REGION: ${{ vars.AWS_REGION || 'us-east-1' }}
HEAD_SHA: ${{ needs.resolve.outputs.head_sha }}
RUN_NUMBER: ${{ github.run_number }}
run: |
set -euo pipefail
aws cloudformation delete-stack --stack-name backgroundagent-integ || true
# No `|| true` on the wait: a DELETE_FAILED must surface loudly so we
# never silently leak billable resources in the shared account.
aws cloudformation wait stack-delete-complete --stack-name backgroundagent-integ
export INTEG_STACK_NAME="int-${HEAD_SHA:0:12}-${RUN_NUMBER}"
echo "Integ stack name: $INTEG_STACK_NAME"
mise //cdk:integ -- --all --require-approval never \
-c stackName="integ-${{ github.run_id }}-${{ github.run_attempt }}" \
-c github:sha="$(git rev-parse HEAD)" \
-c github:ref="$(git branch --show-current || echo "detached")" \
-c github:actor="${{ github.triggering_actor }}" \
-c github:repository="$(gh repo view --json nameWithOwner --jq .nameWithOwner)" \
-c github:clean="$([ -z "$(git status --porcelain)" ] && echo true || echo false)"

# Post the final integ-smoke status back to the PR head so the check flips from
# pending to success/failure. Skipped for workflow_dispatch (no PR to gate).
Expand Down
9 changes: 8 additions & 1 deletion cdk/test/integ/integ.task-api-smoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,15 @@ class TaskApiSmokeStack extends Stack {
}
}

// Stack name is run-unique in CI (`int-<short-sha>-<run-number>`, set by
// integ.yml) so a stranded stack from a cancelled/crashed run never collides
// with — or blocks — a later run under one fixed name. Falls back to a stable
// local name so the `mise //cdk:integ` dev path is unchanged. The `int-` prefix
// keeps it letter-led and the rest is lowercase-alphanumeric + hyphens, so the
// name is CloudFormation-valid.
const app = new App();
const stack = new TaskApiSmokeStack(app, 'backgroundagent-integ');
const stackName = process.env.INTEG_STACK_NAME || 'backgroundagent-integ-local';
const stack = new TaskApiSmokeStack(app, stackName);

const integ = new IntegTest(app, 'TaskApiSmoke', {
testCases: [stack],
Expand Down
Loading