diff --git a/README.md b/README.md index 285070a..e85e7cc 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,8 @@ devclaw status # Container state, FQDN, RG devclaw logs # Tail logs until you see `[gateway] starting HTTP server` ``` +If `devclaw` asks you to authenticate Azure CLI, run `devclaw login` for this repo profile and retry. This is typically needed for runtime operations (`logs`, `start`, `stop`, `restart`) and occasionally for live-state lookup. + `devclaw test` only prints a hint that points you at the in-container console. It does not exercise the model end-to-end. The fastest real smoke test is the WebChat UI below. ### Open the WebChat UI diff --git a/devclaw b/devclaw old mode 100644 new mode 100755 index 3168a41..8d5b1ae --- a/devclaw +++ b/devclaw @@ -44,10 +44,22 @@ check_prereqs() { get_app() { RG=$(azd env get-value AZURE_RESOURCE_GROUP 2>/dev/null || true) + SUB=$(azd env get-value AZURE_SUBSCRIPTION_ID 2>/dev/null || true) if [ -z "$RG" ]; then echo -e " ${R}No deployment found. Run 'devclaw up' first.${N}" exit 1 fi + if ! az account show >/dev/null 2>&1; then + echo -e " ${Y}Azure CLI is not logged in for this workspace profile.${N}" + echo -e " ${D}Run 'devclaw login' and retry.${N}" + echo -e " ${D}Profile: ${AZURE_CONFIG_DIR}${N}" + exit 1 + fi + if [ -n "$SUB" ] && ! az account set --subscription "$SUB" >/dev/null 2>&1; then + echo -e " ${Y}Azure CLI cannot access subscription ${SUB}.${N}" + echo -e " ${D}Run 'devclaw login' with an account that can access this subscription.${N}" + exit 1 + fi APP=$(az containerapp list --resource-group "$RG" --query "[0].name" -o tsv 2>/dev/null || true) if [ -z "$APP" ]; then echo -e " ${R}No container app found in $RG.${N}" @@ -203,15 +215,53 @@ test) ;; status) - get_app + RG=$(azd env get-value AZURE_RESOURCE_GROUP 2>/dev/null || true) + SUB=$(azd env get-value AZURE_SUBSCRIPTION_ID 2>/dev/null || true) + if [ -z "$RG" ]; then + echo -e " ${R}No deployment found. Run 'devclaw up' first.${N}" + exit 1 + fi + + APP="" + SHOW_JSON=$(azd show -o json 2>/dev/null || true) + if [ -n "$SHOW_JSON" ]; then + RESOURCE_ID=$(printf '%s\n' "$SHOW_JSON" | grep -Eo '/subscriptions/[^\"]+/resourceGroups/[^\"]+/providers/Microsoft\.App/containerApps/[^\"]+' | head -1 || true) + if [ -n "$RESOURCE_ID" ]; then + APP="${RESOURCE_ID##*/}" + fi + fi + + # Fallback if azd show did not include resource IDs yet. + if [ -z "$APP" ]; then + HOST=$(azd env get-value HOST_FQDN 2>/dev/null || true) + if [ -n "$HOST" ]; then + APP="${HOST%%.*}" + fi + fi + + if [ -z "$APP" ]; then + echo -e " ${R}No container app found in $RG.${N}" + echo -e " ${D}Try 'devclaw deploy' or check 'azd show'.${N}" + exit 1 + fi + echo "" echo -e " ${C}devclaw${N}" echo -e " ${C}--------${N}" - FQDN=$(az containerapp show --name "$APP" --resource-group "$RG" \ - --query "properties.configuration.ingress.fqdn" -o tsv 2>/dev/null) - STATUS=$(az containerapp revision list --name "$APP" --resource-group "$RG" \ - --query "[?properties.active].properties.runningState" -o tsv 2>/dev/null | head -1) + FQDN=$(azd env get-value HOST_FQDN 2>/dev/null || true) + if [ -z "$FQDN" ] && [ -n "$SHOW_JSON" ]; then + FQDN=$(printf '%s\n' "$SHOW_JSON" | sed -n 's#.*"ingressUrl"[[:space:]]*:[[:space:]]*"https\{0,1\}://\([^/\"]*\)/\{0,1\}".*#\1#p' | head -1) + fi + + STATUS="unknown" + if az account show >/dev/null 2>&1; then + if [ -z "$SUB" ] || az account set --subscription "$SUB" >/dev/null 2>&1; then + STATUS=$(az containerapp revision list --name "$APP" --resource-group "$RG" \ + --query "[?properties.active].properties.runningState" -o tsv 2>/dev/null | head -1) + [ -z "$STATUS" ] && STATUS="unknown" + fi + fi SC="$Y" [ "$STATUS" = "Running" ] && SC="$G" diff --git a/infra/hooks/postdeploy.sh b/infra/hooks/postdeploy.sh old mode 100644 new mode 100755 diff --git a/infra/hooks/postprovision.sh b/infra/hooks/postprovision.sh old mode 100644 new mode 100755 diff --git a/infra/hooks/predeploy.sh b/infra/hooks/predeploy.sh old mode 100644 new mode 100755 diff --git a/infra/hooks/preprovision.sh b/infra/hooks/preprovision.sh old mode 100644 new mode 100755 index f4b70c1..d005ce1 --- a/infra/hooks/preprovision.sh +++ b/infra/hooks/preprovision.sh @@ -25,9 +25,28 @@ fi # Read a free-form azd env value (returns empty string if unset). azd_flag() { - azd env get-value "$1" 2>/dev/null | tr -d '[:space:]' || true + local value + + if ! value="$(azd env get-value "$1" 2>/dev/null)"; then + echo "" + return 0 + fi + + value="$(printf '%s' "$value" | tr -d '[:space:]')" + + # `azd env get-value` may print error text when a key is missing. + case "$value" in + ERROR:*|Suggestion:*) + echo "" + ;; + *) + echo "$value" + ;; + esac } +GUID_REGEX='[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}' + # Some corporate tenants require a serviceManagementReference (an SMR GUID # referencing a service catalogue / asset management record) on every new # Entra ID app registration. When set, pass it through to `az ad app create`. @@ -39,7 +58,7 @@ if [ -z "$SMR" ]; then # Auto-detect: look for an SMR on the user's existing app registrations echo "[preprovision] SERVICE_MANAGEMENT_REFERENCE not set — checking existing app registrations..." DETECTED_SMR=$(az ad app list --show-mine --query "[?serviceManagementReference != null].serviceManagementReference | [0]" -o tsv 2>/dev/null || echo "") - if [[ "$DETECTED_SMR" =~ ^[0-9a-fA-F-]{36}$ ]]; then + if echo "$DETECTED_SMR" | grep -Eq "^$GUID_REGEX$"; then echo "[preprovision] Auto-detected SMR from your existing apps: $DETECTED_SMR" echo "[preprovision] Using it. To override, run: azd env set SERVICE_MANAGEMENT_REFERENCE " SMR="$DETECTED_SMR" @@ -65,11 +84,12 @@ fi # Then re-run `devclaw up`. Existing deployments that already have a # BOT_APP_ID continue to work without setting the flag. # --------------------------------------------------------------------------- -EXISTING_APP_ID=$(azd env get-value BOT_APP_ID 2>/dev/null | grep -oP '^[0-9a-f-]+$' || echo "") +EXISTING_APP_ID=$(azd_flag BOT_APP_ID | grep -Eo "$GUID_REGEX" || echo "") ENABLE_TEAMS_FLAG="$(azd_flag ENABLE_TEAMS)" +ENABLE_TEAMS_FLAG_LOWER="$(printf '%s' "$ENABLE_TEAMS_FLAG" | tr '[:upper:]' '[:lower:]')" if [ -n "$EXISTING_APP_ID" ]; then echo "[preprovision] Bot app registration already exists: $EXISTING_APP_ID" -elif [ "${ENABLE_TEAMS_FLAG,,}" != "true" ]; then +elif [ "$ENABLE_TEAMS_FLAG_LOWER" != "true" ]; then echo "[preprovision] Teams integration not enabled — skipping bot app registration." echo "[preprovision] To enable Teams later: azd env set ENABLE_TEAMS true && devclaw up" else @@ -81,7 +101,7 @@ else --sign-in-audience "AzureADMyOrg" \ ${SMR_ARGS[@]+"${SMR_ARGS[@]}"} \ --query appId -o tsv 2>&1) - APP_ID=$(echo "$APP_ERR" | grep -oP '^[0-9a-f-]{36}$' | head -1) + APP_ID=$(echo "$APP_ERR" | grep -Eo "$GUID_REGEX" | head -1) if [ -z "$APP_ID" ]; then echo "[preprovision] ERROR: Failed to create bot app registration" @@ -129,7 +149,7 @@ fi # Easy Auth — Entra ID app registration for ACA built-in authentication # Forces Microsoft login before any request reaches the container # --------------------------------------------------------------------------- -EXISTING_AUTH_ID=$(azd env get-value EASYAUTH_APP_ID 2>/dev/null | grep -oP '^[0-9a-f-]+$' || echo "") +EXISTING_AUTH_ID=$(azd_flag EASYAUTH_APP_ID | grep -Eo "$GUID_REGEX" || echo "") if [ -n "$EXISTING_AUTH_ID" ]; then echo "[preprovision] Easy Auth app registration already exists: $EXISTING_AUTH_ID" else @@ -144,7 +164,7 @@ else --enable-id-token-issuance true \ ${SMR_ARGS[@]+"${SMR_ARGS[@]}"} \ --query appId -o tsv 2>&1) - AUTH_APP_ID=$(echo "$AUTH_OUTPUT" | grep -oP '^[0-9a-f-]{36}$' | head -1) + AUTH_APP_ID=$(echo "$AUTH_OUTPUT" | grep -Eo "$GUID_REGEX" | head -1) if [ -z "$AUTH_APP_ID" ]; then echo "[preprovision] ERROR: Failed to create Easy Auth app registration" diff --git a/infra/main.bicep b/infra/main.bicep index 9f4301b..d5d64e6 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -20,7 +20,6 @@ param environmentName string 'swedencentral' 'switzerlandnorth' 'uksouth' - 'westcentralus' ]) @metadata({ azd: {