diff --git a/.github/workflows/deploy-orchestrator.yml b/.github/workflows/deploy-orchestrator.yml index 75676e43..257e4aa8 100644 --- a/.github/workflows/deploy-orchestrator.yml +++ b/.github/workflows/deploy-orchestrator.yml @@ -22,6 +22,11 @@ on: required: false default: false type: boolean + enableScalability: + description: 'Enable Scalability (WAF only - opt-in)' + required: false + default: false + type: boolean EXP: description: 'Enable EXP' required: false @@ -83,6 +88,7 @@ jobs: azure_location: ${{ inputs.azure_location }} resource_group_name: ${{ inputs.resource_group_name }} waf_enabled: ${{ inputs.waf_enabled }} + enableScalability: ${{ inputs.enableScalability }} EXP: ${{ inputs.EXP }} build_docker_image: ${{ inputs.build_docker_image }} existing_webapp_url: ${{ inputs.existing_webapp_url }} diff --git a/.github/workflows/deploy-v2.yml b/.github/workflows/deploy-v2.yml index 8fbebdad..bb67b532 100644 --- a/.github/workflows/deploy-v2.yml +++ b/.github/workflows/deploy-v2.yml @@ -31,7 +31,7 @@ on: - 'Local' default: 'codespace' azure_location: - description: 'Azure Location For Deployment' + description: 'Azure Region (Non-AI Services)' required: false default: 'australiaeast' type: choice @@ -49,25 +49,31 @@ on: required: false default: '' type: string + build_docker_image: + description: 'Build & Use Custom Images (Optional)' + required: false + default: false + type: boolean waf_enabled: - description: 'Enable WAF' + description: 'Deploy WAF' required: false default: false type: boolean EXP: - description: 'Enable EXP' + description: 'Deploy EXP' required: false default: false type: boolean - build_docker_image: - description: 'Build And Push Docker Image (Optional)' + + enableScalability: + description: 'Enable Scalability (WAF only)' required: false default: false type: boolean cleanup_resources: - description: 'Cleanup Deployed Resources' + description: 'Auto Delete RG' required: false default: false type: boolean @@ -83,17 +89,17 @@ on: - 'None' AZURE_ENV_EXISTING_LOG_ANALYTICS_WORKSPACE_RID: - description: 'Log Analytics Workspace Resource ID (Optional)' + description: 'Existing Log Analytics Workspace Resource ID (Optional)' required: false default: '' type: string AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: - description: 'AI Project Resource ID (Optional)' + description: 'Existing AI Project Resource ID (Optional)' required: false default: '' type: string existing_webapp_url: - description: 'Existing Container WebApp URL (Skips Deployment)' + description: 'Run Tests Against Existing RG (Provide Web App URL)' required: false default: '' type: string @@ -113,6 +119,7 @@ jobs: azure_location: ${{ steps.validate.outputs.azure_location }} resource_group_name: ${{ steps.validate.outputs.resource_group_name }} waf_enabled: ${{ steps.validate.outputs.waf_enabled }} + enable_scalability: ${{ steps.validate.outputs.enable_scalability }} exp: ${{ steps.validate.outputs.exp }} build_docker_image: ${{ steps.validate.outputs.build_docker_image }} cleanup_resources: ${{ steps.validate.outputs.cleanup_resources }} @@ -128,6 +135,7 @@ jobs: INPUT_AZURE_LOCATION: ${{ github.event.inputs.azure_location }} INPUT_RESOURCE_GROUP_NAME: ${{ github.event.inputs.resource_group_name }} INPUT_WAF_ENABLED: ${{ github.event.inputs.waf_enabled }} + INPUT_ENABLE_SCALABILITY: ${{ github.event.inputs.enableScalability }} INPUT_EXP: ${{ github.event.inputs.EXP }} INPUT_BUILD_DOCKER_IMAGE: ${{ github.event.inputs.build_docker_image }} INPUT_CLEANUP_RESOURCES: ${{ github.event.inputs.cleanup_resources }} @@ -187,6 +195,18 @@ jobs: else echo "✅ waf_enabled: '$WAF_ENABLED' is valid" fi + + # Validate enableScalability (boolean, WAF-only opt-in) + ENABLE_SCALABILITY="${INPUT_ENABLE_SCALABILITY:-false}" + if [[ "$ENABLE_SCALABILITY" != "true" && "$ENABLE_SCALABILITY" != "false" ]]; then + echo "❌ ERROR: enableScalability must be 'true' or 'false', got: '$ENABLE_SCALABILITY'" + VALIDATION_FAILED=true + else + echo "✅ enableScalability: '$ENABLE_SCALABILITY' is valid" + if [[ "$ENABLE_SCALABILITY" == "true" && "$WAF_ENABLED" != "true" ]]; then + echo "ℹ️ Note: enableScalability=true only applies when waf_enabled=true; it will be ignored for non-WAF deployments." + fi + fi # Validate EXP (boolean) EXP_ENABLED="${INPUT_EXP:-false}" @@ -280,6 +300,7 @@ jobs: echo "azure_location=$LOCATION" >> $GITHUB_OUTPUT echo "resource_group_name=$INPUT_RESOURCE_GROUP_NAME" >> $GITHUB_OUTPUT echo "waf_enabled=$WAF_ENABLED" >> $GITHUB_OUTPUT + echo "enable_scalability=$ENABLE_SCALABILITY" >> $GITHUB_OUTPUT echo "exp=$EXP_ENABLED" >> $GITHUB_OUTPUT echo "build_docker_image=$BUILD_DOCKER" >> $GITHUB_OUTPUT echo "cleanup_resources=$CLEANUP_RESOURCES" >> $GITHUB_OUTPUT @@ -297,6 +318,7 @@ jobs: azure_location: ${{ needs.validate-inputs.outputs.azure_location || 'australiaeast' }} resource_group_name: ${{ needs.validate-inputs.outputs.resource_group_name || '' }} waf_enabled: ${{ needs.validate-inputs.outputs.waf_enabled == 'true' }} + enableScalability: ${{ needs.validate-inputs.outputs.enable_scalability == 'true' }} EXP: ${{ needs.validate-inputs.outputs.exp == 'true' }} build_docker_image: ${{ needs.validate-inputs.outputs.build_docker_image == 'true' }} cleanup_resources: ${{ needs.validate-inputs.outputs.cleanup_resources == 'true' }} diff --git a/.github/workflows/deploy-windows.yml b/.github/workflows/deploy-windows.yml index 256a4bd8..1fc6f563 100644 --- a/.github/workflows/deploy-windows.yml +++ b/.github/workflows/deploy-windows.yml @@ -30,6 +30,11 @@ on: required: false default: false type: boolean + enableScalability: + description: 'Enable Scalability (WAF only - opt-in)' + required: false + default: false + type: boolean EXP: description: 'Enable EXP' required: false @@ -87,6 +92,7 @@ jobs: azure_location: ${{ steps.validate.outputs.azure_location }} resource_group_name: ${{ steps.validate.outputs.resource_group_name }} waf_enabled: ${{ steps.validate.outputs.waf_enabled }} + enable_scalability: ${{ steps.validate.outputs.enable_scalability }} exp: ${{ steps.validate.outputs.exp }} build_docker_image: ${{ steps.validate.outputs.build_docker_image }} cleanup_resources: ${{ steps.validate.outputs.cleanup_resources }} @@ -102,6 +108,7 @@ jobs: INPUT_AZURE_LOCATION: ${{ github.event.inputs.azure_location }} INPUT_RESOURCE_GROUP_NAME: ${{ github.event.inputs.resource_group_name }} INPUT_WAF_ENABLED: ${{ github.event.inputs.waf_enabled }} + INPUT_ENABLE_SCALABILITY: ${{ github.event.inputs.enableScalability }} INPUT_EXP: ${{ github.event.inputs.EXP }} INPUT_BUILD_DOCKER_IMAGE: ${{ github.event.inputs.build_docker_image }} INPUT_CLEANUP_RESOURCES: ${{ github.event.inputs.cleanup_resources }} @@ -147,6 +154,18 @@ jobs: echo "✅ waf_enabled: '$WAF_ENABLED' is valid" fi + # Validate enableScalability (boolean, WAF-only opt-in) + ENABLE_SCALABILITY="${INPUT_ENABLE_SCALABILITY:-false}" + if [[ "$ENABLE_SCALABILITY" != "true" && "$ENABLE_SCALABILITY" != "false" ]]; then + echo "❌ ERROR: enableScalability must be 'true' or 'false', got: '$ENABLE_SCALABILITY'" + VALIDATION_FAILED=true + else + echo "✅ enableScalability: '$ENABLE_SCALABILITY' is valid" + if [[ "$ENABLE_SCALABILITY" == "true" && "$WAF_ENABLED" != "true" ]]; then + echo "ℹ️ Note: enableScalability=true only applies when waf_enabled=true; it will be ignored for non-WAF deployments." + fi + fi + # Validate EXP (boolean) EXP_ENABLED="${INPUT_EXP:-false}" if [[ "$EXP_ENABLED" != "true" && "$EXP_ENABLED" != "false" ]]; then @@ -238,6 +257,7 @@ jobs: echo "azure_location=$LOCATION" >> $GITHUB_OUTPUT echo "resource_group_name=$INPUT_RESOURCE_GROUP_NAME" >> $GITHUB_OUTPUT echo "waf_enabled=$WAF_ENABLED" >> $GITHUB_OUTPUT + echo "enable_scalability=$ENABLE_SCALABILITY" >> $GITHUB_OUTPUT echo "exp=$EXP_ENABLED" >> $GITHUB_OUTPUT echo "build_docker_image=$BUILD_DOCKER" >> $GITHUB_OUTPUT echo "cleanup_resources=$CLEANUP_RESOURCES" >> $GITHUB_OUTPUT @@ -255,6 +275,7 @@ jobs: azure_location: ${{ needs.validate-inputs.outputs.azure_location || 'australiaeast' }} resource_group_name: ${{ needs.validate-inputs.outputs.resource_group_name || '' }} waf_enabled: ${{ needs.validate-inputs.outputs.waf_enabled == 'true' }} + enableScalability: ${{ needs.validate-inputs.outputs.enable_scalability == 'true' }} EXP: ${{ needs.validate-inputs.outputs.exp == 'true' }} build_docker_image: ${{ needs.validate-inputs.outputs.build_docker_image == 'true' }} cleanup_resources: ${{ needs.validate-inputs.outputs.cleanup_resources == 'true' }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e739d864..58ec8fca 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -102,10 +102,12 @@ jobs: id: check_create_rg run: | set -e + OWNER_TAG_VALUE="${{ github.actor }}" echo "Checking if resource group exists..." rg_exists=$(az group exists --name ${{ env.RESOURCE_GROUP_NAME }}) if [ "$rg_exists" = "false" ]; then echo "Resource group does not exist. Creating..." + echo "🏷️ Adding Owner tag: Owner=${OWNER_TAG_VALUE}" # Generate current timestamp in desired format: YYYY-MM-DDTHH:MM:SS.SSSSSSSZ current_date=$(date -u +"%Y-%m-%dT%H:%M:%S.%7NZ") @@ -115,9 +117,12 @@ jobs: "Purpose=Deploying and Cleaning Up Resources for Validation" \ "CreatedDate=$current_date" \ "ApplicationName=Content Processing Accelerator" \ + "Owner=${OWNER_TAG_VALUE}" \ || { echo "Error creating resource group"; exit 1; } else echo "Resource group already exists." + echo "🏷️ Merging Owner tag on existing resource group: Owner=${OWNER_TAG_VALUE}" + az group update --name "${{ env.RESOURCE_GROUP_NAME }}" --set tags.Owner="${OWNER_TAG_VALUE}" --output none || echo "⚠️ Warning: failed to update Owner tag on existing resource group '${{ env.RESOURCE_GROUP_NAME }}'." fi - name: Generate Environment Name diff --git a/.github/workflows/job-deploy-linux.yml b/.github/workflows/job-deploy-linux.yml index 86c2529a..5ed710f9 100644 --- a/.github/workflows/job-deploy-linux.yml +++ b/.github/workflows/job-deploy-linux.yml @@ -28,6 +28,11 @@ on: required: false type: string default: "false" + ENABLE_SCALABILITY: + description: 'Enable Scalability (WAF only - opt-in)' + required: false + type: string + default: "false" AZURE_ENV_EXISTING_LOG_ANALYTICS_WORKSPACE_RID: required: false type: string @@ -188,12 +193,24 @@ jobs: shell: bash env: WAF_ENABLED: ${{ inputs.WAF_ENABLED }} + ENABLE_SCALABILITY: ${{ inputs.ENABLE_SCALABILITY }} run: | if [[ "$WAF_ENABLED" == "true" ]]; then cp infra/main.waf.parameters.json infra/main.parameters.json echo "✅ Successfully copied WAF parameters to main parameters file" + SCALABILITY_VAL="${ENABLE_SCALABILITY:-false}" + if [[ "$SCALABILITY_VAL" != "true" && "$SCALABILITY_VAL" != "false" ]]; then + echo "⚠️ Invalid ENABLE_SCALABILITY value '$SCALABILITY_VAL'; defaulting to false." + SCALABILITY_VAL="false" + fi + echo "🔧 Setting enableScalability=$SCALABILITY_VAL in infra/main.parameters.json (WAF deployment)" + jq --argjson val "$SCALABILITY_VAL" '.parameters.enableScalability.value = $val' infra/main.parameters.json > infra/main.parameters.tmp.json \ + && mv infra/main.parameters.tmp.json infra/main.parameters.json else echo "🔧 Configuring Non-WAF deployment - using default main.parameters.json..." + if [[ "${ENABLE_SCALABILITY:-false}" == "true" ]]; then + echo "ℹ️ enableScalability=true was provided but WAF is disabled; the flag will be ignored for this deployment." + fi fi - name: Install azd diff --git a/.github/workflows/job-deploy-windows.yml b/.github/workflows/job-deploy-windows.yml index e8b4bda5..149bc272 100644 --- a/.github/workflows/job-deploy-windows.yml +++ b/.github/workflows/job-deploy-windows.yml @@ -28,6 +28,11 @@ on: required: false type: string default: "false" + ENABLE_SCALABILITY: + description: 'Enable Scalability (WAF only - opt-in)' + required: false + type: string + default: "false" AZURE_ENV_EXISTING_LOG_ANALYTICS_WORKSPACE_RID: required: false type: string @@ -188,12 +193,24 @@ jobs: shell: bash env: WAF_ENABLED: ${{ inputs.WAF_ENABLED }} + ENABLE_SCALABILITY: ${{ inputs.ENABLE_SCALABILITY }} run: | if [[ "$WAF_ENABLED" == "true" ]]; then cp infra/main.waf.parameters.json infra/main.parameters.json echo "✅ Successfully copied WAF parameters to main parameters file" + SCALABILITY_VAL="${ENABLE_SCALABILITY:-false}" + if [[ "$SCALABILITY_VAL" != "true" && "$SCALABILITY_VAL" != "false" ]]; then + echo "⚠️ Invalid ENABLE_SCALABILITY value '$SCALABILITY_VAL'; defaulting to false." + SCALABILITY_VAL="false" + fi + echo "🔧 Setting enableScalability=$SCALABILITY_VAL in infra/main.parameters.json (WAF deployment)" + jq --argjson val "$SCALABILITY_VAL" '.parameters.enableScalability.value = $val' infra/main.parameters.json > infra/main.parameters.tmp.json \ + && mv infra/main.parameters.tmp.json infra/main.parameters.json else echo "🔧 Configuring Non-WAF deployment - using default main.parameters.json..." + if [[ "${ENABLE_SCALABILITY:-false}" == "true" ]]; then + echo "ℹ️ enableScalability=true was provided but WAF is disabled; the flag will be ignored for this deployment." + fi fi - name: Setup Azure Developer CLI (Windows) diff --git a/.github/workflows/job-deploy.yml b/.github/workflows/job-deploy.yml index 827b4d4b..c131651b 100644 --- a/.github/workflows/job-deploy.yml +++ b/.github/workflows/job-deploy.yml @@ -26,6 +26,11 @@ on: required: false default: false type: boolean + enableScalability: + description: 'Enable Scalability (WAF only - opt-in)' + required: false + default: false + type: boolean EXP: description: 'Enable EXP' required: false @@ -400,15 +405,19 @@ jobs: id: check_create_rg shell: bash run: | - set -e + set -e + OWNER_TAG_VALUE="${{ github.actor }}" echo "🔍 Checking if resource group '$RESOURCE_GROUP_NAME' exists..." rg_exists=$(az group exists --name $RESOURCE_GROUP_NAME) if [ "$rg_exists" = "false" ]; then echo "📦 Resource group does not exist. Creating new resource group '$RESOURCE_GROUP_NAME' in location '$AZURE_LOCATION'..." - az group create --name $RESOURCE_GROUP_NAME --location $AZURE_LOCATION --tags ${{ env.RG_TAGS }} || { echo "❌ Error creating resource group"; exit 1; } + echo "🏷️ Adding Owner tag: Owner=${OWNER_TAG_VALUE}" + az group create --name $RESOURCE_GROUP_NAME --location $AZURE_LOCATION --tags ${{ env.RG_TAGS }} "Owner=${OWNER_TAG_VALUE}" || { echo "❌ Error creating resource group"; exit 1; } echo "✅ Resource group '$RESOURCE_GROUP_NAME' created successfully." else echo "✅ Resource group '$RESOURCE_GROUP_NAME' already exists. Deploying to existing resource group." + echo "🏷️ Merging Owner tag on existing resource group: Owner=${OWNER_TAG_VALUE}" + az group update --name "$RESOURCE_GROUP_NAME" --set tags.Owner="${OWNER_TAG_VALUE}" --output none || echo "⚠️ Warning: failed to update Owner tag on existing resource group '$RESOURCE_GROUP_NAME'." fi echo "RESOURCE_GROUP_NAME=$RESOURCE_GROUP_NAME" >> $GITHUB_OUTPUT echo "RESOURCE_GROUP_NAME=$RESOURCE_GROUP_NAME" >> $GITHUB_ENV @@ -536,6 +545,7 @@ jobs: BUILD_DOCKER_IMAGE: ${{ inputs.build_docker_image || 'false' }} EXP: ${{ needs.azure-setup.outputs.EXP_ENABLED }} WAF_ENABLED: ${{ inputs.waf_enabled == true && 'true' || 'false' }} + ENABLE_SCALABILITY: ${{ inputs.enableScalability == true && 'true' || 'false' }} AZURE_ENV_EXISTING_LOG_ANALYTICS_WORKSPACE_RID: ${{ inputs.AZURE_ENV_EXISTING_LOG_ANALYTICS_WORKSPACE_RID }} AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: ${{ inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }} secrets: inherit @@ -554,6 +564,7 @@ jobs: BUILD_DOCKER_IMAGE: ${{ inputs.build_docker_image || 'false' }} EXP: ${{ needs.azure-setup.outputs.EXP_ENABLED }} WAF_ENABLED: ${{ inputs.waf_enabled == true && 'true' || 'false' }} + ENABLE_SCALABILITY: ${{ inputs.enableScalability == true && 'true' || 'false' }} AZURE_ENV_EXISTING_LOG_ANALYTICS_WORKSPACE_RID: ${{ inputs.AZURE_ENV_EXISTING_LOG_ANALYTICS_WORKSPACE_RID }} AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: ${{ inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }} secrets: inherit diff --git a/.github/workflows/job-send-notification.yml b/.github/workflows/job-send-notification.yml index 63f414ce..d604cb8a 100644 --- a/.github/workflows/job-send-notification.yml +++ b/.github/workflows/job-send-notification.yml @@ -99,17 +99,25 @@ jobs: echo "TEST_SUITE_NAME=$TEST_SUITE_NAME" >> $GITHUB_OUTPUT echo "Test Suite: $TEST_SUITE_NAME" - - name: Determine Cleanup Status + - name: Determine Cleanup Pill id: cleanup shell: bash env: CLEANUP_RESULT: ${{ inputs.cleanup_result }} run: | + PILL_BASE="display:inline-block; min-width:70px; text-align:center; padding:4px 12px; border-radius:20px; font-size:12px; font-weight:600; line-height:1.4;" case "$CLEANUP_RESULT" in - success) echo "CLEANUP_STATUS=✅ SUCCESS" >> $GITHUB_OUTPUT ;; - failure) echo "CLEANUP_STATUS=❌ FAILED (Needs Manual Cleanup)" >> $GITHUB_OUTPUT ;; - *) echo "CLEANUP_STATUS=⏭️ SKIPPED (Needs Manual Cleanup)" >> $GITHUB_OUTPUT ;; + success) + CLEANUP_PILL="✅ SUCCESS" + ;; + failure) + CLEANUP_PILL="❌ FAILED" + ;; + *) + CLEANUP_PILL="⏭️ SKIPPED" + ;; esac + echo "CLEANUP_PILL=$CLEANUP_PILL" >> $GITHUB_OUTPUT - name: Determine Configuration Label id: config @@ -122,6 +130,9 @@ jobs: EXP_LABEL=$( [ "$EXP" = "true" ] && echo "EXP" || echo "Non-EXP" ) echo "CONFIG_LABEL=${WAF_LABEL} + ${EXP_LABEL}" >> $GITHUB_OUTPUT + # ------------------------------------------------------------------ + # Quota failure + # ------------------------------------------------------------------ - name: Send Quota Failure Notification if: inputs.deploy_result == 'failure' && inputs.QUOTA_FAILED == 'true' shell: bash @@ -130,127 +141,339 @@ jobs: GITHUB_RUN_ID: ${{ github.run_id }} ACCELERATOR_NAME: ${{ env.accelerator_name }} LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }} - CLEANUP_STATUS: ${{ steps.cleanup.outputs.CLEANUP_STATUS }} + CLEANUP_PILL: ${{ steps.cleanup.outputs.CLEANUP_PILL }} CONFIG_LABEL: ${{ steps.config.outputs.CONFIG_LABEL }} run: | RUN_URL="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" - EMAIL_BODY=$(cat <Dear Team,

