diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b2205552a..8fecb01f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -143,11 +143,23 @@ jobs: needs: [ quick-checks ] runs-on: sm-standard-4 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: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" steps: - name: Checkout repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + with: + # This job's token can cancel runs and delete Actions caches. Checkout + # otherwise writes it into .git/config, where a PR's own build.rs or + # proc-macro could read it back out. + persist-credentials: false - name: Setup Rust environment uses: ./.github/actions/setup @@ -278,6 +290,48 @@ jobs: - name: Run rebalance/decommission migration proofs run: ./scripts/check_migration_gate_count.sh + # Early stop. Once this job has failed the PR cannot merge, so the sibling + # lanes are burning runners on a result nobody can act on: on run + # 30674613104 three lanes had already failed while Test and Lint and the + # 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 + if: failure() && github.event_name == 'pull_request' + run: | + echo "## CI early-stop" >> "$GITHUB_STEP_SUMMARY" + echo "Job \`${GITHUB_JOB}\` (Test and Lint) failed; cancelling run ${GITHUB_RUN_ID} to free runners." >> "$GITHUB_STEP_SUMMARY" + 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: >- + failure() && github.event_name == 'pull_request' + && github.event.pull_request.head.repo.full_name == github.repository + continue-on-error: true + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + curl -fsS -X POST \ + -H "Authorization: Bearer ${GH_TOKEN}" \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/cancel" || true + # Dedicated serial lane for the ILM / lifecycle integration tests. These tests # drive the object layer through process-global singletons (the GLOBAL_ENV # ECStore, the global tier-config manager, background-expiry workers) and bind @@ -361,7 +415,13 @@ jobs: runs-on: sm-standard-4 timeout-minutes: 60 strategy: - fail-fast: false + # On a PR, one failing protocol leg is enough to know the PR is not ready, + # so stop the sibling leg instead of paying another ~40 minutes for it. + # Everywhere else (main pushes, the merge queue, the weekly schedule) keep + # the full signal: there we want to know whether swift AND sftp are broken, + # not just whichever failed first. This is the only part of the early-stop + # work that also covers fork PRs, since it needs no token. + fail-fast: ${{ github.event_name == 'pull_request' }} matrix: features: - name: swift