From 572a56a3f6bc1dd05ecb29663400bcfff0135b6d Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 9 Oct 2026 19:07:52 +0530 Subject: [PATCH 1/2] ci(deploy): SKIP_DEV_TESTS repo variable skips the dev test gates A green 'Tests skipped' job records it on the run, and Finalize Deploy still prunes images. main ignores the flag. --- .github/workflows/deploy-backend.yml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-backend.yml b/.github/workflows/deploy-backend.yml index 85158cec..6dd90bae 100644 --- a/.github/workflows/deploy-backend.yml +++ b/.github/workflows/deploy-backend.yml @@ -250,6 +250,9 @@ jobs: smoke-tests: name: Smoke Tests needs: deploy + # Repo variable SKIP_DEV_TESTS=true skips the dev test gates (e.g. while the + # test owner is away). main ignores it: prod deploys are always gated. + if: github.ref_name == 'main' || vars.SKIP_DEV_TESTS != 'true' # No `environment:` here -- GitHub rejects the entire workflow file at # parse time if a `uses:` job declares one. Consequence: this job also # cannot see environment-scoped vars, which is why api_base_url comes @@ -300,6 +303,16 @@ jobs: TEST_PASSWORD_2: ${{ secrets.TEST_PASSWORD_2 }} KEYCLOAK_CLIENT_SECRET: ${{ secrets.KEYCLOAK_CLIENT_SECRET }} + tests-skipped: + name: Tests skipped (SKIP_DEV_TESTS) + needs: deploy + if: github.ref_name != 'main' && vars.SKIP_DEV_TESTS == 'true' + runs-on: ubuntu-latest + steps: + - run: | + echo "::notice::SKIP_DEV_TESTS=true: smoke and functional tests were skipped for this dev deploy." + echo "**SKIP_DEV_TESTS=true**: smoke and functional tests were skipped. Delete the repo variable to turn them back on." >> "$GITHUB_STEP_SUMMARY" + rollback-on-smoke-failure: name: Rollback (smoke tests failed) # `deploy` must be in needs: for needs.deploy.result to resolve here. @@ -357,10 +370,10 @@ jobs: name: Finalize Deploy needs: [deploy, smoke-tests, functional-tests] # Explicit results, not success()/!failure(): on main the functional stage - # is skipped by design. + # is skipped by design, and on dev SKIP_DEV_TESTS skips smoke too. if: >- ${{ !cancelled() && needs.deploy.result == 'success' && - needs.smoke-tests.result == 'success' && + (needs.smoke-tests.result == 'success' || needs.smoke-tests.result == 'skipped') && (needs.functional-tests.result == 'success' || needs.functional-tests.result == 'skipped') }} runs-on: ubuntu-latest environment: ${{ github.ref_name == 'main' && 'production' || 'development' }} From 2f5f92525f1f45e0be88bae89b1f8d6e7f64fb74 Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 9 Oct 2026 19:08:30 +0530 Subject: [PATCH 2/2] ci(deploy): manual rollback button for dev Run workflow on dev with 'rollback' ticked skips the build and runs the existing rollback job, which restores the previously live image. The run stays green when the rollback succeeds. --- .github/workflows/deploy-backend.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-backend.yml b/.github/workflows/deploy-backend.yml index 6dd90bae..ce870818 100644 --- a/.github/workflows/deploy-backend.yml +++ b/.github/workflows/deploy-backend.yml @@ -64,6 +64,11 @@ on: type: boolean required: false default: false + rollback: + description: "Roll dev back to the image that was live before the last deploy (no build, no tests). Ignored on main." + type: boolean + required: false + default: false # Queue overlapping deploys to the SAME environment rather than cancelling (a # cancelled run mid-deploy could leave .deploy/ state and the running @@ -82,6 +87,7 @@ env: jobs: build: name: Build and push image + if: ${{ !inputs.rollback }} runs-on: ubuntu-latest timeout-minutes: 45 permissions: @@ -319,9 +325,11 @@ jobs: needs: [deploy, smoke-tests, functional-tests] # Explicit results rather than failure(), so that a skipped functional stage # on main never reads as a failure. !cancelled() keeps cancellation a no-op. + # Also the manual rollback button: Run workflow on dev with `rollback` ticked. if: >- - ${{ !cancelled() && needs.deploy.result == 'success' && - (needs.smoke-tests.result == 'failure' || needs.functional-tests.result == 'failure') }} + ${{ !cancelled() && ((inputs.rollback && github.ref_name != 'main') || + (needs.deploy.result == 'success' && + (needs.smoke-tests.result == 'failure' || needs.functional-tests.result == 'failure'))) }} runs-on: ubuntu-latest environment: ${{ github.ref_name == 'main' && 'production' || 'development' }} # 50m: must comfortably exceed the Restore previous image step's own @@ -360,6 +368,7 @@ jobs: script: cd "$HOME/${{ env.DEPLOY_PATH }}" && sudo bash scripts/ci-rollback.sh "${{ vars.HEALTH_CHECK_URL || 'http://127.0.0.1:8000/health/' }}" "${{ github.actor }}" - name: Mark this run as failed + if: ${{ !inputs.rollback }} # The mitigation succeeded, but the run must still read RED -- a bad # deploy that silently self-heals is a bad deploy nobody investigates. run: |