From 2db67e64acd617d33ed62e7c53e2a3e0a3ea3c24 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Wed, 4 Mar 2026 15:13:13 +0100 Subject: [PATCH 1/6] chore(ci): make Verify E2E Passed a required check on main The verify-e2e job now runs on all PRs to main (not just release-please PRs). On non-release PRs it passes instantly; on release PRs it performs the full E2E verification. This allows it to be added as a required status check without blocking feature PRs. Co-Authored-By: Claude Opus 4.6 Entire-Checkpoint: a6408bad87ba --- .github/workflows/release-gate.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-gate.yml b/.github/workflows/release-gate.yml index c806f29283..8a03ea3ce9 100644 --- a/.github/workflows/release-gate.yml +++ b/.github/workflows/release-gate.yml @@ -73,11 +73,16 @@ jobs: verify-e2e: name: Verify E2E Passed needs: detect-changes - if: startsWith(github.head_ref, 'release-please--') + if: '!cancelled()' runs-on: ubuntu-latest timeout-minutes: 40 steps: + - name: Skip (not a release PR) + if: "!startsWith(github.head_ref, 'release-please--')" + run: echo "Not a release PR — E2E gate not applicable" + - name: Wait for Web E2E and verify it passed + if: startsWith(github.head_ref, 'release-please--') env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | @@ -117,7 +122,7 @@ jobs: echo "Web E2E: passed" - name: Verify Desktop E2E (if desktop changed) - if: needs.detect-changes.outputs.desktop == 'true' + if: startsWith(github.head_ref, 'release-please--') && needs.detect-changes.outputs.desktop == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PREV_TAG: ${{ needs.detect-changes.outputs.prev_tag }} @@ -256,8 +261,9 @@ jobs: echo "Desktop E2E: passed (run ${LATEST_IN_RANGE_RUN_ID} at ${LATEST_IN_RANGE_SHA} with tests executed)" - name: Skip Desktop E2E (no desktop changes) - if: needs.detect-changes.outputs.desktop != 'true' + if: startsWith(github.head_ref, 'release-please--') && needs.detect-changes.outputs.desktop != 'true' run: echo "No desktop-related changes since last release — skipping Desktop E2E gate" - name: Summary + if: startsWith(github.head_ref, 'release-please--') run: echo "All E2E gates passed — release is safe to merge." From a68178b043949b490d4528243ea32adc084f3db7 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Wed, 4 Mar 2026 15:25:28 +0100 Subject: [PATCH 2/6] fix(ci): ensure verify-e2e never skips on non-release PRs Make detect-changes run on all PRs (short-circuits for non-release PRs) so verify-e2e's needs dependency is always satisfied. This prevents the required check from being skipped and silently passing branch protection. Co-Authored-By: Claude Opus 4.6 Entire-Checkpoint: c08fdccd40bd --- .github/workflows/release-gate.yml | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release-gate.yml b/.github/workflows/release-gate.yml index 8a03ea3ce9..cff719f031 100644 --- a/.github/workflows/release-gate.yml +++ b/.github/workflows/release-gate.yml @@ -8,17 +8,29 @@ on: jobs: detect-changes: name: Detect Changes Since Last Release - if: startsWith(github.head_ref, 'release-please--') runs-on: ubuntu-latest outputs: desktop: ${{ steps.check.outputs.desktop }} prev_tag: ${{ steps.check.outputs.prev_tag }} + is_release: ${{ steps.release-check.outputs.is_release }} steps: + - name: Check if release PR + id: release-check + run: | + if [[ "${{ github.head_ref }}" == release-please--* ]]; then + echo "is_release=true" >> "$GITHUB_OUTPUT" + else + echo "is_release=false" >> "$GITHUB_OUTPUT" + echo "Not a release PR — skipping change detection" + fi + - uses: actions/checkout@v4 + if: steps.release-check.outputs.is_release == 'true' with: fetch-depth: 0 - name: Check for desktop-related changes since last release + if: steps.release-check.outputs.is_release == 'true' id: check run: | # Find the latest non-staging release tag @@ -73,16 +85,15 @@ jobs: verify-e2e: name: Verify E2E Passed needs: detect-changes - if: '!cancelled()' runs-on: ubuntu-latest timeout-minutes: 40 steps: - name: Skip (not a release PR) - if: "!startsWith(github.head_ref, 'release-please--')" + if: needs.detect-changes.outputs.is_release != 'true' run: echo "Not a release PR — E2E gate not applicable" - name: Wait for Web E2E and verify it passed - if: startsWith(github.head_ref, 'release-please--') + if: needs.detect-changes.outputs.is_release == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | @@ -122,7 +133,7 @@ jobs: echo "Web E2E: passed" - name: Verify Desktop E2E (if desktop changed) - if: startsWith(github.head_ref, 'release-please--') && needs.detect-changes.outputs.desktop == 'true' + if: needs.detect-changes.outputs.is_release == 'true' && needs.detect-changes.outputs.desktop == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PREV_TAG: ${{ needs.detect-changes.outputs.prev_tag }} @@ -261,9 +272,9 @@ jobs: echo "Desktop E2E: passed (run ${LATEST_IN_RANGE_RUN_ID} at ${LATEST_IN_RANGE_SHA} with tests executed)" - name: Skip Desktop E2E (no desktop changes) - if: startsWith(github.head_ref, 'release-please--') && needs.detect-changes.outputs.desktop != 'true' + if: needs.detect-changes.outputs.is_release == 'true' && needs.detect-changes.outputs.desktop != 'true' run: echo "No desktop-related changes since last release — skipping Desktop E2E gate" - name: Summary - if: startsWith(github.head_ref, 'release-please--') + if: needs.detect-changes.outputs.is_release == 'true' run: echo "All E2E gates passed — release is safe to merge." From 3c9ecc61118dad6030317a89ad4373eb7347c611 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Wed, 4 Mar 2026 15:35:43 +0100 Subject: [PATCH 3/6] fix(ci): add default outputs and detect-changes failure guard - Set explicit default outputs (desktop=false, prev_tag=) for non-release PRs so downstream outputs are always defined - Add if: !cancelled() to verify-e2e so it runs even if detect-changes fails unexpectedly - Add explicit failure step for release PRs when detect-changes didn't succeed Co-Authored-By: Claude Opus 4.6 Entire-Checkpoint: b63abff3adfd --- .github/workflows/release-gate.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-gate.yml b/.github/workflows/release-gate.yml index cff719f031..ddad5cafbc 100644 --- a/.github/workflows/release-gate.yml +++ b/.github/workflows/release-gate.yml @@ -10,8 +10,8 @@ jobs: name: Detect Changes Since Last Release runs-on: ubuntu-latest outputs: - desktop: ${{ steps.check.outputs.desktop }} - prev_tag: ${{ steps.check.outputs.prev_tag }} + desktop: ${{ steps.check.outputs.desktop || steps.release-check.outputs.desktop }} + prev_tag: ${{ steps.check.outputs.prev_tag || steps.release-check.outputs.prev_tag }} is_release: ${{ steps.release-check.outputs.is_release }} steps: - name: Check if release PR @@ -21,6 +21,8 @@ jobs: echo "is_release=true" >> "$GITHUB_OUTPUT" else echo "is_release=false" >> "$GITHUB_OUTPUT" + echo "desktop=false" >> "$GITHUB_OUTPUT" + echo "prev_tag=" >> "$GITHUB_OUTPUT" echo "Not a release PR — skipping change detection" fi @@ -85,9 +87,16 @@ jobs: verify-e2e: name: Verify E2E Passed needs: detect-changes + if: '!cancelled()' runs-on: ubuntu-latest timeout-minutes: 40 steps: + - name: Fail if detect-changes did not complete (release PR) + if: needs.detect-changes.outputs.is_release == 'true' && needs.detect-changes.result != 'success' + run: | + echo "::error::detect-changes must succeed before E2E verification." + exit 1 + - name: Skip (not a release PR) if: needs.detect-changes.outputs.is_release != 'true' run: echo "Not a release PR — E2E gate not applicable" From a6227542d69ff69abe54588242b79f5b02d1f412 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Wed, 4 Mar 2026 15:46:01 +0100 Subject: [PATCH 4/6] fix(ci): use always() && !cancelled() for verify-e2e job condition Explicit always() ensures the job runs even when detect-changes is skipped/failed, removing ambiguity about !cancelled() alone. Co-Authored-By: Claude Opus 4.6 Entire-Checkpoint: 61cdbe0daf6d --- .github/workflows/release-gate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-gate.yml b/.github/workflows/release-gate.yml index ddad5cafbc..ef873a686f 100644 --- a/.github/workflows/release-gate.yml +++ b/.github/workflows/release-gate.yml @@ -87,7 +87,7 @@ jobs: verify-e2e: name: Verify E2E Passed needs: detect-changes - if: '!cancelled()' + if: ${{ always() && !cancelled() }} runs-on: ubuntu-latest timeout-minutes: 40 steps: From 1cb73d14358383a15dec0a7340d2a8fa768f7b4a Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Wed, 4 Mar 2026 16:00:05 +0100 Subject: [PATCH 5/6] fix(ci): use github.head_ref for release PR detection in verify-e2e Detect release PRs via startsWith(github.head_ref) instead of needs.detect-changes.outputs.is_release. This fails closed if detect-changes crashes before producing outputs. Co-Authored-By: Claude Opus 4.6 Entire-Checkpoint: 85ab87bf85f5 --- .github/workflows/release-gate.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release-gate.yml b/.github/workflows/release-gate.yml index ef873a686f..4e93de63ac 100644 --- a/.github/workflows/release-gate.yml +++ b/.github/workflows/release-gate.yml @@ -92,17 +92,17 @@ jobs: timeout-minutes: 40 steps: - name: Fail if detect-changes did not complete (release PR) - if: needs.detect-changes.outputs.is_release == 'true' && needs.detect-changes.result != 'success' + if: startsWith(github.head_ref, 'release-please--') && needs.detect-changes.result != 'success' run: | echo "::error::detect-changes must succeed before E2E verification." exit 1 - name: Skip (not a release PR) - if: needs.detect-changes.outputs.is_release != 'true' + if: "!startsWith(github.head_ref, 'release-please--')" run: echo "Not a release PR — E2E gate not applicable" - name: Wait for Web E2E and verify it passed - if: needs.detect-changes.outputs.is_release == 'true' + if: startsWith(github.head_ref, 'release-please--') env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | @@ -142,7 +142,7 @@ jobs: echo "Web E2E: passed" - name: Verify Desktop E2E (if desktop changed) - if: needs.detect-changes.outputs.is_release == 'true' && needs.detect-changes.outputs.desktop == 'true' + if: startsWith(github.head_ref, 'release-please--') && needs.detect-changes.outputs.desktop == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PREV_TAG: ${{ needs.detect-changes.outputs.prev_tag }} @@ -281,9 +281,9 @@ jobs: echo "Desktop E2E: passed (run ${LATEST_IN_RANGE_RUN_ID} at ${LATEST_IN_RANGE_SHA} with tests executed)" - name: Skip Desktop E2E (no desktop changes) - if: needs.detect-changes.outputs.is_release == 'true' && needs.detect-changes.outputs.desktop != 'true' + if: startsWith(github.head_ref, 'release-please--') && needs.detect-changes.outputs.desktop != 'true' run: echo "No desktop-related changes since last release — skipping Desktop E2E gate" - name: Summary - if: needs.detect-changes.outputs.is_release == 'true' + if: startsWith(github.head_ref, 'release-please--') run: echo "All E2E gates passed — release is safe to merge." From f53e66370e5e609e1b5ae5c23e91960c84079dc6 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Wed, 4 Mar 2026 16:07:02 +0100 Subject: [PATCH 6/6] fix(ci): pass github.head_ref via env to avoid script injection github.head_ref is user-controlled (PR branch name). Pass it through an environment variable instead of interpolating directly in the run script. Co-Authored-By: Claude Opus 4.6 Entire-Checkpoint: bf29201a8eff --- .github/workflows/release-gate.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-gate.yml b/.github/workflows/release-gate.yml index 4e93de63ac..15b83e40aa 100644 --- a/.github/workflows/release-gate.yml +++ b/.github/workflows/release-gate.yml @@ -16,8 +16,10 @@ jobs: steps: - name: Check if release PR id: release-check + env: + HEAD_REF: ${{ github.head_ref }} run: | - if [[ "${{ github.head_ref }}" == release-please--* ]]; then + if [[ "$HEAD_REF" == release-please--* ]]; then echo "is_release=true" >> "$GITHUB_OUTPUT" else echo "is_release=false" >> "$GITHUB_OUTPUT"