ci: preserve failure verdict before early stop (#6552)

This commit is contained in:
Zhengchao An
2026-08-25 04:33:16 +08:00
committed by GitHub
parent bb9491f782
commit 3da319624f
2 changed files with 31 additions and 44 deletions
+29 -42
View File
@@ -181,22 +181,14 @@ jobs:
needs: [ quick-checks ] needs: [ quick-checks ]
runs-on: sm-standard-4 runs-on: sm-standard-4
timeout-minutes: 90 timeout-minutes: 90
# Both lines are required. Job-level `permissions` replaces the workflow
# block rather than merging with it, so declaring only `actions: write`
# would drop `contents: read` and break this job's checkout and the
# repo-token the setup action hands to setup-protoc.
permissions:
contents: read
actions: write
env: env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with: with:
# This job's token can cancel runs and delete Actions caches. Checkout # Checkout otherwise writes the token into .git/config, where a PR's
# otherwise writes it into .git/config, where a PR's own build.rs or # own build.rs or proc-macro could read it back out.
# proc-macro could read it back out.
persist-credentials: false persist-credentials: false
- name: Setup Rust environment - name: Setup Rust environment
@@ -347,41 +339,36 @@ jobs:
- name: Run rebalance/decommission migration proofs - name: Run rebalance/decommission migration proofs
run: ./scripts/check_migration_gate_count.sh run: ./scripts/check_migration_gate_count.sh
# Early stop. Once this job has failed the PR cannot merge, so the sibling # Record the reason before this job completes as FAILURE. A separate
# lanes are burning runners on a result nobody can act on: on run # dependent job cancels sibling lanes only after GitHub has preserved this
# 30674613104 three lanes had already failed while Test and Lint and the # required check's failure verdict.
# rio-v2 variant kept going past 70 minutes.
#
# Only this job may cancel. The lanes that are NOT required checks
# (protocols, ILM, e2e, s3-tests) must never hold that power: a flake in
# one of them would turn the required "Test and Lint" into `cancelled`,
# which blocks the merge. Today a maintainer can merge with sftp red, and
# that has to stay true.
#
# These steps run last so the `if: always()` artifact upload above still
# captures logs and diagnostics before the run goes away.
- name: Annotate early-stop reason - name: Annotate early-stop reason
if: failure() && github.event_name == 'pull_request'
run: |
{
echo "## CI early-stop"
echo "Job \`${GITHUB_JOB}\` (Test and Lint) failed; cancelling run ${GITHUB_RUN_ID} to free runners."
echo "Sibling jobs showing **cancelled** were stopped by this job, not by their own failure."
} >> "$GITHUB_STEP_SUMMARY"
# curl rather than `gh`: every existing `gh` call in this repo runs on
# ubuntu-latest, and the sm-standard-* images are custom and trimmed (they
# ship no C toolchain, see the e2e job below), so `gh` is not known to
# exist here.
#
# Fork PRs are excluded explicitly instead of relying on the error path:
# their GITHUB_TOKEN is forced read-only and job-level permissions cannot
# raise it, so the call would always 403. Skipping keeps their logs clean.
- name: Cancel run on failure (same-repo PR only)
if: >- if: >-
failure() && github.event_name == 'pull_request' failure() && github.event_name == 'pull_request'
&& github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.head.repo.full_name == github.repository
continue-on-error: true run: |
{
echo "## CI early-stop"
echo "Job \`${GITHUB_JOB}\` (Test and Lint) failed; a follow-up job will cancel sibling lanes to free runners."
echo "Sibling jobs showing **cancelled** were stopped by the early-stop follow-up, not by their own failure."
} >> "$GITHUB_STEP_SUMMARY"
# Preserve the required Test and Lint FAILURE verdict before stopping sibling
# lanes. Cancelling from inside test-and-lint changed its own conclusion to
# CANCELLED and hid the actionable failure in the PR checks UI.
cancel-after-test-and-lint-failure:
name: Cancel siblings after Test and Lint failure
if: >-
failure() && needs.test-and-lint.result == 'failure'
&& github.event_name == 'pull_request'
&& github.event.pull_request.head.repo.full_name == github.repository
needs: [ test-and-lint ]
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
actions: write
steps:
- name: Cancel remaining jobs
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: | run: |
@@ -389,7 +376,7 @@ jobs:
-H "Authorization: Bearer ${GH_TOKEN}" \ -H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \ -H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \ -H "X-GitHub-Api-Version: 2022-11-28" \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/cancel" || true "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/cancel"
# Dedicated serial lane for the ILM / lifecycle integration tests. These tests # Dedicated serial lane for the ILM / lifecycle integration tests. These tests
# drive the object layer through process-global singletons (the GLOBAL_ENV # drive the object layer through process-global singletons (the GLOBAL_ENV
@@ -6,8 +6,8 @@
# where it stays for the rest of the job. On this repository that matters more # where it stays for the rest of the job. On this repository that matters more
# than usual: pull_request jobs run on self-hosted runners and execute the PR's # than usual: pull_request jobs run on self-hosted runners and execute the PR's
# own build.rs, proc-macros and tests, any of which can read that file. The # own build.rs, proc-macros and tests, any of which can read that file. The
# Test and Lint job additionally holds actions: write, so its token can cancel # post-failure cancellation job holds actions: write but never checks out
# runs and delete the Actions caches the whole pipeline depends on. # repository code, so untrusted build scripts cannot read that token from Git.
# #
# A checkout is exempt only when the token IS the credential the job needs — # A checkout is exempt only when the token IS the credential the job needs —
# helm-package pushes to rustfs/helm with it. Mark those with a comment # helm-package pushes to rustfs/helm with it. Mark those with a comment