diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 931694db1b..7915921ba8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -623,9 +623,16 @@ jobs: # Post-publish verification: run the release smoke suite against the exact # beta version that was just published. + # + # The condition must carry an explicit status-check function: without one, + # GitHub attaches an implicit success(), which evaluates the needs chain + # transitively — and publish_beta's chain contains verify_beta_candidate, + # which is skipped on every promote-mode beta. The implicit form silently + # skipped this job on the first promote-mode beta after the candidate + # lane landed. smoke_beta: needs: publish_beta - if: ${{ !inputs.dry_run }} + if: ${{ !cancelled() && needs.publish_beta.result == 'success' && !inputs.dry_run }} uses: ./.github/workflows/release-smoke.yml with: paperclip_version: ${{ needs.publish_beta.outputs.beta_version }} diff --git a/scripts/__tests__/release-verify-workflow.test.mjs b/scripts/__tests__/release-verify-workflow.test.mjs index 354bd81c4c..d32f08ebcc 100644 --- a/scripts/__tests__/release-verify-workflow.test.mjs +++ b/scripts/__tests__/release-verify-workflow.test.mjs @@ -60,6 +60,19 @@ test("candidate-branch betas are validated and fully verified before publish", ( assert.match(releaseWorkflow, /needs\.verify_beta_candidate\.result == 'success'/); }); +test("post-publish beta smoke survives the skipped candidate-verification ancestor", () => { + const releaseWorkflow = readWorkflow("release.yml"); + + // publish_beta's needs chain contains verify_beta_candidate, which is + // skipped on promote-mode betas. An `if:` without a status-check function + // gets an implicit success() that evaluates that chain transitively and + // silently skips the smoke. The condition must stay explicit. + assert.match( + releaseWorkflow, + /smoke_beta:\n\s+needs: publish_beta\n\s+if: \$\{\{ !cancelled\(\) && needs\.publish_beta\.result == 'success' && !inputs\.dry_run \}\}/, + ); +}); + test("every lane's tag push degrades to recovery instructions when rejected", () => { const releaseWorkflow = readWorkflow("release.yml");