Skip to content
Draft
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
22 changes: 3 additions & 19 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ jobs:
- name: Terraform Validate
run: terraform validate

- name: Ensure migration image exists for Terraform
- name: Preflight bootstrap images for Terraform
if: github.ref == 'refs/heads/main'
working-directory: .
run: |
Expand All @@ -374,27 +374,11 @@ jobs:
registry_endpoint=$(terraform -chdir=infra output -raw AZURE_CONTAINER_REGISTRY_ENDPOINT 2>/dev/null || true)

if [[ -z "$registry_name" || -z "$registry_endpoint" ]]; then
echo "No existing container registry output found; skipping migration image bootstrap."
echo "No existing container registry output found; skipping bootstrap image preflight."
exit 0
fi

if az acr repository show-tags \
--name "$registry_name" \
--repository migrations \
--query "contains(@, 'latest')" \
--output tsv 2>/dev/null | grep -q true; then
echo "Migration image already exists: $registry_endpoint/migrations:latest"
exit 0
fi

echo "Bootstrapping migration image for Terraform validation."
az acr login --name "$registry_name"
docker build -f api/Dockerfile \
--target migrations-runtime \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--label git-commit=${{ github.sha }} \
-t "$registry_endpoint/migrations:latest" .
docker push "$registry_endpoint/migrations:latest"
infra/scripts/preflight-bootstrap-images.sh "$registry_name" "$registry_endpoint"

- name: Terraform Plan
run: terraform plan -out=tfplan -input=false -lock-timeout=120s
Expand Down
15 changes: 15 additions & 0 deletions docs/migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,21 @@ multi-worker race to defend against.
> ever executes `alembic upgrade head` per deploy. `alembic/env.py` relies
> on this and does not include application-layer concurrency controls.

### Bootstrap image preflight

Terraform keeps the migration job shape, but it no longer manages rollout tags.
The job template now uses a bootstrap image (`migrations:bootstrap`) and ignores
future image changes. Deploy uses commit SHA tags when it starts the job.

Before `terraform apply`, check that the bootstrap image exists in ACR:

```bash
infra/scripts/preflight-bootstrap-images.sh <acr-name> <acr-endpoint>
```

The deploy workflow runs this same preflight in CI and fails early with a clear
error if the bootstrap image is missing.

### Failure Detection

`alembic/env.py` logs and re-raises every exception from
Expand Down
8 changes: 7 additions & 1 deletion infra/migrations.tf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ resource "azurerm_container_app_job" "migrations" {
replica_retry_limit = 0
tags = local.tags

lifecycle {
ignore_changes = [
template[0].container[0].image,
]
}

identity {
type = "UserAssigned"
identity_ids = [azurerm_user_assigned_identity.migrations.id]
Expand All @@ -25,7 +31,7 @@ resource "azurerm_container_app_job" "migrations" {
template {
container {
name = "migrations"
image = "${azurerm_container_registry.main.login_server}/migrations:latest"
image = "${azurerm_container_registry.main.login_server}/migrations:bootstrap"
command = ["python"]
args = ["scripts/run_migrations.py"]
cpu = 0.5
Expand Down
59 changes: 59 additions & 0 deletions infra/scripts/preflight-bootstrap-images.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
set -euo pipefail

registry_name="${1:-}"
registry_endpoint="${2:-}"

if [[ -z "$registry_name" || -z "$registry_endpoint" ]]; then
echo "Usage: $0 <acr-name> <acr-endpoint>" >&2
exit 2
fi

missing=0

emit_error() {
local message="$1"
if [[ "${GITHUB_ACTIONS:-}" == "true" ]]; then
echo "::error::$message"
return
fi

echo "ERROR: $message" >&2
}

check_image() {
local repository="$1"
local tag="$2"
local image_ref="$registry_endpoint/$repository:$tag"

if az acr repository show-tags \
--name "$registry_name" \
--repository "$repository" \
--query "contains(@, '$tag')" \
--output tsv 2>/dev/null | grep -q true; then
echo "Found required bootstrap image: $image_ref"
return
fi

emit_error "Missing required bootstrap image: $image_ref"
missing=1
}

check_image migrations bootstrap

if [[ "$missing" -eq 0 ]]; then
exit 0
fi

cat <<EOF
Terraform uses bootstrap image tags to create Azure Container App resources.
Seed missing bootstrap images before terraform apply, for example:

az acr login --name $registry_name
docker build -f api/Dockerfile \\
--target migrations-runtime \\
-t $registry_endpoint/migrations:bootstrap .
docker push $registry_endpoint/migrations:bootstrap
EOF

exit 1