Skip to content

Deploy the API to ECS from CI, with OIDC and no stored AWS keys - #52

Merged
KacemMathlouthi merged 1 commit into
mainfrom
fix/pre-launch-hardening-6
Sep 2, 2026
Merged

KacemMathlouthi merged 1 commit into
mainfrom
fix/pre-launch-hardening-6

Conversation

@KacemMathlouthi

@KacemMathlouthi KacemMathlouthi commented Aug 31, 2026 •

Copy link
Copy Markdown
Owner

Replaces manual docker build + aws ecs update-service with a gated pipeline, and closes the "broken code can deploy" blocker.

What happens on a merge to main

  1. CI runs the five checks as today
  2. Only on green, Deploy API builds the image, pushes to ECR tagged by commit, registers a task definition revision, rolls the ECS service, and waits for it to stabilise
  3. It then polls https://api.tryanimus.app/health until it answers 200, and fails the job if it never does

The Vercel-like part

The deploy job declares environment: Production, so runs appear in the repo's Deployments tab with a clickable link to the API, the same way Vercel's did. The environment is restricted to protected branches, so only main can deploy.

Security

  • No AWS keys anywhere. GitHub OIDC only. The IAM role trusts exactly one subject: repo:KacemMathlouthi/animus:environment:Production.
  • Least privilege. ECR push scoped to the one repository, UpdateService scoped to the one service, PassRole scoped to the task execution role with an ecs-tasks.amazonaws.com condition.
  • Every action pinned to a SHA, in this workflow and in CI. vitest-coverage-report-action was a mutable v2 tag in a job holding pull-requests: write.
  • No task definition in the repo. It is derived from the live revision at deploy time, so the public repo never carries the account id, the ECR URI or the role ARN.
  • permissions: {} at workflow level, widened per job.

Details worth knowing

  • Images are tagged by commit, never latest, so an ECS restart cannot pull different code than the deploy that was verified.
  • workflow_run fires on CI failure too, so the job explicitly checks conclusion == 'success'.
  • workflow_run checks out the default branch by default, which would silently deploy the wrong tree. The checkout pins head_sha.
  • Concurrency is serialised without cancel-in-progress: a half-finished rollout should settle, not be abandoned.
  • workflow_dispatch allows a redeploy of current main without a code change.

Tradeoff

Every merge to main redeploys the API, including web-only changes. workflow_run cannot use path filters. Deploys are idempotent and take about six minutes, so this is left simple for now.

Note

Merging this deploys the current main, which includes the better-auth pin from #51 — that is the fix for OAuth being broken in production.

@vercel

vercel Bot commented Aug 31, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
animus-web Ready Ready Preview Sep 2, 2026 7:32pm UTC

@github-actions

github-actions Bot commented Aug 31, 2026 •

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 56.88% 5653 / 9938
🔵 Statements 56.88% 5653 / 9938
🔵 Functions 73.18% 262 / 358
🔵 Branches 86.98% 976 / 1122
File CoverageNo changed files found.
Generated in workflow #130 for commit e343df4 by the Vitest Coverage Report Action

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/deploy-api.yml">

<violation number="1" location=".github/workflows/deploy-api.yml:134">
P2: The health gate runs after the deployment is already promoted. `update-service` makes the new task definition the active deployment, `services-stable` waits for it, and only then does the `/health` probe run — so when it fails, the broken revision is already serving production traffic and the job just reports failure. There is no automatic rollback to the previous revision. This only protects the deploy when the new tasks fail the ALB health check (ECS reverts those internally); it does not protect when `/health` is stricter than the ALB check — e.g. DB down — because ECS sees the tasks as healthy. Since the PR's stated goal is that broken code cannot reach prod, add a rollback step (re-register the previous task definition and `update-service` back to it) on health-check failure, or gate `update-service` itself behind a pre-deploy readiness signal.</violation>

<violation number="2" location=".github/workflows/deploy-api.yml:135">
P1: When `/health` never returns 200, `exit 1` only marks the job failed while ECS continues serving the new task definition. Capture the previous task-definition ARN and roll back on validation failure, or configure ECS deployment rollback.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread .github/workflows/deploy-api.yml Outdated
sleep 10
done
echo "health never returned 200" >&2
exit 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: When /health never returns 200, exit 1 only marks the job failed while ECS continues serving the new task definition. Capture the previous task-definition ARN and roll back on validation failure, or configure ECS deployment rollback.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/deploy-api.yml, line 135:

<comment>When `/health` never returns 200, `exit 1` only marks the job failed while ECS continues serving the new task definition. Capture the previous task-definition ARN and roll back on validation failure, or configure ECS deployment rollback.</comment>

<file context>
@@ -0,0 +1,135 @@
+            sleep 10
+          done
+          echo "health never returned 200" >&2
+          exit 1
</file context>

Comment thread .github/workflows/deploy-api.yml Resolved
Comment thread .github/workflows/deploy-api.yml Resolved
Comment thread .github/workflows/deploy-api.yml Outdated
echo "health $code, retrying"
sleep 10
done
echo "health never returned 200" >&2

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The health gate runs after the deployment is already promoted. update-service makes the new task definition the active deployment, services-stable waits for it, and only then does the /health probe run — so when it fails, the broken revision is already serving production traffic and the job just reports failure. There is no automatic rollback to the previous revision. This only protects the deploy when the new tasks fail the ALB health check (ECS reverts those internally); it does not protect when /health is stricter than the ALB check — e.g. DB down — because ECS sees the tasks as healthy. Since the PR's stated goal is that broken code cannot reach prod, add a rollback step (re-register the previous task definition and update-service back to it) on health-check failure, or gate update-service itself behind a pre-deploy readiness signal.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/deploy-api.yml, line 134:

<comment>The health gate runs after the deployment is already promoted. `update-service` makes the new task definition the active deployment, `services-stable` waits for it, and only then does the `/health` probe run — so when it fails, the broken revision is already serving production traffic and the job just reports failure. There is no automatic rollback to the previous revision. This only protects the deploy when the new tasks fail the ALB health check (ECS reverts those internally); it does not protect when `/health` is stricter than the ALB check — e.g. DB down — because ECS sees the tasks as healthy. Since the PR's stated goal is that broken code cannot reach prod, add a rollback step (re-register the previous task definition and `update-service` back to it) on health-check failure, or gate `update-service` itself behind a pre-deploy readiness signal.</comment>

<file context>
@@ -0,0 +1,135 @@
+            echo "health $code, retrying"
+            sleep 10
+          done
+          echo "health never returned 200" >&2
+          exit 1
</file context>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/ci.yml">

<violation number="1">
P1: The workflow now executes mutable action tags instead of immutable SHAs, allowing an upstream retag or compromise to change CI code without a repository change. Restore SHA pins for every changed action, including the coverage action with `pull-requests: write`.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

@KacemMathlouthi
KacemMathlouthi force-pushed the fix/pre-launch-hardening-6 branch from 584d937 to e343df4 Compare September 2, 2026 19:32
@KacemMathlouthi
KacemMathlouthi merged commit 112f4ea into main Sep 2, 2026
9 checks passed

This branch was successfully deployed

1 active deployment
Preview — e343df4c Deployed Sep 2, 2026 by vercel[bot]
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