We would like to inform you that the ${ACCELERATOR_NAME} deployment has failed due to insufficient quota.

Status Summary:
StageStatus
Deployment❌ FAILED (Insufficient Quota)
E2E Tests⏭️ SKIPPED
Cleanup${CLEANUP_STATUS}

Configuration: ${CONFIG_LABEL}

Run URL: ${RUN_URL}

Please resolve the quota issue and retry the deployment.

Best regards,
Your Automation Team

", - "subject": "❌[CI/CD-Automation] [${ACCELERATOR_NAME}] Insufficient Quota" - } - EOF + PILL_BASE="display:inline-block; min-width:70px; text-align:center; padding:4px 12px; border-radius:20px; font-size:12px; font-weight:600; line-height:1.4;" + DEPLOY_PILL="❌ FAILED" + E2E_PILL="⏭️ SKIPPED" + + BODY_HTML=$(cat < + + + + + +
+ + + +

Pipeline Failed — Insufficient Quota

+

${ACCELERATOR_NAME} Accelerator • CI/CD Automation

+ ${CONFIG_LABEL} +
+
+

Dear Team,
The ${ACCELERATOR_NAME} deployment has failed due to insufficient quota.

+
+

Status Summary

+ + + + +
Deployment${DEPLOY_PILL}
E2E Tests${E2E_PILL}
Cleanup${CLEANUP_PILL}
+
+
+ Please resolve the quota issue and retry the deployment. +
+

