GitHub Actions deploy: build on push, deploy on demand¶
Motivation¶
Until now every deploy to the bundled Container App ran from a maintainer's
laptop via scripts/dev/quick-deploy.sh or scripts/dev/postprovision.sh.
The user asked for the build step to run automatically in CI while keeping
the actual revision swap behind a manual trigger they control from the
GitHub UI.
User-facing change¶
Two new workflows under .github/workflows/:
- Build Images (
build-images.yml) — runs on every push tomainthat touchesapi/**,web/**,terminal/**, or the related shell helpers underscripts/dev/. Buildselb-api,elb-frontend, andelb-terminalin parallel viaaz acr buildand tags each asgha-<short-sha>pluslatest-main. Does not touch the running Container App. - Deploy to Container App (
deploy.yml) —workflow_dispatchonly. Maintainer picks a sidecar (all/api/worker/beat/frontend/terminal) and a tag (defaults tolatest-main). Verifies the chosen tag exists in ACR, then patches the Container App viaaz containerapp update. Gated behind a GitHubproductionenvironment that requires a maintainer approval click beforeaz containerapp updateactually runs. Ends with a 3-minute retry loop onhttps://$CONTAINER_APP_FQDN/api/health.
API / IaC diff summary¶
scripts/dev/quick-deploy.sh:- new
--no-buildflag: skipaz acr build, only PATCH an existing tag. Used bydeploy.yml. - new
--build-onlyflag: build images, skip the Container App PATCH. Used bybuild-images.yml. - The two flags are mutually exclusive (
dieif both set). - Frontend PATCH conditionally skips
--set-env-varswhen--no-buildso GHA does not need to re-resolve VITE_ on every deploy — runtime env from the last full deploy (or Bicep) stays as-is, while the build-baked VITE_ values from the image remain authoritative. scripts/dev/setup-gha-oidc.sh(new) — one-shot, idempotent helper that creates an App Registrationgha-elb-dashboard, federated identity credentials formainpush / PR /productionenvironment, and the minimum RBAC:AcrPushon the ACR,Contributoron the Container App,Readeron the RG. No client secrets. Prints the GitHub secrets + variables to paste into repo settings.- No Bicep changes. No new Azure resources beyond the App Registration.
Security¶
- OIDC federated credential only — no client secret in source, env, or Key Vault. Satisfies charter §12.
- Scope is intentionally narrow:
AcrPushon the single ACR,Contributoronly on the Container App (not the whole RG). The principal cannot create/delete resources, cannot read Key Vault, cannot touch Storage. productionenvironment with required reviewer means an attacker who somehow forces a workflow run still cannot mutate the live revision without a human approval click.
Validation evidence¶
bash -n scripts/dev/quick-deploy.sh→ syntax OK after both patches.bash -n scripts/dev/setup-gha-oidc.sh→ syntax OK.- Workflow YAML follows the same patterns as the existing
.github/workflows/{docs,release,test}.ymlfiles. - The workflows have not been exercised end-to-end yet — the operator must
first run
scripts/dev/setup-gha-oidc.sh, paste the secrets/variables into GitHub, and create theproductionenvironment. The first push tomainafter that will produce the first artifact.
Rollback¶
- Delete
.github/workflows/build-images.ymland.github/workflows/deploy.yml. - Revert the
quick-deploy.shpatch — the--no-build/--build-onlypaths are additive and the default behaviour is unchanged. scripts/dev/setup-gha-oidc.shis opt-in. If the OIDC App Registration has already been created,az ad app delete --id <APP_ID>removes it.