From d73bb963f8963b7456c94977fb7d8cf4f5bb5087 Mon Sep 17 00:00:00 2001 From: Payton McIntosh Date: Sat, 24 Oct 2026 12:54:14 +0100 Subject: [PATCH] Place Dependabot pull requests on GitHub-hosted runners GitHub-hosted minutes cost nothing on a public repository, so a pull request authored by dependabot[bot] now falls back to ubuntu-latest like a fork's. The expression tests pull_request.user.login rather than github.actor, which changes when someone re-runs or pushes. A push to main has no pull_request payload and stays on Ubicloud, so main still writes the cache. Every Ubicloud lane that had the fork fallback gains the Dependabot arm (workflow `runs-on` lines, plus the `runner:`/`os:` matrix keys in `ci.yml`, `rust-toy-app.yml` and the `test-install-*` and `test-setup-rust-mold` workflows). The two `test-ubicloud-sccache-proxy.yml` jobs carry a `dependabot-runner: exempt` comment: they prove the Ubicloud cache proxy, which a hosted runner cannot show; `test-upload-codescene-coverage.yml` and the push/dispatch-only lanes were already exempt. `test_runner_placement.py` and `test_coverage_watchdog_lane.py` read only the exact combined condition. The 35 mutations cover every armed workflow file plus the contract regex. Heavy-repository note: the repository's own Dependabot PRs (shared-actions pin bumps) are the ones that run here, so the first natural one is the measured run. --- .github/workflows/ci.yml | 14 ++++++++--- .github/workflows/rust-toy-app.yml | 12 ++++++--- .github/workflows/test-coverage-watchdog.yml | 6 ++++- .github/workflows/test-cv005-contracts.yml | 6 ++++- .../test-determine-release-modes.yml | 6 ++++- .../workflows/test-export-cargo-metadata.yml | 9 +++++-- .github/workflows/test-install-makeutil.yml | 15 ++++++++--- .github/workflows/test-install-mdtablefix.yml | 3 ++- .github/workflows/test-install-tool.yml | 12 ++++++--- .github/workflows/test-install-whitaker.yml | 9 +++++-- .github/workflows/test-install-yaml-lint.yml | 11 ++++++-- ...test-rust-build-release-root-discovery.yml | 6 ++++- .../workflows/test-setup-rust-clang-lld.yml | 6 ++++- .github/workflows/test-setup-rust-mold.yml | 3 ++- .github/workflows/test-setup-rust-sccache.yml | 12 ++++++--- .../test-stage-release-artefacts.yml | 15 ++++++++--- .../workflows/test-ubicloud-sccache-proxy.yml | 4 +++ .../workflows/test-upload-release-assets.yml | 9 +++++-- docs/developers-guide.md | 25 +++++++++++++------ .../workflows/test_coverage_watchdog_lane.py | 4 ++- tests/workflows/test_runner_placement.py | 4 ++- 21 files changed, 145 insertions(+), 46 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c7eb2de2..5a6feaa8b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,8 @@ jobs: include: - platform: linux os: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} - platform: macos os: macos-15 @@ -172,8 +173,12 @@ jobs: timeout-minutes: 30 permissions: contents: read + # Dependabot pull requests run on the hosted pool too (free on a public + # repository). The author is tested, not the actor, which changes when + # someone else pushes to the branch. runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} env: MAKEUTIL_REVISION: '7ab9b7b73a8f50ef1d3df548c7def7b94db35ba2' @@ -288,7 +293,10 @@ jobs: timeout-minutes: 30 permissions: contents: read - runs-on: ${{ github.event.pull_request.head.repo.fork && 'ubuntu-latest' || 'ubicloud-standard-2' }} + runs-on: >- + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') + && 'ubuntu-latest' || 'ubicloud-standard-2' }} # Job-level because two steps read it: `install-tool` pins the archive to # this version, and the lane assertion below names it. A step-level `env:` # is visible only to the step that declares it. diff --git a/.github/workflows/rust-toy-app.yml b/.github/workflows/rust-toy-app.yml index 7a9969ca2..f699e9b69 100644 --- a/.github/workflows/rust-toy-app.yml +++ b/.github/workflows/rust-toy-app.yml @@ -18,17 +18,20 @@ jobs: include: - platform: linux runner: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} target: x86_64-unknown-linux-gnu - platform: linux runner: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} target: aarch64-unknown-linux-gnu - platform: linux runner: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} target: x86_64-unknown-illumos - platform: macos @@ -39,7 +42,8 @@ jobs: target: aarch64-apple-darwin - platform: linux runner: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} target: x86_64-unknown-freebsd # - platform: linux diff --git a/.github/workflows/test-coverage-watchdog.yml b/.github/workflows/test-coverage-watchdog.yml index 4c2f46062..5d87bd6f7 100644 --- a/.github/workflows/test-coverage-watchdog.yml +++ b/.github/workflows/test-coverage-watchdog.yml @@ -40,8 +40,12 @@ jobs: # regardless, so a green run is not evidence that it parsed as one # line. The workflow contract reads the raw value and refuses one # carrying a break. + # Dependabot pull requests run on the hosted pool too (free on a public + # repository). The author is tested, not the actor, which changes when + # someone else pushes to the branch. runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} steps: - name: Checkout repository diff --git a/.github/workflows/test-cv005-contracts.yml b/.github/workflows/test-cv005-contracts.yml index 9ee8f029e..9ae691e2d 100644 --- a/.github/workflows/test-cv005-contracts.yml +++ b/.github/workflows/test-cv005-contracts.yml @@ -25,8 +25,12 @@ jobs: # The library's own lane is short and needs nothing from a hosted image, so # it runs on the smallest Ubicloud runner; a fork's pull request cannot # obtain one and falls back to a hosted runner. + # Dependabot pull requests run on the hosted pool too (free on a public + # repository). The author is tested, not the actor, which changes when + # someone else pushes to the branch. runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 20 strategy: diff --git a/.github/workflows/test-determine-release-modes.yml b/.github/workflows/test-determine-release-modes.yml index b9c63481b..a691b608b 100644 --- a/.github/workflows/test-determine-release-modes.yml +++ b/.github/workflows/test-determine-release-modes.yml @@ -15,8 +15,12 @@ on: jobs: test-determine-modes: + # Dependabot pull requests run on the hosted pool too (free on a public + # repository). The author is tested, not the actor, which changes when + # someone else pushes to the branch. runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 10 permissions: diff --git a/.github/workflows/test-export-cargo-metadata.yml b/.github/workflows/test-export-cargo-metadata.yml index ad82c44f8..f435106fc 100644 --- a/.github/workflows/test-export-cargo-metadata.yml +++ b/.github/workflows/test-export-cargo-metadata.yml @@ -5,8 +5,12 @@ on: jobs: test-export-metadata: + # Dependabot pull requests run on the hosted pool too (free on a public + # repository). The author is tested, not the actor, which changes when + # someone else pushes to the branch. runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 10 permissions: @@ -33,7 +37,8 @@ jobs: test-export-metadata-env-overrides: runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 10 permissions: diff --git a/.github/workflows/test-install-makeutil.yml b/.github/workflows/test-install-makeutil.yml index b6d221375..aa22410cd 100644 --- a/.github/workflows/test-install-makeutil.yml +++ b/.github/workflows/test-install-makeutil.yml @@ -30,7 +30,8 @@ jobs: include: - platform: linux-x86_64 runner: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} - platform: linux-aarch64 runner: ubuntu-24.04-arm @@ -103,7 +104,8 @@ jobs: include: - platform: linux-x86_64 runner: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} - platform: linux-aarch64 runner: ubuntu-24.04-arm @@ -135,8 +137,12 @@ jobs: # replaces only what the download is checked against, not what is # fetched, so this proves the verification is not merely decorative. name: Fail closed on a tampered digest + # Dependabot pull requests run on the hosted pool too (free on a public + # repository). The author is tested, not the actor, which changes when + # someone else pushes to the branch. runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 15 steps: @@ -171,7 +177,8 @@ jobs: # an unpinned version is refused before anything is downloaded. name: Fail closed on an unpinned version runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 10 steps: diff --git a/.github/workflows/test-install-mdtablefix.yml b/.github/workflows/test-install-mdtablefix.yml index 3e6a887e6..15c0e22fc 100644 --- a/.github/workflows/test-install-mdtablefix.yml +++ b/.github/workflows/test-install-mdtablefix.yml @@ -32,7 +32,8 @@ jobs: include: - platform: linux runner: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} - platform: macos runner: macos-15 diff --git a/.github/workflows/test-install-tool.yml b/.github/workflows/test-install-tool.yml index cd07dad28..c09b20b5c 100644 --- a/.github/workflows/test-install-tool.yml +++ b/.github/workflows/test-install-tool.yml @@ -34,7 +34,8 @@ jobs: # A tarball with the binary under one directory, GNU tar. - platform: linux runner: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} tool: sccache version: 0.17.0 @@ -46,7 +47,8 @@ jobs: # repository depends on every pull request. - platform: linux runner: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} tool: merman-cli version: 0.7.0 @@ -139,8 +141,12 @@ jobs: refuses-an-unpinned-version: # A version outside the manifest must fail closed rather than reach for # whatever is newest, which is the failure mode this action exists to end. + # Dependabot pull requests run on the hosted pool too (free on a public + # repository). The author is tested, not the actor, which changes when + # someone else pushes to the branch. runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 10 steps: diff --git a/.github/workflows/test-install-whitaker.yml b/.github/workflows/test-install-whitaker.yml index f92ae984e..3d23a811f 100644 --- a/.github/workflows/test-install-whitaker.yml +++ b/.github/workflows/test-install-whitaker.yml @@ -16,8 +16,12 @@ env: jobs: install-whitaker: + # Dependabot pull requests run on the hosted pool too (free on a public + # repository). The author is tested, not the actor, which changes when + # someone else pushes to the branch. runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 15 steps: @@ -99,7 +103,8 @@ jobs: install-whitaker-failure: runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 10 steps: diff --git a/.github/workflows/test-install-yaml-lint.yml b/.github/workflows/test-install-yaml-lint.yml index 99a211f46..c03a4eb76 100644 --- a/.github/workflows/test-install-yaml-lint.yml +++ b/.github/workflows/test-install-yaml-lint.yml @@ -34,7 +34,8 @@ jobs: include: - platform: linux runner: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} - platform: linux-arm64 runner: ubuntu-24.04-arm @@ -127,7 +128,13 @@ jobs: # embedded in the action, so an archive no runner here exercises cannot # carry a wrong digest or a wrong asset name unnoticed. name: Verify every configured release digest - runs-on: ${{ github.event.pull_request.head.repo.fork && 'ubuntu-latest' || 'ubicloud-standard-2' }} + # Dependabot pull requests run on the hosted pool too (free on a public + # repository). The author is tested, not the actor, which changes when + # someone else pushes to the branch. + runs-on: >- + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') + && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 15 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 diff --git a/.github/workflows/test-rust-build-release-root-discovery.yml b/.github/workflows/test-rust-build-release-root-discovery.yml index 4d95abb1f..18cbc0853 100644 --- a/.github/workflows/test-rust-build-release-root-discovery.yml +++ b/.github/workflows/test-rust-build-release-root-discovery.yml @@ -5,8 +5,12 @@ on: jobs: test-action-setup-root: + # Dependabot pull requests run on the hosted pool too (free on a public + # repository). The author is tested, not the actor, which changes when + # someone else pushes to the branch. runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 10 permissions: diff --git a/.github/workflows/test-setup-rust-clang-lld.yml b/.github/workflows/test-setup-rust-clang-lld.yml index 8a8df2dbb..0ff0f4724 100644 --- a/.github/workflows/test-setup-rust-clang-lld.yml +++ b/.github/workflows/test-setup-rust-clang-lld.yml @@ -25,8 +25,12 @@ concurrency: jobs: installs-on-linux: name: Install clang and lld (linux) + # Dependabot pull requests run on the hosted pool too (free on a public + # repository). The author is tested, not the actor, which changes when + # someone else pushes to the branch. runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 15 steps: diff --git a/.github/workflows/test-setup-rust-mold.yml b/.github/workflows/test-setup-rust-mold.yml index b1565a0cd..654ec471f 100644 --- a/.github/workflows/test-setup-rust-mold.yml +++ b/.github/workflows/test-setup-rust-mold.yml @@ -34,7 +34,8 @@ jobs: include: - platform: linux-x86_64 runner: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} - platform: linux-aarch64 runner: ubuntu-24.04-arm diff --git a/.github/workflows/test-setup-rust-sccache.yml b/.github/workflows/test-setup-rust-sccache.yml index 3bfedccd2..990c3d05d 100644 --- a/.github/workflows/test-setup-rust-sccache.yml +++ b/.github/workflows/test-setup-rust-sccache.yml @@ -118,8 +118,12 @@ jobs: respects_a_caller_backend_choice: # A caller who turned the backend off must stay off, so the action cannot # quietly re-enable a cache they declined. + # Dependabot pull requests run on the hosted pool too (free on a public + # repository). The author is tested, not the actor, which changes when + # someone else pushes to the branch. runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 10 env: @@ -153,7 +157,8 @@ jobs: respects_a_caller_wrapper: runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 10 env: @@ -192,7 +197,8 @@ jobs: # survives the action. SCCACHE_GHA_ENABLED is off so the assertion is about # the variable alone, not about reaching any cache service. runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 10 env: diff --git a/.github/workflows/test-stage-release-artefacts.yml b/.github/workflows/test-stage-release-artefacts.yml index 697221977..4d7d3fe8e 100644 --- a/.github/workflows/test-stage-release-artefacts.yml +++ b/.github/workflows/test-stage-release-artefacts.yml @@ -5,8 +5,12 @@ on: jobs: test-stage-artefacts: + # Dependabot pull requests run on the hosted pool too (free on a public + # repository). The author is tested, not the actor, which changes when + # someone else pushes to the branch. runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 10 permissions: @@ -60,7 +64,8 @@ jobs: test-stage-artefacts-env-overrides: runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 10 permissions: @@ -119,7 +124,8 @@ jobs: test-stage-artefacts-powershell-help: runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 10 permissions: @@ -192,7 +198,8 @@ jobs: test-stage-artefacts-binstall: runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 10 permissions: diff --git a/.github/workflows/test-ubicloud-sccache-proxy.yml b/.github/workflows/test-ubicloud-sccache-proxy.yml index b693adea7..24bc93239 100644 --- a/.github/workflows/test-ubicloud-sccache-proxy.yml +++ b/.github/workflows/test-ubicloud-sccache-proxy.yml @@ -33,6 +33,8 @@ jobs: if: >- github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + # dependabot-runner: exempt. This job proves the Ubicloud cache proxy, which a + # GitHub-hosted runner cannot show, so Dependabot's pull requests stay here. runs-on: ubicloud-standard-2 timeout-minutes: 10 env: @@ -154,6 +156,8 @@ jobs: if: >- github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + # dependabot-runner: exempt. This job proves the Ubicloud cache proxy, which a + # GitHub-hosted runner cannot show, so Dependabot's pull requests stay here. runs-on: ubicloud-standard-2 timeout-minutes: 10 steps: diff --git a/.github/workflows/test-upload-release-assets.yml b/.github/workflows/test-upload-release-assets.yml index 4bcce81a0..9af2d46c4 100644 --- a/.github/workflows/test-upload-release-assets.yml +++ b/.github/workflows/test-upload-release-assets.yml @@ -5,8 +5,12 @@ on: jobs: test-upload-assets-dry-run: + # Dependabot pull requests run on the hosted pool too (free on a public + # repository). The author is tested, not the actor, which changes when + # someone else pushes to the branch. runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 10 permissions: @@ -40,7 +44,8 @@ jobs: test-upload-assets-env-overrides: runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} timeout-minutes: 10 permissions: diff --git a/docs/developers-guide.md b/docs/developers-guide.md index 2c9aa3220..5372d1a5c 100644 --- a/docs/developers-guide.md +++ b/docs/developers-guide.md @@ -2310,7 +2310,7 @@ each of which a plausible edit would otherwise break silently: | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | The proof runs | Some step's only command executes `workflow_scripts/prove_cargo_watchdog.py` unconditionally, directly or through `uv run`, with `--runner .github/actions/generate-coverage/scripts/run_rust.py`. | | The lane is unfiltered | The `pull_request` trigger declares none of `paths`, `paths-ignore`, `branches`, `branches-ignore` or `types`. | -| The runner is mapped | Every job's `runs-on` sends a fork's pull request to `ubuntu-latest` and everything else to `ubicloud-standard-2`. | +| The runner is mapped | Every job's `runs-on` sends a fork's or Dependabot's pull request to `ubuntu-latest` and everything else to `ubicloud-standard-2`. | | The expression is one line | Every job's `runs-on`, read as written, parses without a line break. | @@ -3675,18 +3675,25 @@ queue, and the job ceiling does not end that wait: `timeout-minutes` limits a job's running time, not its time in the queue, and GitHub lets a job wait for a runner for up to 24 hours. An external contribution would lose its Linux CI and might not learn so for a day. A lane a fork's pull request can reach therefore -selects its label from the head repository: +selects its label from the head repository. A pull request Dependabot opened +takes the same hosted arm: hosted minutes cost nothing on a public repository, +and Dependabot opens many pull requests, which therefore run on the hosted pool +without Ubicloud's cache store. The condition tests the pull request's author, +not `github.actor`, because the actor is whoever triggered the run, so a person +pushing to a Dependabot branch would flip the runner: ```yaml runs-on: >- - ${{ github.event.pull_request.head.repo.fork + ${{ (github.event.pull_request.head.repo.fork + || github.event.pull_request.user.login == 'dependabot[bot]') && 'ubuntu-latest' || 'ubicloud-standard-2' }} ``` -On every other event `github.event.pull_request` is absent, so the expression -yields the Ubicloud label and a push, dispatch or tag lane is unaffected. A -lane with no `pull_request` trigger names the label outright, because no fork -can reach it and the expression would be decoration. +On every other event `github.event.pull_request` is absent, so both properties +are empty strings (falsy) and the expression yields the Ubicloud label and a +push, dispatch or tag lane is unaffected. A lane with no `pull_request` trigger +names the label outright, because no fork can reach it and the expression would +be decoration. Falling back rather than skipping is the point. A guard that skips leaves a fork with no Linux CI at all, which is a worse answer to the same problem. The @@ -3699,7 +3706,9 @@ through the repository's own action tree, which a fork's pull request cannot reach in the shape the proof needs. Its service-touching half, `test-codescene-parser-proof.yml`, is dispatch-only from main, so no fork reaches it and it names the Ubicloud label outright. Both skip a fork's pull -request instead, and `FORK_FALLBACK_EXEMPTIONS` records why while +request instead, and so stay on Ubicloud for Dependabot, whose pull requests +are same-repository ones (each carries a `dependabot-runner: exempt` comment +saying so), and `FORK_FALLBACK_EXEMPTIONS` records why while `test_a_fork_skipping_lane_really_skips_forks` checks the guard is actually there. diff --git a/tests/workflows/test_coverage_watchdog_lane.py b/tests/workflows/test_coverage_watchdog_lane.py index c7a59b797..a6b854f22 100644 --- a/tests/workflows/test_coverage_watchdog_lane.py +++ b/tests/workflows/test_coverage_watchdog_lane.py @@ -341,7 +341,9 @@ def _raw_runs_on(workflow: _Workflow, job_name: str) -> str: #: exactly: swapping `fork` for a sibling such as `private` changes which #: pull requests fall back and must not match here. _FORK_AWARE_RUNS_ON: typ.Final[re.Pattern[str]] = re.compile( - r"^\$\{\{\s*github\.event\.pull_request\.head\.repo\.fork" + r"^\$\{\{\s*\(\s*github\.event\.pull_request\.head\.repo\.fork" + r"\s*\|\|\s*github\.event\.pull_request\.user\.login" + r"\s*==\s*'dependabot\[bot\]'\s*\)" r"\s*&&\s*'(?P[^']+)'" r"\s*\|\|\s*'(?P[^']+)'\s*\}\}$" ) diff --git a/tests/workflows/test_runner_placement.py b/tests/workflows/test_runner_placement.py index 9c2d3c592..246072a3d 100644 --- a/tests/workflows/test_runner_placement.py +++ b/tests/workflows/test_runner_placement.py @@ -47,7 +47,9 @@ #: nothing here, which is the point: the rule is about forks, not about #: whichever boolean sits next to it. _FORK_AWARE_RUNS_ON: typ.Final[re.Pattern[str]] = re.compile( - r"^\$\{\{\s*github\.event\.pull_request\.head\.repo\.fork" + r"^\$\{\{\s*\(\s*github\.event\.pull_request\.head\.repo\.fork" + r"\s*\|\|\s*github\.event\.pull_request\.user\.login" + r"\s*==\s*'dependabot\[bot\]'\s*\)" r"\s*&&\s*'(?P[^']+)'" r"\s*\|\|\s*'(?P[^']+)'\s*\}\}$" )