Deployment Details

+ + + + + +
Triggered By${{ github.actor }}
Branch${{ env.BRANCH_NAME }}
+
+ VIEW PIPELINE RUN +
+
+ + + +
CI/CD Automation Pipeline${ACCELERATOR_NAME} Accelerator
+
+ + + HTML ) + BODY_JSON=$(printf '%s' "$BODY_HTML" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))') + PAYLOAD="{\"subject\":\"\u274C[CI/CD-Automation] [${ACCELERATOR_NAME}] Insufficient Quota\",\"body\":${BODY_JSON}}" curl -X POST "${LOGICAPP_URL}" \ -H "Content-Type: application/json" \ - -d "$EMAIL_BODY" || echo "Failed to send quota failure notification" + -d "$PAYLOAD" || echo "Failed to send quota failure notification" + # ------------------------------------------------------------------ + # Deployment failure (non-quota) + # ------------------------------------------------------------------ - name: Send Deployment Failure Notification if: inputs.deploy_result == 'failure' && inputs.QUOTA_FAILED != 'true' shell: bash env: + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_RUN_ID: ${{ github.run_id }} INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }} ACCELERATOR_NAME: ${{ env.accelerator_name }} LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }} CONFIG_LABEL: ${{ steps.config.outputs.CONFIG_LABEL }} - CLEANUP_STATUS: ${{ steps.cleanup.outputs.CLEANUP_STATUS }} + CLEANUP_PILL: ${{ steps.cleanup.outputs.CLEANUP_PILL }} run: | - RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + RUN_URL="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" RESOURCE_GROUP="$INPUT_RESOURCE_GROUP_NAME" - - EMAIL_BODY=$(cat <Dear Team,

