ci(rust): gate jobs with if: instead of a workflow-level paths filter (#2580)

Prerequisite for making `parity` a required status check on main.

A workflow skipped by a top-level `paths:` filter never creates its check runs
at all, so a required check sourced from it sits pending forever on any PR that
misses those paths and the PR can never merge. A job skipped by `if:` still
creates a check run, reports skipped, and GitHub counts skipped as success.

Moves the seven path patterns verbatim from the `on:` block into a new
`rust-changes` job (dorny/paths-filter) and gates all five existing jobs on
`needs.rust-changes.outputs.rust == 'true'`. Named rust-changes, not changes,
to avoid colliding with ci.yml's existing check. Job `name:` fields unchanged,
so no check names move. schedule/workflow_dispatch force rust=true, preserving
the nightly full-suite behaviour.

CI spend unchanged: same jobs on the same PRs, plus a ~15s gate job on PRs that
previously skipped the workflow outright.

Verified by the PR itself — it edits rust.yml, which is in the path list, so it
exercises the rust=true branch: rust-changes and parity both SUCCESS.

Follow-up before adding parity to required checks: confirm a non-Rust PR reports
parity as skipped rather than absent. ci.yml has the mirror-image problem
(paths-ignore on docs) and is deliberately not addressed here.
This commit is contained in:
Tejas Chopra 2026-07-27 07:34:00 -07:00 committed by GitHub
parent 85e8699451
commit e562d007d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,25 +1,18 @@
name: rust
# Path gating lives in the `rust-changes` job below, NOT in a workflow-level
# `paths:` filter. The distinction matters for branch protection: a workflow
# skipped by `paths:` never creates its check runs at all, so a required status
# check from it sits pending forever on any PR that misses those paths, and the
# PR can never merge. A job skipped by `if:` still creates a check run, reports
# `skipped`, and GitHub counts skipped as success for a required check.
#
# Same coverage as the old filter — the path list moved verbatim into
# `rust-changes` — but `parity` is now safe to mark required on `main`.
on:
push:
branches: [ main, rust-rewrite ]
paths:
- 'crates/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'rust-toolchain.toml'
- 'tests/parity/**'
- 'Makefile'
- '.github/workflows/rust.yml'
pull_request:
paths:
- 'crates/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'rust-toolchain.toml'
- 'tests/parity/**'
- 'Makefile'
- '.github/workflows/rust.yml'
schedule:
# Nightly parity run at 07:17 UTC (weekdays only). Redundant with the
# per-PR gate below, but catches drift from toolchain/dependency updates
@ -38,8 +31,43 @@ permissions:
contents: read
jobs:
# Carries the path list the workflow-level `paths:` filter used to hold. Named
# `rust-changes` rather than `changes` so it does not collide with ci.yml's
# `changes` check.
rust-changes:
name: rust-changes
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
rust: ${{ steps.decide.outputs.rust }}
steps:
- uses: actions/checkout@v7
- uses: dorny/paths-filter@v4
id: filter
if: github.event_name == 'pull_request' || github.event_name == 'push'
with:
filters: |
rust:
- 'crates/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'rust-toolchain.toml'
- 'tests/parity/**'
- 'Makefile'
- '.github/workflows/rust.yml'
- id: decide
# `schedule` and `workflow_dispatch` have no diff to filter against, so
# they run the full suite — that is the point of the nightly job.
run: |
case "${{ github.event_name }}" in
pull_request|push) echo "rust=${{ steps.filter.outputs.rust }}" >> "$GITHUB_OUTPUT" ;;
*) echo "rust=true" >> "$GITHUB_OUTPUT" ;;
esac
test:
name: test (ubuntu)
needs: rust-changes
if: needs.rust-changes.outputs.rust == 'true'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
@ -78,6 +106,8 @@ jobs:
simulator-e2e:
name: simulator e2e (${{ matrix.os }})
needs: rust-changes
if: needs.rust-changes.outputs.rust == 'true'
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
@ -97,6 +127,8 @@ jobs:
wheels:
name: wheels (${{ matrix.target }})
needs: rust-changes
if: needs.rust-changes.outputs.rust == 'true'
runs-on: ${{ matrix.os }}
timeout-minutes: 45
strategy:
@ -137,6 +169,8 @@ jobs:
audit:
name: audit
needs: rust-changes
if: needs.rust-changes.outputs.rust == 'true'
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
@ -168,6 +202,8 @@ jobs:
# drifting away from the fixtures; that needs re-recording, not this job.
parity:
name: parity
needs: rust-changes
if: needs.rust-changes.outputs.rust == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7