# Builds both images via ACR Quick Tasks and deploys them to Container Apps.
#
# `az acr build` ships the build context to ACR and builds there, so no Docker
# daemon is needed on the runner — the same approach rust-blog uses.
#
# Repository secrets required:
# AZURE_CREDENTIALS the app deploy SP (Contributor on sub-lovelace)
# AZURE_RESOURCE_GROUP rg-lovelace
# PLATFORM_ACR acrplatform….azurecr.io
# S10_INGEST_URL, S10_INGEST_KEY telemetry; omit to run with it disabled
name: deploy-azure
on:
workflow_dispatch:
inputs:
shelf:
description: "Bake a shelf of repos into the backend image"
type: boolean
default: false
concurrency:
# One deploy at a time: two overlapping runs would race on the revision.
group: deploy-azure
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
cache-dependency-path: web/package-lock.json
- name: Build the Next.js standalone bundle
working-directory: web
run: |
npm ci
npm run build
# The runtime image wants only the assembled server, so build the
# context here rather than shipping source and node_modules to ACR.
mkdir -p .deploy/.next
cp -r .next/standalone/. .deploy/
cp -r .next/static .deploy/.next/static
cp -r public .deploy/public
cp Dockerfile .deploy/Dockerfile
- name: Build and push both images
env:
PLATFORM_ACR: ${{ secrets.PLATFORM_ACR }}
run: |
set -euo pipefail
ACR_NAME="${PLATFORM_ACR%%.*}"
TAG="${GITHUB_SHA::7}"
# ACR lives in the platform subscription; the SP has Contributor
# there, so name the subscription explicitly rather than relying on
# whichever one happens to be current.
az acr build --registry "$ACR_NAME" \
--image "lovelace-backend:$TAG" --file Dockerfile .
az acr build --registry "$ACR_NAME" \
--image "lovelace-gateway:$TAG" --file web/Dockerfile web/.deploy
echo "BACKEND=${PLATFORM_ACR}/lovelace-backend:$TAG" >> "$GITHUB_ENV"
echo "GATEWAY=${PLATFORM_ACR}/lovelace-gateway:$TAG" >> "$GITHUB_ENV"
- name: Roll the backend
env:
RG: ${{ secrets.AZURE_RESOURCE_GROUP }}
run: |
set -euo pipefail
# Pinned to a single replica on purpose. jac-serve holds every repo
# in memory, so a second replica would answer from a different world:
# an attestation made against one would be invisible from the other.
# See DEPLOY.md.
az containerapp update \
--name ca-lovelace-backend --resource-group "$RG" \
--image "$BACKEND" \
--min-replicas 1 --max-replicas 1 \
--set-env-vars \
JAC_HOST_DIR=/srv/jack \
JAC_SERVE_PORT=8787 \
S10_INGEST_URL="${{ secrets.S10_INGEST_URL }}" \
S10_INGEST_KEY="${{ secrets.S10_INGEST_KEY }}" \
--output none
- name: Roll the gateway
env:
RG: ${{ secrets.AZURE_RESOURCE_GROUP }}
run: |
set -euo pipefail
BACKEND_FQDN=$(az containerapp show \
--name ca-lovelace-backend --resource-group "$RG" \
--query "properties.configuration.ingress.fqdn" -o tsv)
az containerapp update \
--name ca-lovelace-gateway --resource-group "$RG" \
--image "$GATEWAY" \
--set-env-vars \
JAC_SERVE_URL="https://${BACKEND_FQDN}" \
--output none
- name: Say where it landed
env:
RG: ${{ secrets.AZURE_RESOURCE_GROUP }}
run: |
FQDN=$(az containerapp show --name ca-lovelace-gateway \
--resource-group "$RG" \
--query "properties.configuration.ingress.fqdn" -o tsv)
echo "Lovelace: https://${FQDN}" >> "$GITHUB_STEP_SUMMARY"