We would like to inform you that the ${ACCELERATOR_NAME} deployment has failed.

Status Summary:
StageStatus
Deployment❌ FAILED (Deployment Issue)
E2E Tests⏭️ SKIPPED
Cleanup${CLEANUP_STATUS}

Deployment Details:
• Resource Group: ${RESOURCE_GROUP}

Configuration: ${CONFIG_LABEL}

Run URL: ${RUN_URL}

Please investigate the deployment failure at your earliest convenience.

Best regards,
Your Automation Team

", - "subject": "❌[CI/CD-Automation] [${ACCELERATOR_NAME}] Deployment-Failed" - } - EOF + PILL_BASE="display:inline-block; min-width:70px; text-align:center; padding:4px 12px; border-radius:20px; font-size:12px; font-weight:600; line-height:1.4;" + DEPLOY_PILL="❌ FAILED" + E2E_PILL="⏭️ SKIPPED" + + BODY_HTML=$(cat < + + + + + +
+ + + +

Pipeline Failed

+

${ACCELERATOR_NAME} Accelerator • CI/CD Automation

+ ${CONFIG_LABEL} +
+
+

Dear Team,
The ${ACCELERATOR_NAME} deployment has failed. Please investigate at your earliest convenience.

+
+

Status Summary

+ + + + +
Deployment${DEPLOY_PILL}
E2E Tests${E2E_PILL}
Cleanup${CLEANUP_PILL}
+
+
+ Please investigate the deployment failure at your earliest convenience. +
+

