6868 BUILD_DOCKER_IMAGE : ${{ github.event_name == 'workflow_dispatch' && (github.event.inputs.build_docker_image || false) || false }}
6969
7070jobs :
71+ docker-build :
72+ # ============================================================================
73+ # DOCKER BUILD JOB - When does it run?
74+ # ============================================================================
75+ # ✅ RUNS ONLY WHEN:
76+ # - Trigger = Manual (workflow_dispatch)
77+ # AND "Build and push Docker image" checkbox = ✅ CHECKED
78+ #
79+ # ❌ SKIPPED WHEN:
80+ # - Any automatic trigger (Pull Request, Schedule, Workflow Run)
81+ # - Manual trigger but "Build and push Docker image" = ❌ UNCHECKED
82+ # - Manual trigger but checkbox not provided (defaults to false)
83+ #
84+ # SIMPLE RULE: Only builds Docker when you manually trigger AND check the box!
85+ # ============================================================================
86+ if : github.event_name == 'workflow_dispatch' && github.event.inputs.build_docker_image == 'true'
87+ runs-on : ubuntu-latest
88+ outputs :
89+ IMAGE_TAG : ${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}
90+ steps :
91+ - name : Checkout Code
92+ uses : actions/checkout@v4
93+
94+ - name : Generate Unique Docker Image Tag
95+ id : generate_docker_tag
96+ run : |
97+ echo "🔨 Building new Docker image - generating unique tag..."
98+ # Generate unique tag for manual deployment runs
99+ TIMESTAMP=$(date +%Y%m%d-%H%M%S)
100+ RUN_ID="${{ github.run_id }}"
101+ BRANCH_NAME="${{ github.head_ref || github.ref_name }}"
102+ # Sanitize branch name for Docker tag (replace invalid characters with hyphens)
103+ CLEAN_BRANCH_NAME=$(echo "$BRANCH_NAME" | sed 's/[^a-zA-Z0-9._-]/-/g' | sed 's/--*/-/g' | sed 's/^-\|-$//g')
104+ UNIQUE_TAG="${CLEAN_BRANCH_NAME}-${TIMESTAMP}-${RUN_ID}"
105+ echo "IMAGE_TAG=$UNIQUE_TAG" >> $GITHUB_ENV
106+ echo "IMAGE_TAG=$UNIQUE_TAG" >> $GITHUB_OUTPUT
107+ echo "Generated unique Docker tag: $UNIQUE_TAG"
108+
109+ - name : Set up Docker Buildx
110+ uses : docker/setup-buildx-action@v3
111+
112+ - name : Log in to Azure Container Registry
113+ uses : azure/docker-login@v2
114+ with :
115+ login-server : ${{ secrets.ACR_TEST_LOGIN_SERVER }}
116+ username : ${{ secrets.ACR_TEST_USERNAME }}
117+ password : ${{ secrets.ACR_TEST_PASSWORD }}
118+
119+ - name : Build and Push Docker Image
120+ id : build_push_image
121+ uses : docker/build-push-action@v6
122+ with :
123+ context : ./src
124+ file : ./src/WebApp.Dockerfile
125+ push : true
126+ tags : |
127+ ${{ secrets.ACR_TEST_LOGIN_SERVER }}/webapp:${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}
128+ ${{ secrets.ACR_TEST_LOGIN_SERVER }}/webapp:${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}_${{ github.run_number }}
129+
130+ - name : Verify Docker Image Build
131+ run : |
132+ echo "✅ Docker image successfully built and pushed"
133+ echo "Image tag: ${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}"
134+ echo "Run number: ${{ github.run_number }}"
135+
71136 deploy :
72- # Skip deployment if an existing webapp URL is provided
73- if : github.event_name != 'workflow_dispatch' || github.event.inputs.existing_webapp_url == '' || github.event.inputs.existing_webapp_url == null
137+ # ============================================================================
138+ # DEPLOY JOB - When does it run?
139+ # ============================================================================
140+ # ✅ RUNS WHEN:
141+ # - ANY automatic trigger (Pull Request, Schedule, Workflow Run) = ALWAYS RUNS
142+ # - Manual trigger with "Existing WebApp URL" = EMPTY/BLANK
143+ # - Manual trigger with "Existing WebApp URL" = NOT PROVIDED
144+ #
145+ # ❌ SKIPPED WHEN:
146+ # - Manual trigger with "Existing WebApp URL" = PROVIDED/FILLED
147+ # (Why skip? Because you want to test existing app, not deploy new one!)
148+ #
149+ # SIMPLE RULE: Deploys new infrastructure unless you provide existing webapp URL!
150+ # ============================================================================
151+ if : always() && (github.event_name != 'workflow_dispatch' || github.event.inputs.existing_webapp_url == '' || github.event.inputs.existing_webapp_url == null)
152+ needs : [docker-build] # Add dependency on docker-build job (will be skipped if not needed)
74153 runs-on : ubuntu-latest
75154 outputs :
76155 RESOURCE_GROUP_NAME : ${{ steps.check_create_rg.outputs.RESOURCE_GROUP_NAME }}
77156 WEBAPP_URL : ${{ steps.get_output.outputs.WEBAPP_URL }}
78157 ENV_NAME : ${{ steps.generate_env_name.outputs.ENV_NAME }}
79158 AZURE_LOCATION : ${{ steps.set_region.outputs.AZURE_LOCATION }}
80- IMAGE_TAG : ${{ steps.generate_docker_tag .outputs.IMAGE_TAG }}
159+ IMAGE_TAG : ${{ steps.determine_image_tag .outputs.IMAGE_TAG }}
81160 env :
82161 # For automatic triggers: force Non-WAF + Non-EXP, for manual dispatch: use inputs
83162 WAF_ENABLED : ${{ github.event_name == 'workflow_dispatch' && (github.event.inputs.waf_enabled || false) || false }}
@@ -245,21 +324,18 @@ jobs:
245324 echo "SOLUTION_PREFIX=${UNIQUE_SOLUTION_PREFIX}" >> $GITHUB_ENV
246325 echo "Generated SOLUTION_PREFIX: ${UNIQUE_SOLUTION_PREFIX}"
247326
248- - name : Generate Unique Docker Image Tag
249- id : generate_docker_tag
327+ - name : Determine Docker Image Tag
328+ id : determine_image_tag
250329 run : |
251330 if [[ "${{ env.BUILD_DOCKER_IMAGE }}" == "true" ]]; then
252- echo "🔨 Building new Docker image - generating unique tag..."
253- # Generate unique tag for manual deployment runs
254- TIMESTAMP=$(date +%Y%m%d-%H%M%S)
255- RUN_ID="${{ github.run_id }}"
256- BRANCH_NAME="${{ env.BRANCH_NAME }}"
257- # Sanitize branch name for Docker tag (replace invalid characters with hyphens)
258- CLEAN_BRANCH_NAME=$(echo "$BRANCH_NAME" | sed 's/[^a-zA-Z0-9._-]/-/g' | sed 's/--*/-/g' | sed 's/^-\|-$//g')
259- UNIQUE_TAG="${CLEAN_BRANCH_NAME}-${TIMESTAMP}-${RUN_ID}"
260- echo "IMAGE_TAG=$UNIQUE_TAG" >> $GITHUB_ENV
261- echo "IMAGE_TAG=$UNIQUE_TAG" >> $GITHUB_OUTPUT
262- echo "Generated unique Docker tag: $UNIQUE_TAG"
331+ # Use the tag from docker-build job if it was built
332+ if [[ "${{ needs.docker-build.result }}" == "success" ]]; then
333+ IMAGE_TAG="${{ needs.docker-build.outputs.IMAGE_TAG }}"
334+ echo "🔗 Using Docker image tag from build job: $IMAGE_TAG"
335+ else
336+ echo "❌ Docker build job failed or was skipped, but BUILD_DOCKER_IMAGE is true"
337+ exit 1
338+ fi
263339 else
264340 echo "🏷️ Using existing Docker image based on branch..."
265341 BRANCH_NAME="${{ env.BRANCH_NAME }}"
@@ -280,41 +356,11 @@ jobs:
280356 echo "Using default for branch '$BRANCH_NAME' - image tag: latest_waf"
281357 fi
282358
283- echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
284- echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_OUTPUT
285359 echo "Using existing Docker image tag: $IMAGE_TAG"
286360 fi
287-
288- - name : Set up Docker Buildx
289- if : env.BUILD_DOCKER_IMAGE == 'true'
290- uses : docker/setup-buildx-action@v3
291-
292- - name : Log in to Azure Container Registry
293- if : env.BUILD_DOCKER_IMAGE == 'true'
294- uses : azure/docker-login@v2
295- with :
296- login-server : ${{ secrets.ACR_TEST_LOGIN_SERVER }}
297- username : ${{ secrets.ACR_TEST_USERNAME }}
298- password : ${{ secrets.ACR_TEST_PASSWORD }}
299-
300- - name : Build and Push Docker Image
301- if : env.BUILD_DOCKER_IMAGE == 'true'
302- id : build_push_image
303- uses : docker/build-push-action@v6
304- with :
305- context : ./src
306- file : ./src/WebApp.Dockerfile
307- push : true
308- tags : |
309- ${{ secrets.ACR_TEST_LOGIN_SERVER }}/webapp:${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}
310- ${{ secrets.ACR_TEST_LOGIN_SERVER }}/webapp:${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}_${{ github.run_number }}
311-
312- - name : Verify Docker Image Build
313- if : env.BUILD_DOCKER_IMAGE == 'true'
314- run : |
315- echo "✅ Docker image successfully built and pushed"
316- echo "Image tag: ${{ env.IMAGE_TAG }}"
317- echo "Run number: ${{ github.run_number }}"
361+
362+ echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
363+ echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_OUTPUT
318364
319365 - name : Generate Unique Environment Name
320366 id : generate_env_name
@@ -345,9 +391,9 @@ jobs:
345391 - name : Display Docker Image Tag
346392 run : |
347393 echo "=== Docker Image Information ==="
348- echo "Docker Image Tag: ${{ env .IMAGE_TAG }}"
394+ echo "Docker Image Tag: ${{ steps.determine_image_tag.outputs .IMAGE_TAG }}"
349395 echo "Registry: ${{ secrets.ACR_TEST_LOGIN_SERVER }}"
350- echo "Full Image: ${{ secrets.ACR_TEST_LOGIN_SERVER }}/webapp:${{ env .IMAGE_TAG }}"
396+ echo "Full Image: ${{ secrets.ACR_TEST_LOGIN_SERVER }}/webapp:${{ steps.determine_image_tag.outputs .IMAGE_TAG }}"
351397 echo "================================"
352398
353399 - name : Deploy using azd up and extract values (${{ github.event.inputs.waf_enabled == 'true' && 'WAF' || 'Non-WAF' }}+${{ (github.event.inputs.EXP == 'true' || github.event.inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID != '' || github.event.inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID != '') && 'EXP' || 'Non-EXP' }})
@@ -357,7 +403,7 @@ jobs:
357403 echo "Starting azd deployment..."
358404 echo "WAF Enabled: ${{ env.WAF_ENABLED }}"
359405 echo "EXP: ${{ env.EXP }}"
360- echo "Using Docker Image Tag: ${{ env .IMAGE_TAG }}"
406+ echo "Using Docker Image Tag: ${{ steps.determine_image_tag.outputs .IMAGE_TAG }}"
361407
362408 # Install azd (Azure Developer CLI)
363409 curl -fsSL https://aka.ms/install-azd.sh | bash
@@ -376,7 +422,7 @@ jobs:
376422 azd env set AZURE_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}"
377423 azd env set AZURE_ENV_OPENAI_LOCATION="$AZURE_LOCATION"
378424 azd env set AZURE_RESOURCE_GROUP="$RESOURCE_GROUP_NAME"
379- azd env set AZURE_ENV_IMAGETAG="${{ env .IMAGE_TAG }}"
425+ azd env set AZURE_ENV_IMAGETAG="${{ steps.determine_image_tag.outputs .IMAGE_TAG }}"
380426 azd env set AZURE_DEV_COLLECT_TELEMETRY="no"
381427
382428 # Set ACR name only when building Docker image
@@ -496,9 +542,21 @@ jobs:
496542 echo "Logged out from Azure."
497543
498544 e2e-test :
499- # Run e2e tests only when deploy succeeds or when using existing webapp URL
500- # Skip if deploy job failed (for both automatic and manual triggers)
501- # Respect the run_e2e_tests checkbox - skip if explicitly unchecked
545+ # ============================================================================
546+ # E2E TEST JOB - When does it run?
547+ # ============================================================================
548+ # ✅ RUNS WHEN:
549+ # - Deploy job succeeded AND "Run end-to-end tests" = ✅ CHECKED (default)
550+ # - Manual trigger with "Existing WebApp URL" provided AND tests = ✅ CHECKED
551+ # - Any automatic trigger (tests always run by default if deploy succeeds)
552+ #
553+ # ❌ SKIPPED WHEN:
554+ # - Deploy job failed or was skipped
555+ # - Manual trigger with "Run end-to-end tests" = ❌ UNCHECKED
556+ # - No webapp available (neither deployed nor existing URL provided)
557+ #
558+ # SIMPLE RULE: Tests run against any available webapp unless you uncheck the box!
559+ # ============================================================================
502560 if : (needs.deploy.result == 'success' || github.event.inputs.existing_webapp_url != '') && (github.event_name != 'workflow_dispatch' || github.event.inputs.run_e2e_tests == 'true' || github.event.inputs.run_e2e_tests == null)
503561 needs : [deploy]
504562 uses : ./.github/workflows/test-automation.yml
@@ -507,10 +565,25 @@ jobs:
507565 secrets : inherit
508566
509567 cleanup-deployment :
510- # Cleanup when deploy succeeded and cleanup is not explicitly disabled
511- # Skip if webapp URL is provided (no resources to cleanup) or cleanup checkbox is unchecked
512- if : needs.deploy.result == 'success' && needs.deploy.outputs.RESOURCE_GROUP_NAME != '' && github.event.inputs.existing_webapp_url == '' && (github.event_name != 'workflow_dispatch' || github.event.inputs.cleanup_resources == 'true' || github.event.inputs.cleanup_resources == null)
513- needs : [deploy, e2e-test]
568+ # ============================================================================
569+ # CLEANUP JOB - When does it run?
570+ # ============================================================================
571+ # ✅ RUNS WHEN:
572+ # - Deploy job succeeded (created new resources to clean up)
573+ # - Resource group was created (has RESOURCE_GROUP_NAME output)
574+ # - "Existing WebApp URL" = EMPTY (means we deployed new stuff to clean)
575+ # - "Cleanup deployed resources" = ✅ CHECKED (default) OR not manual trigger
576+ #
577+ # ❌ SKIPPED WHEN:
578+ # - Deploy job failed (nothing to clean up)
579+ # - "Existing WebApp URL" provided (didn't deploy new resources)
580+ # - Manual trigger with "Cleanup deployed resources" = ❌ UNCHECKED
581+ # - No resource group name available (deployment didn't create resources)
582+ #
583+ # SIMPLE RULE: Cleans up newly deployed resources unless you uncheck the box!
584+ # ============================================================================
585+ if : always() && needs.deploy.result == 'success' && needs.deploy.outputs.RESOURCE_GROUP_NAME != '' && github.event.inputs.existing_webapp_url == '' && (github.event_name != 'workflow_dispatch' || github.event.inputs.cleanup_resources == 'true' || github.event.inputs.cleanup_resources == null)
586+ needs : [docker-build, deploy, e2e-test]
514587 runs-on : ubuntu-latest
515588 env :
516589 RESOURCE_GROUP_NAME : ${{ needs.deploy.outputs.RESOURCE_GROUP_NAME }}
@@ -546,22 +619,33 @@ jobs:
546619 set -e
547620 echo "🗑️ Cleaning up Docker images from Azure Container Registry..."
548621
549- if [[ -n "${{ env.IMAGE_TAG }}" && "${{ env.IMAGE_TAG }}" != "latest_waf" ]]; then
550- echo "Deleting Docker images with tag: ${{ env.IMAGE_TAG }}"
622+ # Determine the image tag to delete - check if docker-build job ran
623+ if [[ "${{ needs.docker-build.result }}" == "success" ]]; then
624+ IMAGE_TAG="${{ needs.docker-build.outputs.IMAGE_TAG }}"
625+ echo "Using image tag from docker-build job: $IMAGE_TAG"
626+ else
627+ IMAGE_TAG="${{ needs.deploy.outputs.IMAGE_TAG }}"
628+ echo "Using image tag from deploy job: $IMAGE_TAG"
629+ fi
630+
631+ if [[ -n "$IMAGE_TAG" && "$IMAGE_TAG" != "latest_waf" && "$IMAGE_TAG" != "dev" && "$IMAGE_TAG" != "demo" ]]; then
632+ echo "Deleting Docker images with tag: $IMAGE_TAG"
551633
552634 # Delete the main image
553- echo "Deleting image: ${{ secrets.ACR_TEST_LOGIN_SERVER }}/webapp:${{ env. IMAGE_TAG }} "
635+ echo "Deleting image: ${{ secrets.ACR_TEST_LOGIN_SERVER }}/webapp:$IMAGE_TAG"
554636 az acr repository delete --name $(echo "${{ secrets.ACR_TEST_LOGIN_SERVER }}" | cut -d'.' -f1) \
555- --image webapp:${{ env. IMAGE_TAG }} --yes || echo "Warning: Failed to delete main image or image not found"
637+ --image webapp:$IMAGE_TAG --yes || echo "Warning: Failed to delete main image or image not found"
556638
557- # Delete the image with run number suffix
558- echo "Deleting image: ${{ secrets.ACR_TEST_LOGIN_SERVER }}/webapp:${{ env.IMAGE_TAG }}_${{ github.run_number }}"
559- az acr repository delete --name $(echo "${{ secrets.ACR_TEST_LOGIN_SERVER }}" | cut -d'.' -f1) \
560- --image webapp:${{ env.IMAGE_TAG }}_${{ github.run_number }} --yes || echo "Warning: Failed to delete run-numbered image or image not found"
639+ # Delete the image with run number suffix (only if it was a custom build)
640+ if [[ "${{ needs.docker-build.result }}" == "success" ]]; then
641+ echo "Deleting image: ${{ secrets.ACR_TEST_LOGIN_SERVER }}/webapp:${IMAGE_TAG}_${{ github.run_number }}"
642+ az acr repository delete --name $(echo "${{ secrets.ACR_TEST_LOGIN_SERVER }}" | cut -d'.' -f1) \
643+ --image webapp:${IMAGE_TAG}_${{ github.run_number }} --yes || echo "Warning: Failed to delete run-numbered image or image not found"
644+ fi
561645
562646 echo "✅ Docker images cleanup completed"
563647 else
564- echo "⚠️ Skipping Docker image cleanup (using latest_waf or no custom image tag )"
648+ echo "⚠️ Skipping Docker image cleanup (using standard branch image: $IMAGE_TAG )"
565649 fi
566650
567651 - name : Select Environment
0 commit comments