Files
The-transit-Project/.github/agents/azure-deploy/deploy/references/code-deployment-container-apps.md
T
Sven laptop 635a130f0b
Build and Push Docker Images / detect-changes (push) Successful in 11s
Build and Push Docker Images / build-auth-api (push) Successful in 1m0s
Build and Push Docker Images / build-employee-api (push) Successful in 1m2s
Build and Push Docker Images / build-transit-api (push) Successful in 1m1s
Build and Push Docker Images / build-error-log-api (push) Successful in 1m5s
Build and Push Docker Images / build-admin-ui (push) Successful in 1m13s
Build and Push Docker Images / build-monitoring-ui (push) Successful in 1m15s
Build and Push Docker Images / deploy-test-node (push) Successful in 3s
──────
### Summary of Changes
  1. Documentation Consolidation:
      • Relocated the docs/ folder and all repository .md files (README.md, DEPLOYMENT.md, DOCKER_README.md,
      DOMAINS_README.md, GETTING_STARTED.md, CI_CD_PORTAINER_GUIDE.md, ADMIN_DEPLOYMENT_SUMMARY.md,
      SECURITY_PRIVATE_FILES.md, handoff.md, azure-project-plan.md, plus service-specific READMEs) into .
      • Updated document links in README.md.
  2. AI Agent Project Map:
      • Created PROJECT_MAP.md in the project root containing complete architecture diagrams, service specs, ports, DB
      schemas, environment variables, and developer workflows.
      • Added PROJECT_MAP.md to .gitignore.
  3. Monitoring UI & Infrastructure Integration:
      • Added monitoring_ui service to docker-compose.yml (port 5175).
      • Configured Gitea workflow docker-build.yml for monitoring_ui builds.
      • Added CORS middleware headers in index.js.
      • Added nginx.conf copy step in Dockerfile.

  ──────
  ### Recommended Commit Message

    docs: consolidate project documentation into monitoring_ui and add agent project map

    - Move all markdown documentation files and docs/ folder into monitoring_ui/docs/
    - Update documentation hub index (monitoring_ui/docs/README.md) with relative paths
    - Generate comprehensive AI agent reference map (PROJECT_MAP.md) in repository root
    - Add PROJECT_MAP.md and monitoring_ui build output/env overrides to .gitignore
    - Integrate monitoring_ui service into docker-compose.yml and Gitea CI workflow
    - Configure CORS handling in employee_api for monitoring dashboard access
    ──────
  ### Alternative Short Commit Message

    refactor(docs): move docs to monitoring_ui and create AI agent PROJECT_MAP.md
2026-09-20 23:33:55 +02:00

8.9 KiB
Raw Blame History

Important: This skill provides guidance and recommended instructions to assist the AI system. Outputs are not guaranteed to be complete, correct, secure, or applicable to every scenario. Results should be reviewed and validated by a human before being applied. The AI model may choose not to follow all instructions exactly, and additional verification may be required.

Code Deployment — Container Apps

After IaC deployment creates the Container App with a placeholder image (mcr.microsoft.com/azuredocs/containerapps-helloworld:latest), deploy the actual application code. This is Phase 2 of the two-phase Container Apps deployment pattern.

⛔ --subscription {subscriptionId} on EVERY az command (from context.json.azure.subscriptionId). Without it, the CLI uses whatever subscription is currently active — which may have changed since the prepare phase. This applies to ALL commands below.

⛔ Phase 2 is NOT optional. If IaC deployed a Container App with a placeholder image, you MUST execute Phase 2 before health checks. Do NOT leave a placeholder image and tell the user to deploy code manually.

Determine the code deploy path:

Condition Path Steps
ACR exists in IaC (services[] has Container Registry) ACR build + image update Steps 1–4 below
No ACR in IaC, simple app (single Dockerfile or source) Add ACR to IaC + redeploy, then ACR build Step 0 + Steps 1–4
No ACR in IaC, no Dockerfile, Oryx-compatible (Node/Python/Go/.NET) az containerapp update --source . Step 5 (Oryx shortcut)

Step 0 — Add ACR to IaC (if not already present)

Add container-registry.bicep module (ACR Basic, adminUserEnabled: false) + AcrPull role assignment for CA's managed identity (role GUID: 7f951dda-4ed3-4680-a7ca-43fe172d538d). Redeploy IaC, wait ~60s for AcrPull role propagation. Log as healing attempt.

⛔ Update prepare-plan.json (add ACR to services[]), scaffold-manifest.json.files[], and costEstimate.

Steps 1–4 — ACR Build + Image Update (IaC-compliant)

Step Command Notes
1. Build image az acr build --subscription {subscriptionId} -r {acrName} -t {appName}:latest . --no-logs Builds from workspace root. If buildRequirements.hasBuildKitSyntax, use -f Dockerfile.azure (see BuildKit handling below). ⛔ Build-time env vars: If Dockerfile has ARG NEXT_PUBLIC_* or ARG VITE_*, pass --build-arg NEXT_PUBLIC_API_URL=https://{api-fqdn} to inject the deployed API URL. These vars are baked into the JS bundle at build time — runtime env vars have no effect on client-side code. Get the API FQDN from Phase 1 output: az containerapp show -g {rg} -n {apiApp} --query properties.configuration.ingress.fqdn -o tsv.
2. Update Bicep image Edit infra/modules/{containerapp}.bicep: replace placeholder with image: '{acrLoginServer}/{appName}:latest'. Add ACR registry config: registries: [{ server: acrLoginServer, identity: 'system' }] IaC-only — no imperative az containerapp update
3. Redeploy az deployment sub create --subscription {subscriptionId} --location {location} --template-file infra/main.bicep --parameters @infra/main.parameters.json --parameters containerImage='{acrLoginServer}/{appName}:latest' --name app-onboard-code-deploy-{timestamp} --query properties.provisioningState -o tsv Emit new portal link. ⛔ No DB password param — databases are managed-identity only (see Parameter Pass-Through).
4. Verify revision az containerapp show --subscription {subscriptionId} -g {rg} -n {ca} --query "{revision:properties.latestReadyRevisionName, image:properties.template.containers[0].image}" -o json Confirm image is not the placeholder