Deployment Details

+ + + + + + + +
Resource Group${RESOURCE_GROUP}
Triggered By${{ github.actor }}
Branch${{ env.BRANCH_NAME }}
+
+ INVESTIGATE FAILURE +
+
+ + + +
CI/CD Automation Pipeline${ACCELERATOR_NAME} Accelerator
+
+ + + HTML ) - + BODY_JSON=$(printf '%s' "$BODY_HTML" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))') + PAYLOAD="{\"subject\":\"\u274C[CI/CD-Automation] [${ACCELERATOR_NAME}] Deployment-Failed\",\"body\":${BODY_JSON}}" + curl -X POST "${LOGICAPP_URL}" \ -H "Content-Type: application/json" \ - -d "$EMAIL_BODY" || echo "Failed to send deployment failure notification" + -d "$PAYLOAD" || echo "Failed to send deployment failure notification" + # ------------------------------------------------------------------ + # Success (deploy + optional E2E) + # ------------------------------------------------------------------ - name: Send Success Notification if: inputs.deploy_result == 'success' && (inputs.e2e_test_result == 'skipped' || inputs.TEST_SUCCESS == 'true') shell: bash env: - INPUT_WEB_APPURL: ${{ inputs.CONTAINER_WEB_APPURL }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_RUN_ID: ${{ github.run_id }} + INPUT_CONTAINER_WEB_APPURL: ${{ inputs.CONTAINER_WEB_APPURL }} INPUT_EXISTING_WEBAPP_URL: ${{ inputs.existing_webapp_url }} INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }} INPUT_TEST_REPORT_URL: ${{ inputs.TEST_REPORT_URL }} INPUT_E2E_TEST_RESULT: ${{ inputs.e2e_test_result }} ACCELERATOR_NAME: ${{ env.accelerator_name }} LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }} - GITHUB_REPOSITORY: ${{ github.repository }} - GITHUB_RUN_ID: ${{ github.run_id }} CONFIG_LABEL: ${{ steps.config.outputs.CONFIG_LABEL }} - CLEANUP_STATUS: ${{ steps.cleanup.outputs.CLEANUP_STATUS }} - RUN_E2E_TESTS: ${{ env.RUN_E2E_TESTS }} + CLEANUP_PILL: ${{ steps.cleanup.outputs.CLEANUP_PILL }} TEST_SUITE_NAME: ${{ steps.test_suite.outputs.TEST_SUITE_NAME }} - run: | + # HTML-escape values that get embedded into the email template to avoid HTML/attribute injection from workflow inputs. + html_escape() { + printf '%s' "$1" | sed -e 's/&/\&/g' -e 's//\>/g' -e 's/"/\"/g' -e "s/'/\'/g" + } RUN_URL="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" - WEBAPP_URL="${INPUT_WEB_APPURL:-$INPUT_EXISTING_WEBAPP_URL}" - RESOURCE_GROUP="$INPUT_RESOURCE_GROUP_NAME" - TEST_REPORT_URL="$INPUT_TEST_REPORT_URL" - + WEBAPP_URL="$(html_escape "${INPUT_CONTAINER_WEB_APPURL:-$INPUT_EXISTING_WEBAPP_URL}")" + RESOURCE_GROUP="$(html_escape "$INPUT_RESOURCE_GROUP_NAME")" + TEST_REPORT_URL="$(html_escape "$INPUT_TEST_REPORT_URL")" + PILL_BASE="display:inline-block; min-width:70px; text-align:center; padding:4px 12px; border-radius:20px; font-size:12px; font-weight:600; line-height:1.4;" + DEPLOY_PILL="✅ SUCCESS" + if [ "$INPUT_E2E_TEST_RESULT" = "skipped" ]; then - EMAIL_BODY=$(cat <Dear Team,

