Per-deployment control-plane toggles survive every redeploy¶
Motivation¶
The optional Service Bus integration (and its dashboard Message Flow card)
kept disappearing after a redeploy. The card renders only when
service_bus_enabled() is true, which requires the env gate
SERVICEBUS_ENABLED to be truthy on the api / worker / beat sidecars.
Root cause was in the deploy path, not the app:
scripts/dev/quick-deploy.shonly calledload_azd_envwhen one of the four core target vars (AZURE_RESOURCE_GROUP,ACR_NAME,ACR_LOGIN_SERVER,CONTAINER_APP_NAME) was unset. The normal moonchoi deploy passes all four explicitly, so azd env was skipped entirely and the per-deployment pinSERVICEBUS_ENABLED=true(stored only in azd env) never reachedcontrol_plane_env_pairs— it fell back to theinfra/control-plane-env.jsondefault"false"and every such redeploy reset the live env tofalse.- Even when
load_azd_envdid run,azd env get-valuesblocks on an interactiveSelect an environment to use:prompt (no.azure/config.jsondefault environment), gets killed by the 8 s timeout, and leaves the prompt text on stdout — which is non-whitespace, so the old "is the output empty?" check was fooled and no fallback fired.
User-facing change¶
No UI change. Operationally: a control-plane toggle pinned in azd env
(azd env set SERVICEBUS_ENABLED true) now stays applied across every
quick-deploy.sh redeploy instead of silently reverting to the repo default.
The repo default stays OFF (charter §12a Rule 4); only an explicit azd-env (or
CLI/file) value turns it on.
API / IaC diff summary¶
scripts/dev/quick-deploy.sh—load_azd_envis now called unconditionally in the deploy preamble (it only fills UNSET keys via the${!key+x}guard, so explicit CLI/file target overrides are untouched).scripts/dev/lib-env.sh—load_azd_envredirectsazd env get-valuesstdin from/dev/nullso the CLI can never hang on an interactive prompt (fails fast instead of burning the timeout).- The "CLI produced usable data?" test now checks for an actual
KEY=VALUEassignment line, not merely non-empty output, so a killed prompt no longer suppresses the fallback. - New best-effort
_azd_env_fileresolver + a direct-read fallback to.azure/<env>/.env(resolution order:$AZURE_ENV_NAME→.azure/config.jsondefaultEnvironment→ the sole.azure/*/dir). This keeps the pin flowing even when theazdCLI is absent / not logged in / slow. - No Bicep / infra template change. The
azd provisionpath already wiredserviceBusEnabledfrom${SERVICEBUS_ENABLED=}(infra/main.parameters.json); this fix closes the parallel gap in thequick-deploy.sh--set-env-varspath.
Validation evidence¶
bash -n scripts/dev/lib-env.sh scripts/dev/quick-deploy.sh scripts/dev/tests/test_lib_env.sh— clean.bash scripts/dev/tests/test_lib_env.sh— ALL PASS, including the new case "load_azd_env falls back to .azure/<env>/.env when CLI yields nothing" (asserts the pin is imported AND an explicit export is preserved).- Real-repo smoke (clean
env -i):load_azd_envimportsSERVICEBUS_ENABLED=trueand the correctAZURE_SUBSCRIPTION_IDin 0 s (file fallback, no prompt wait). - End-to-end: with the four core target vars pre-set (the flow that previously
skipped azd env),
control_plane_env_pairsnow emitsSERVICEBUS_ENABLED=trueforapi,worker, andbeat. uv run pytest -q api/tests/test_control_plane_env.py— 10 passed.- Live remediation (immediate): re-applied
SERVICEBUS_ENABLED=trueto the three sidecars (revision0000390, RunningAtMaxScale);/api/settings/ service-bus→effective_enabled: true;/api/monitor/message-flow→enabled: true,active_total: 30.