⛔ az acr build failures count toward the deploy-result.json.healingAttempts[] counter. After 3 failed builds (even with different root causes), pause and present a diagnosis to the user: "Web image build failed 3 times: [root causes]. Continue healing? (Yes / Cancel)." Each build fix attempt = 1 healing entry. Cross-reference deploy instructions.md healing loop rule.

⛔ Windows: az acr build may fail with UnicodeEncodeError. Azure CLI log streaming crashes on non-ASCII characters (✓, ✗) using Windows charmap codec. Append --no-logs to the build command. Build success/failure is still reported via exit code.

Step 5 — Oryx Shortcut (no ACR needed, no Dockerfile)

az containerapp update --subscription {subscriptionId} -g {rg} -n {ca} --source . --set-env-vars NODE_ENV=production

⛔ az containerapp update --source is allowed for code deploy ONLY. This is different from az containerapp up --source (which is ⛔ BLOCKED because it creates resources imperatively). update --source updates an EXISTING Container App — no state drift.

App-Internal Secrets (native CA secrets — No Key Vault, no seeding step)

App-internal secrets are set as native Container Apps secrets directly in the Bicep, with the value supplied as an @secure() param on the deployment command. ⛔ No Key Vault, no az keyvault secret set step. Generate each secret ONCE and pass it on every az deployment sub create:

$secretKey = (openssl rand -base64 32) -replace '[/+=]',''   # generate ONCE, persist to deploy-secrets.env
az deployment sub create ... --parameters secretKey=$secretKey --query properties.provisioningState -o tsv

⛔ Reload the SAME value from deploy-secrets.env on every retry/redeploy (shell variables don't persist between tool calls). Pass it to EVERY deployment so the desired-state apply keeps the CA secret stable.

After Code Deploy (all paths)

  • Wait 30–60s for the new revision to become ready
  • Proceed to Step 7 (Health-Check Endpoints)
  • If health check returns placeholder content → image update didn't take effect. Check az containerapp revision list

⛔ az containerapp revision restart does NOT pick up a changed secret. Container Apps bind secret values at revision creation time. To pick up an updated native secret, create a NEW revision by redeploying Bicep (preferred) or az containerapp update --revision-suffix rev{timestamp}.

⛔ ACR registries require the CA managed identity + AcrPull to exist first. Phase 1 of the two-phase deploy creates the Container App with a placeholder image and registries: [] (native secrets MAY be set in Phase 1 — they have no RBAC dependency). ACR registries with identity: 'system' fails in Phase 1 because the CA's managed identity doesn't exist yet (no principalId → AcrPull fails → "Operation expired"). After Phase 1 completes and RBAC propagates (~60s), Phase 2 redeploys with ACR registries + real image + correct targetPort.

Config-File Apps (Go/Viper, Spring Boot, etc.)

Creating Azure-specific config (e.g., config-azure.yml) is fine for non-secret values.

⛔ NEVER bake secrets into config files COPY'd into Docker images.

Secret injection: Config uses placeholders → az containerapp secret set → az containerapp update --set-env-vars KEY=secretref:name → framework env var override.

Go/Viper: AutomaticEnv() maps POSTGRES_PASSWORD → postgres_password (underscores), NOT postgres.password. Without SetEnvKeyReplacer(strings.NewReplacer(".", "_")), env vars can't override nested keys.

BuildKit Dockerfile Handling

ACR's az acr build uses the classic Docker builder — it does NOT support BuildKit. When buildRequirements.hasBuildKitSyntax == true: build using the ACR-compatible copy instead: az acr build -f Dockerfile.azure .. The user's original Dockerfile stays untouched. If Dockerfile.azure doesn't exist yet, create it by stripping BuildKit syntax from the original Dockerfile per dockerfile-generation.md § ACR Build Compatibility.

Parameter Pass-Through (Bicep Redeploy Safety)

⛔ On every redeploy, pass the SAME values you originally used — a full desired-state apply, not a patch, so an omitted param reverts to its default. The one param that bites:

  • containerImage — omitting it reverts the Container App to the placeholder image.

⛔ No administratorLoginPassword (or any DB password/key) param exists — databases are Entra/managed-identity only. There is nothing to re-pass and no deploy-secrets.env DB password. App-internal secrets (e.g. SECRET_KEY) live in Key Vault and are read via the app's MI — they are not passed as deploy params.

az deployment sub create ... `
  --parameters containerImage='{acrLoginServer}/{appName}:latest' `
  --query properties.provisioningState -o tsv

Database Post-Deploy Verification

⛔ You MUST read database-post-deploy.md for migration discovery, execution via Container Apps exec, error handling, and PostgreSQL-specific checks.