We would like to inform you that the ${ACCELERATOR_NAME} deployment has completed successfully.

Status Summary:
StageStatus
Deployment✅ SUCCESS
E2E Tests⏭️ SKIPPED
Cleanup${CLEANUP_STATUS}

Deployment Details:
• Resource Group: ${RESOURCE_GROUP}
• Web App URL: ${WEBAPP_URL}

Configuration: ${CONFIG_LABEL}

Run URL: ${RUN_URL}

Best regards,
Your Automation Team

", - "subject": "✅[CI/CD-Automation] [${ACCELERATOR_NAME}] Success" - } - EOF - ) + E2E_PILL="⏭️ SKIPPED" + INTRO="The ${ACCELERATOR_NAME} deployment has completed successfully." + TEST_DETAIL_ROWS="" else - EMAIL_BODY=$(cat <Dear Team,

We would like to inform you that the ${ACCELERATOR_NAME} deployment and test automation has completed successfully.

Status Summary:
StageStatus
Deployment✅ SUCCESS
E2E Tests✅ SUCCESS
Cleanup${CLEANUP_STATUS}

Deployment Details:
• Resource Group: ${RESOURCE_GROUP}
• Web App URL: ${WEBAPP_URL}
• Test Suite: ${TEST_SUITE_NAME}
• Test Report: View Report

Configuration: ${CONFIG_LABEL}

Run URL: ${RUN_URL}

Best regards,
Your Automation Team

", - "subject": "✅[CI/CD-Automation] [${ACCELERATOR_NAME}] Success" - } - EOF - ) + E2E_PILL="✅ SUCCESS" + INTRO="The ${ACCELERATOR_NAME} deployment and test automation has completed successfully." + TEST_DETAIL_ROWS="Test Suite${TEST_SUITE_NAME}Test Report📄 View Report" fi - + + BODY_HTML=$(cat < + + + + + +
+ + + +

Pipeline Succeeded

+

${ACCELERATOR_NAME} Accelerator • CI/CD Automation

+ ${CONFIG_LABEL} +
+
+

Dear Team,
${INTRO}

+
+

Status Summary

+ + + + +
Deployment${DEPLOY_PILL}
E2E Tests${E2E_PILL}
Cleanup${CLEANUP_PILL}
+
+

Deployment Details

+ + + + + + + + + + ${TEST_DETAIL_ROWS} +
Resource Group${RESOURCE_GROUP}
Web App URL${WEBAPP_URL}
Triggered By${{ github.actor }}
Branch${{ env.BRANCH_NAME }}
+
+ VIEW PIPELINE RUN +
+
+ + + +
CI/CD Automation Pipeline${ACCELERATOR_NAME} Accelerator
+
+ + + HTML + ) + BODY_JSON=$(printf '%s' "$BODY_HTML" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))') + PAYLOAD="{\"subject\":\"\u2705[CI/CD-Automation] [${ACCELERATOR_NAME}] Success\",\"body\":${BODY_JSON}}" + curl -X POST "${LOGICAPP_URL}" \ -H "Content-Type: application/json" \ - -d "$EMAIL_BODY" || echo "Failed to send success notification" + -d "$PAYLOAD" || echo "Failed to send success notification" + # ------------------------------------------------------------------ + # E2E test failure (deploy succeeded) + # ------------------------------------------------------------------ - name: Send Test Failure Notification if: inputs.deploy_result == 'success' && inputs.e2e_test_result != 'skipped' && inputs.TEST_SUCCESS != 'true' shell: bash env: GITHUB_REPOSITORY: ${{ github.repository }} GITHUB_RUN_ID: ${{ github.run_id }} - INPUT_WEB_APPURL: ${{ inputs.CONTAINER_WEB_APPURL }} + INPUT_CONTAINER_WEB_APPURL: ${{ inputs.CONTAINER_WEB_APPURL }} INPUT_EXISTING_WEBAPP_URL: ${{ inputs.existing_webapp_url }} INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }} + INPUT_TEST_REPORT_URL: ${{ inputs.TEST_REPORT_URL }} ACCELERATOR_NAME: ${{ env.accelerator_name }} LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }} - CLEANUP_STATUS: ${{ steps.cleanup.outputs.CLEANUP_STATUS }} + CLEANUP_PILL: ${{ steps.cleanup.outputs.CLEANUP_PILL }} CONFIG_LABEL: ${{ steps.config.outputs.CONFIG_LABEL }} - RUN_E2E_TESTS: ${{ env.RUN_E2E_TESTS }} TEST_SUITE_NAME: ${{ steps.test_suite.outputs.TEST_SUITE_NAME }} - INPUT_TEST_REPORT_URL: ${{ inputs.TEST_REPORT_URL }} run: | - RUN_URL="https://github.com/${{ env.GITHUB_REPOSITORY }}/actions/runs/${{ env.GITHUB_RUN_ID }}" - TEST_REPORT_URL="$INPUT_TEST_REPORT_URL" - WEBAPP_URL="${INPUT_WEB_APPURL:-$INPUT_EXISTING_WEBAPP_URL}" - RESOURCE_GROUP="$INPUT_RESOURCE_GROUP_NAME" - - EMAIL_BODY=$(cat <Dear Team,

We would like to inform you that ${ACCELERATOR_NAME} test automation has failed.

