diff --git a/.github/workflows/e2e-selfhosted.yml b/.github/workflows/e2e-selfhosted.yml index 3d0ceb73..2ce04ba1 100644 --- a/.github/workflows/e2e-selfhosted.yml +++ b/.github/workflows/e2e-selfhosted.yml @@ -69,6 +69,9 @@ jobs: pull-requests: read outputs: heavy: ${{ steps.filter.outputs.heavy || steps.all.outputs.forced }} + # Narrow GPU-serve gate; forced true off-PR so the merge queue always runs + # the full matrix and its required checks are never starved. + serve: ${{ steps.filter.outputs.serve || steps.all.outputs.forced }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -95,6 +98,33 @@ jobs: - 'install*' - 'docs/keys/**' - '.github/workflows/**' + # The real-GPU serve matrix (e2e-gpu*). Narrower than `heavy`: only + # paths that can change serve BEHAVIOUR or the GPU E2E harness — NOT a + # blanket `**/*.rs`. Compile coverage for every crate already runs on + # ci.yml's always-on build/test lanes, so a dash-only or unrelated- + # crate Rust PR need not fire the heavy serve matrix. Err toward + # inclusion — Cargo.lock, the toolchain, and the workflow itself are + # broad safety nets so a transitive-dep or CI change still runs the + # matrix. (Excluded on purpose: crates/rocm-dash-* — they build into + # `rocm` but cannot change serve behaviour.) + serve: + - 'engines/**' + - 'crates/rocm-core/**' + - 'crates/rocm-engine-protocol/**' + - 'apps/rocm/**' + - 'apps/rocmd/**' + - 'tests/e2e-cucumber/**' + - 'crates/e2e-report/**' + - 'xtask/**' + - '**/*.feature' + - 'scripts/**' + # Root manifest only (NOT `**/Cargo.toml` — that would re-include + # the excluded dash crates): `[workspace.dependencies]` edits here + # can change a serving crate's deps without touching Cargo.lock. + - 'Cargo.toml' + - 'Cargo.lock' + - 'rust-toolchain*' + - '.github/workflows/**' - name: Force full run off pull requests id: all @@ -114,7 +144,7 @@ jobs: && needs.changes.result == 'success' && ( (github.event_name != 'workflow_dispatch' - && needs.changes.outputs.heavy == 'true') + && needs.changes.outputs.serve == 'true') || (github.event_name == 'workflow_dispatch' && (inputs.platform == 'all' || inputs.platform == 'app-dev-gpu')) ) @@ -304,7 +334,7 @@ jobs: && needs.changes.result == 'success' && ( (github.event_name != 'workflow_dispatch' - && needs.changes.outputs.heavy == 'true') + && needs.changes.outputs.serve == 'true') || (github.event_name == 'workflow_dispatch' && (inputs.platform == 'all' || inputs.platform == 'strix-ubuntu')) ) @@ -460,7 +490,7 @@ jobs: && needs.changes.result == 'success' && ( (github.event_name != 'workflow_dispatch' - && needs.changes.outputs.heavy == 'true') + && needs.changes.outputs.serve == 'true') || (github.event_name == 'workflow_dispatch' && (inputs.platform == 'all' || inputs.platform == 'strix-windows')) ) @@ -611,9 +641,13 @@ jobs: - e2e-gpu - e2e-gpu-strix-ubuntu - e2e-gpu-strix-windows + # Gate on `serve`: every lane this report consolidates (the GPU jobs) is now + # serve-gated, so a serve-only change runs them and their report must still be + # produced. On dispatch `serve` is unset, so also run when the trigger was + # manual; `always()` still lets it collect partial/failed tiers. if: >- always() - && (needs.changes.outputs.heavy == 'true' + && (needs.changes.outputs.serve == 'true' || github.event_name == 'workflow_dispatch') steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 diff --git a/docs/ci-hardware-testing.md b/docs/ci-hardware-testing.md index 6b92f364..8209e248 100644 --- a/docs/ci-hardware-testing.md +++ b/docs/ci-hardware-testing.md @@ -59,15 +59,22 @@ runs — by scenario id into one HTML report and GitHub step summary. The GPU jobs (in `e2e-selfhosted.yml`) run automatically on `push`, `pull_request`, and `merge_group` when the workflow's own `changes` job's -`heavy` path filter is `true` (the change touches code that can affect runtime -behavior, not just docs or unrelated files). Unlike the pre-split layout they do -**not** gate on the hosted `build-and-test` job — cross-workflow `needs` is not -possible, so each GPU job builds the `rocm` binary itself as its first real step -(a broken build fails that job fast and non-fatally). `ci.yml`'s required -`build-and-test` and mock `e2e` remain the authoritative pre-merge build gate. +`serve` path filter is `true`. `serve` is narrower than `heavy`: it matches only +paths that can change serve *behaviour* or the GPU E2E harness (the engines, the +serve code path in `apps/rocm`/`apps/rocmd`, `rocm-core`, the e2e-cucumber crate, +plus broad-dependency safety nets), **not** a blanket `**/*.rs`. So a Rust change +that cannot affect serving — e.g. a dashboard-only or unrelated-crate PR — skips +the heavy GPU matrix, while compile coverage for every crate still runs on +`ci.yml`'s always-on build/test lanes. Off `pull_request` (push/merge_group) the +filter is forced `true`, so the full matrix always runs there. Unlike the +pre-split layout the GPU jobs do **not** gate on the hosted `build-and-test` job +— cross-workflow `needs` is not possible, so each GPU job builds the `rocm` +binary itself as its first real step (a broken build fails that job fast and +non-fatally). `ci.yml`'s required `build-and-test` and mock `e2e` remain the +authoritative pre-merge build gate. They can also be triggered manually via `e2e-selfhosted.yml`'s -`workflow_dispatch`, independent of the `heavy` gate, with these inputs: +`workflow_dispatch`, independent of the `serve` gate, with these inputs: - `platform` (choice: `all`, `app-dev-gpu`, `strix-ubuntu`, `strix-windows`) — which self-hosted job(s) to run. `app-dev-gpu` maps to `e2e-gpu`,