Status Summary:
StageStatus
Deployment✅ SUCCESS
E2E Tests❌ FAILED
Cleanup${CLEANUP_STATUS}

Deployment Details:
• Resource Group: ${RESOURCE_GROUP}
• Web App URL: ${WEBAPP_URL}
• Test Suite: ${TEST_SUITE_NAME}
• Test Report: View Report

Configuration: ${CONFIG_LABEL}

Run URL: ${RUN_URL}

Please investigate the matter at your earliest convenience.

Best regards,
Your Automation Team

", - "subject": "❌[CI/CD-Automation] [${ACCELERATOR_NAME}] E2E Test-Failed" + # HTML-escape values that get embedded into the email template to avoid HTML/attribute injection from workflow inputs. + html_escape() { + printf '%s' "$1" | sed -e 's/&/\&/g' -e 's//\>/g' -e 's/"/\"/g' -e "s/'/\'/g" } - EOF + RUN_URL="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" + WEBAPP_URL="$(html_escape "${INPUT_CONTAINER_WEB_APPURL:-$INPUT_EXISTING_WEBAPP_URL}")" + RESOURCE_GROUP="$(html_escape "$INPUT_RESOURCE_GROUP_NAME")" + TEST_REPORT_URL="$(html_escape "$INPUT_TEST_REPORT_URL")" + PILL_BASE="display:inline-block; min-width:70px; text-align:center; padding:4px 12px; border-radius:20px; font-size:12px; font-weight:600; line-height:1.4;" + DEPLOY_PILL="✅ SUCCESS" + E2E_PILL="❌ FAILED" + + BODY_HTML=$(cat < + + + + + +
+ + + +

E2E Tests Failed

+

${ACCELERATOR_NAME} Accelerator • CI/CD Automation

+ ${CONFIG_LABEL} +
+
+

Dear Team,
The ${ACCELERATOR_NAME} test automation has failed. Please investigate at your earliest convenience.

+
+

Status Summary

+ + + + +
Deployment${DEPLOY_PILL}
E2E Tests${E2E_PILL}
Cleanup${CLEANUP_PILL}
+
+

Deployment Details

+ + + + + + + + + + + + + +
Resource Group${RESOURCE_GROUP}
Web App URL${WEBAPP_URL}
Triggered By${{ github.actor }}
Branch${{ env.BRANCH_NAME }}
Test Suite${TEST_SUITE_NAME}
Test Report📄 View Report
+
+ INVESTIGATE FAILURE +
+
+ + + +
CI/CD Automation Pipeline${ACCELERATOR_NAME} Accelerator
+
+ + + HTML ) + BODY_JSON=$(printf '%s' "$BODY_HTML" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))') + PAYLOAD="{\"subject\":\"\u274C[CI/CD-Automation] [${ACCELERATOR_NAME}] E2E Test-Failed\",\"body\":${BODY_JSON}}" curl -X POST "${LOGICAPP_URL}" \ -H "Content-Type: application/json" \ - -d "$EMAIL_BODY" || echo "Failed to send test failure notification" + -d "$PAYLOAD" || echo "Failed to send test failure notification" + # ------------------------------------------------------------------ + # Existing URL: success + # ------------------------------------------------------------------ - name: Send Existing URL Success Notification if: inputs.deploy_result == 'skipped' && inputs.existing_webapp_url != '' && inputs.e2e_test_result == 'success' && (inputs.TEST_SUCCESS == 'true' || inputs.TEST_SUCCESS == '') shell: bash @@ -261,26 +484,86 @@ jobs: INPUT_TEST_REPORT_URL: ${{ inputs.TEST_REPORT_URL }} ACCELERATOR_NAME: ${{ env.accelerator_name }} LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }} - CLEANUP_STATUS: ${{ steps.cleanup.outputs.CLEANUP_STATUS }} - RUN_E2E_TESTS: ${{ env.RUN_E2E_TESTS }} + CLEANUP_PILL: ${{ steps.cleanup.outputs.CLEANUP_PILL }} + CONFIG_LABEL: ${{ steps.config.outputs.CONFIG_LABEL }} TEST_SUITE_NAME: ${{ steps.test_suite.outputs.TEST_SUITE_NAME }} run: | - RUN_URL="https://github.com/${{ env.GITHUB_REPOSITORY }}/actions/runs/${{ env.GITHUB_RUN_ID }}" - EXISTING_URL="$INPUT_EXISTING_WEBAPP_URL" - TEST_REPORT_URL="$INPUT_TEST_REPORT_URL" - - EMAIL_BODY=$(cat <Dear Team,

The ${ACCELERATOR_NAME} pipeline executed against the specified Target URL and test automation has completed successfully.

Status Summary:
StageStatus
Deployment⏭️ SKIPPED (Tests executed on Pre-deployed RG)
E2E Tests✅ SUCCESS
Cleanup${CLEANUP_STATUS}

Test Results:
• Test Suite: ${TEST_SUITE_NAME}
${TEST_REPORT_URL:+• Test Report: View Report}
• Target URL: ${EXISTING_URL}

Run URL: ${RUN_URL}

Best regards,
Your Automation Team

", - "subject": "✅[CI/CD-Automation] [${ACCELERATOR_NAME}] Success" + # HTML-escape values that get embedded into the email template to avoid HTML/attribute injection from workflow inputs. + html_escape() { + printf '%s' "$1" | sed -e 's/&/\&/g' -e 's//\>/g' -e 's/"/\"/g' -e "s/'/\'/g" } - EOF + RUN_URL="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" + EXISTING_URL="$(html_escape "$INPUT_EXISTING_WEBAPP_URL")" + TEST_REPORT_URL="$(html_escape "$INPUT_TEST_REPORT_URL")" + PILL_BASE="display:inline-block; min-width:70px; text-align:center; padding:4px 12px; border-radius:20px; font-size:12px; font-weight:600; line-height:1.4;" + DEPLOY_PILL="⏭️ SKIPPED" + E2E_PILL="✅ SUCCESS" + if [ -n "$TEST_REPORT_URL" ]; then + REPORT_ROW="Test Report📄 View Report" + else + REPORT_ROW="" + fi + + BODY_HTML=$(cat < + + + + + +
+ + + +

Pipeline Succeeded

+

${ACCELERATOR_NAME} Accelerator • CI/CD Automation

+ ${CONFIG_LABEL} +
+
+

Dear Team,
The ${ACCELERATOR_NAME} pipeline executed against the specified Target URL and test automation has completed successfully.

+
+

Status Summary

+ + + + +
Deployment${DEPLOY_PILL}
E2E Tests${E2E_PILL}
Cleanup${CLEANUP_PILL}
+
+

Test Results

+ + + + + + + + + + ${REPORT_ROW} +
Target URL${EXISTING_URL}
Triggered By${{ github.actor }}
Branch${{ env.BRANCH_NAME }}
Test Suite${TEST_SUITE_NAME}
+
+ VIEW PIPELINE RUN +
+
+ + + +
CI/CD Automation Pipeline${ACCELERATOR_NAME} Accelerator
+
+ + + HTML ) + BODY_JSON=$(printf '%s' "$BODY_HTML" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))') + PAYLOAD="{\"subject\":\"\u2705[CI/CD-Automation] [${ACCELERATOR_NAME}] Success\",\"body\":${BODY_JSON}}" curl -X POST "${LOGICAPP_URL}" \ -H "Content-Type: application/json" \ - -d "$EMAIL_BODY" || echo "Failed to send existing URL success notification" + -d "$PAYLOAD" || echo "Failed to send existing URL success notification" + # ------------------------------------------------------------------ + # Existing URL: test failure + # ------------------------------------------------------------------ - name: Send Existing URL Test Failure Notification if: inputs.deploy_result == 'skipped' && inputs.existing_webapp_url != '' && inputs.e2e_test_result == 'failure' shell: bash @@ -291,22 +574,79 @@ jobs: INPUT_TEST_REPORT_URL: ${{ inputs.TEST_REPORT_URL }} ACCELERATOR_NAME: ${{ env.accelerator_name }} LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }} - CLEANUP_STATUS: ${{ steps.cleanup.outputs.CLEANUP_STATUS }} - RUN_E2E_TESTS: ${{ env.RUN_E2E_TESTS }} + CLEANUP_PILL: ${{ steps.cleanup.outputs.CLEANUP_PILL }} + CONFIG_LABEL: ${{ steps.config.outputs.CONFIG_LABEL }} TEST_SUITE_NAME: ${{ steps.test_suite.outputs.TEST_SUITE_NAME }} run: | - RUN_URL="https://github.com/${{ env.GITHUB_REPOSITORY }}/actions/runs/${{ env.GITHUB_RUN_ID }}" - EXISTING_URL="$INPUT_EXISTING_WEBAPP_URL" - TEST_REPORT_URL="$INPUT_TEST_REPORT_URL" - - EMAIL_BODY=$(cat <Dear Team,

The ${ACCELERATOR_NAME} pipeline executed against the specified Target URL and test automation has failed.

Status Summary:
StageStatus
Deployment⏭️ SKIPPED (Tests executed on Pre-deployed RG)
E2E Tests❌ FAILED
Cleanup${CLEANUP_STATUS}

Failure Details:
• Target URL: ${EXISTING_URL}
${TEST_REPORT_URL:+• Test Report: View Report}
• Test Suite: ${TEST_SUITE_NAME}

Run URL: ${RUN_URL}

Best regards,
Your Automation Team

", - "subject": "❌[CI/CD-Automation] [${ACCELERATOR_NAME}] E2E Test-Failed" + # HTML-escape values that get embedded into the email template to avoid HTML/attribute injection from workflow inputs. + html_escape() { + printf '%s' "$1" | sed -e 's/&/\&/g' -e 's//\>/g' -e 's/"/\"/g' -e "s/'/\'/g" } - EOF + RUN_URL="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" + EXISTING_URL="$(html_escape "$INPUT_EXISTING_WEBAPP_URL")" + TEST_REPORT_URL="$(html_escape "$INPUT_TEST_REPORT_URL")" + PILL_BASE="display:inline-block; min-width:70px; text-align:center; padding:4px 12px; border-radius:20px; font-size:12px; font-weight:600; line-height:1.4;" + DEPLOY_PILL="⏭️ SKIPPED" + E2E_PILL="❌ FAILED" + if [ -n "$TEST_REPORT_URL" ]; then + REPORT_ROW="Test Report📄 View Report" + else + REPORT_ROW="" + fi + + BODY_HTML=$(cat < + + + + + +
+ + + +

E2E Tests Failed

+

${ACCELERATOR_NAME} Accelerator • CI/CD Automation

+ ${CONFIG_LABEL} +
+
+

Dear Team,
The ${ACCELERATOR_NAME} pipeline executed against the specified Target URL and test automation has failed.

+
+

Status Summary

+ + + + +
Deployment${DEPLOY_PILL}
E2E Tests${E2E_PILL}
Cleanup${CLEANUP_PILL}
+
+

Failure Details

+ + + + + + + + + + ${REPORT_ROW} +
Target URL${EXISTING_URL}
Triggered By${{ github.actor }}
Branch${{ env.BRANCH_NAME }}
Test Suite${TEST_SUITE_NAME}
+
+ INVESTIGATE FAILURE +
+
+ + + +
CI/CD Automation Pipeline${ACCELERATOR_NAME} Accelerator
+
+ + + HTML ) + BODY_JSON=$(printf '%s' "$BODY_HTML" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))') + PAYLOAD="{\"subject\":\"\u274C[CI/CD-Automation] [${ACCELERATOR_NAME}] E2E Test-Failed\",\"body\":${BODY_JSON}}" curl -X POST "${LOGICAPP_URL}" \ -H "Content-Type: application/json" \ - -d "$EMAIL_BODY" || echo "Failed to send existing URL test failure notification" + -d "$PAYLOAD" || echo "Failed to send existing URL test failure notification"