Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions .github/actionlint.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
self-hosted-runner:
labels:
# Active default lives in the MACOS_RUNNER_15 / MACOS_RUNNER_26 repo
# variables; these are the literal labels referenced as fallbacks or as
# manual workflow_dispatch choices. See docs/macos-ci-runners.md.
# macOS jobs run on Blacksmith (hardcoded in the workflows). These are the
# labels referenced directly or offered as manual workflow_dispatch choices.
# See docs/macos-ci-runners.md.
- blacksmith-6vcpu-macos-15
- blacksmith-6vcpu-macos-26
- blacksmith-6vcpu-macos-latest
- warp-macos-15-arm64-6x
- warp-macos-26-arm64-6x
- depot-macos-latest
- depot-macos-14
2 changes: 1 addition & 1 deletion .github/workflows/build-ghosttykit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ concurrency:

jobs:
build-ghosttykit:
runs-on: ${{ vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x' }}
runs-on: blacksmith-6vcpu-macos-15
timeout-minutes: 20
env:
GHOSTTYKIT_CRASH_REPORT_SUBDIR: cmux/crash
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci-macos-compat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ jobs:
fail-fast: false
matrix:
include:
- os: ${{ vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x' }}
- os: blacksmith-6vcpu-macos-15
timeout: 30
startup_smoke: true
virtual_display: true
skip_zig: false
- os: ${{ vars.MACOS_RUNNER_26 || 'warp-macos-26-arm64-6x' }}
- os: blacksmith-6vcpu-macos-26
timeout: 30
startup_smoke: true
virtual_display: false
Expand Down
16 changes: 11 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,13 @@ jobs:
bun test tests/vm-db-read-model.test.ts

tests:
runs-on: ${{ vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x' }}
# macOS CI runs on Blacksmith (hardcoded, git-tracked; no override variable).
# To change provider or roll back to Warp, edit the runs-on labels in a PR.
# macos-15 vs macos-26: helper / GhosttyKit / nightly jobs build on macos-15
# because zig can only cross-link the universal Ghostty CLI helper against a
# pre-26 SDK; the app and SDK-validation builds use macos-26 (Liquid Glass).
# See docs/macos-ci-runners.md.
runs-on: blacksmith-6vcpu-macos-15
timeout-minutes: 75
env:
CMUX_SKIP_ZIG_BUILD: "1"
Expand Down Expand Up @@ -427,7 +433,7 @@ jobs:
# Keep lag validation separate from UI regressions so functional UI failures
# and performance regressions stay isolated. Broader interactive UI suites
# still run via test-e2e.yml on GitHub-hosted runners.
runs-on: ${{ vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x' }}
runs-on: blacksmith-6vcpu-macos-15
# A cold DerivedData cache (any project.pbxproj or Package.resolved change
# mints a new cache key with no restore-keys fallback) forces a full
# cmux build whose Swift codegen alone can run 20+ min. Project/package
Expand Down Expand Up @@ -622,7 +628,7 @@ jobs:
rm -f /tmp/create-virtual-display

release-ghostty-cli-helper:
runs-on: ${{ vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x' }}
runs-on: blacksmith-6vcpu-macos-15
timeout-minutes: 20
steps:
- name: Checkout
Expand Down Expand Up @@ -661,7 +667,7 @@ jobs:
# mistakes before they reach main.
# Keep the app build on macOS 26 so SDK-gated SwiftUI Liquid Glass code
# compiles into the same artifact shape as nightly and stable releases.
runs-on: ${{ vars.MACOS_RUNNER_26 || 'warp-macos-26-arm64-6x' }}
runs-on: blacksmith-6vcpu-macos-26
timeout-minutes: 20
steps:
- name: Checkout
Expand Down Expand Up @@ -770,7 +776,7 @@ jobs:
[[ "$SDK_VERSION" == 26.* ]]

ui-regressions:
runs-on: ${{ vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x' }}
runs-on: blacksmith-6vcpu-macos-15
# Cold builds after project/package changes can spend more than 25 minutes
# in build-for-testing before the UI regression script starts.
timeout-minutes: 45
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ jobs:
# and injected before signing (see Select Xcode + the helper build/inject
# steps). Building the whole app with Xcode 16 (#5022) shipped a macOS 15 SDK
# binary that Tahoe forced into the legacy non-Liquid-Glass appearance.
runs-on: ${{ vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x' }}
runs-on: blacksmith-6vcpu-macos-15
timeout-minutes: 30
steps:
- name: Checkout build ref
Expand Down
10 changes: 4 additions & 6 deletions .github/workflows/perf-activation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
required: false
default: ""
runner:
description: macOS runner (auto follows the MACOS_RUNNER_15 repo variable, then warp)
description: macOS runner (auto uses the Blacksmith default)
required: false
default: auto
type: choice
Expand All @@ -18,8 +18,6 @@ on:
- blacksmith-6vcpu-macos-15
- blacksmith-6vcpu-macos-26
- blacksmith-6vcpu-macos-latest
- warp-macos-15-arm64-6x
- warp-macos-26-arm64-6x
workspace_count:
description: Fixture workspace count
required: false
Expand All @@ -39,7 +37,7 @@ concurrency:

jobs:
activation-session:
runs-on: ${{ (!inputs.runner || inputs.runner == 'auto') && (vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x') || inputs.runner }}
runs-on: ${{ (!inputs.runner || inputs.runner == 'auto') && 'blacksmith-6vcpu-macos-15' || inputs.runner }}
timeout-minutes: 45
env:
PERF_TAG: perfci
Expand Down Expand Up @@ -94,8 +92,8 @@ jobs:
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: .ci-source-packages
key: spm-${{ (!inputs.runner || inputs.runner == 'auto') && (vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x') || inputs.runner }}-${{ hashFiles('cmux.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}
restore-keys: spm-${{ (!inputs.runner || inputs.runner == 'auto') && (vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x') || inputs.runner }}-
key: spm-${{ (!inputs.runner || inputs.runner == 'auto') && 'blacksmith-6vcpu-macos-15' || inputs.runner }}-${{ hashFiles('cmux.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}
restore-keys: spm-${{ (!inputs.runner || inputs.runner == 'auto') && 'blacksmith-6vcpu-macos-15' || inputs.runner }}-

- name: Resolve Swift packages
run: |
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ env:

jobs:
build-ghostty-cli-helper:
runs-on: ${{ vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x' }}
runs-on: blacksmith-6vcpu-macos-15
timeout-minutes: 20
steps:
- name: Checkout
Expand Down Expand Up @@ -53,7 +53,7 @@ jobs:
# Build the app on macOS 26 so SDK-gated SwiftUI Liquid Glass code compiles
# into stable releases. The real universal Ghostty CLI helper is built on
# macOS 15 above because Zig 0.15.2 cannot link it on macOS 26.
runs-on: ${{ vars.MACOS_RUNNER_26 || 'warp-macos-26-arm64-6x' }}
runs-on: blacksmith-6vcpu-macos-26
timeout-minutes: 20
steps:
- name: Checkout
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-depot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ on:

jobs:
tests:
runs-on: ${{ vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x' }}
runs-on: blacksmith-6vcpu-macos-15
timeout-minutes: 20
steps:
- name: Checkout
Expand Down
18 changes: 8 additions & 10 deletions .github/workflows/test-e2e.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: E2E test with video recording
run-name: ${{ inputs.test_filter }} on ${{ (!inputs.runner || inputs.runner == 'auto') && (vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x') || inputs.runner }} @ ${{ inputs.ref || github.ref_name }}
run-name: ${{ inputs.test_filter }} on ${{ (!inputs.runner || inputs.runner == 'auto') && 'blacksmith-6vcpu-macos-15' || inputs.runner }} @ ${{ inputs.ref || github.ref_name }}

on:
workflow_dispatch:
Expand All @@ -21,7 +21,7 @@ on:
default: true
type: boolean
runner:
description: "Runner OS (auto follows the MACOS_RUNNER_15 repo variable, then warp; pick depot-macos-* for GUI activation)"
description: "Runner OS (auto uses the Blacksmith default; pick depot-macos-* for GUI activation)"
required: false
default: "auto"
type: choice
Expand All @@ -30,26 +30,24 @@ on:
- blacksmith-6vcpu-macos-15
- blacksmith-6vcpu-macos-26
- blacksmith-6vcpu-macos-latest
- warp-macos-15-arm64-6x
- warp-macos-26-arm64-6x
- depot-macos-latest
- depot-macos-14

concurrency:
group: e2e-${{ (!inputs.runner || inputs.runner == 'auto') && (vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x') || inputs.runner }}-${{ inputs.ref || github.ref_name }}-${{ inputs.test_filter }}
group: e2e-${{ (!inputs.runner || inputs.runner == 'auto') && 'blacksmith-6vcpu-macos-15' || inputs.runner }}-${{ inputs.ref || github.ref_name }}-${{ inputs.test_filter }}
cancel-in-progress: true

jobs:
e2e:
runs-on: ${{ (!inputs.runner || inputs.runner == 'auto') && (vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x') || inputs.runner }}
runs-on: ${{ (!inputs.runner || inputs.runner == 'auto') && 'blacksmith-6vcpu-macos-15' || inputs.runner }}
timeout-minutes: 20
env:
TEST_REF: ${{ inputs.ref || github.ref }}
steps:
- name: Validate Depot runner identity
if: ${{ startsWith((!inputs.runner || inputs.runner == 'auto') && (vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x') || inputs.runner, 'depot-macos-') }}
if: ${{ startsWith((!inputs.runner || inputs.runner == 'auto') && 'blacksmith-6vcpu-macos-15' || inputs.runner, 'depot-macos-') }}
env:
REQUESTED_RUNNER: ${{ (!inputs.runner || inputs.runner == 'auto') && (vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x') || inputs.runner }}
REQUESTED_RUNNER: ${{ (!inputs.runner || inputs.runner == 'auto') && 'blacksmith-6vcpu-macos-15' || inputs.runner }}
RUNNER_CONTEXT_NAME: ${{ runner.name }}
run: |
set -euo pipefail
Expand Down Expand Up @@ -232,8 +230,8 @@ jobs:
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: .ci-source-packages
key: spm-${{ (!inputs.runner || inputs.runner == 'auto') && (vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x') || inputs.runner }}-${{ hashFiles('cmux.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}
restore-keys: spm-${{ (!inputs.runner || inputs.runner == 'auto') && (vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x') || inputs.runner }}-
key: spm-${{ (!inputs.runner || inputs.runner == 'auto') && 'blacksmith-6vcpu-macos-15' || inputs.runner }}-${{ hashFiles('cmux.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}
restore-keys: spm-${{ (!inputs.runner || inputs.runner == 'auto') && 'blacksmith-6vcpu-macos-15' || inputs.runner }}-

- name: Resolve Swift packages
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tmux-corpus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:

terminal-nightly:
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
runs-on: ${{ vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x' }}
runs-on: blacksmith-6vcpu-macos-15

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Hardcoded runner label removes the vars.MACOS_RUNNER_15 override, contradicting the PR's stated design of "Auto resolution now follows MACOS_RUNNER_15 first, then Blacksmith." The original pattern allowed operators to redirect this job by setting a repo variable if Blacksmith has queue or capacity issues; the new hardcoded label removes that flexibility entirely.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/tmux-corpus.yml, line 64:

<comment>Hardcoded runner label removes the `vars.MACOS_RUNNER_15` override, contradicting the PR's stated design of "Auto resolution now follows MACOS_RUNNER_15 first, then Blacksmith." The original pattern allowed operators to redirect this job by setting a repo variable if Blacksmith has queue or capacity issues; the new hardcoded label removes that flexibility entirely.</comment>

<file context>
@@ -61,7 +61,7 @@ jobs:
   terminal-nightly:
     if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
-    runs-on: ${{ vars.MACOS_RUNNER_15 || 'blacksmith-6vcpu-macos-15' }}
+    runs-on: blacksmith-6vcpu-macos-15
     timeout-minutes: 30
     steps:
</file context>
Suggested change
runs-on: blacksmith-6vcpu-macos-15
runs-on: ${{ vars.MACOS_RUNNER_15 || 'blacksmith-6vcpu-macos-15' }}

timeout-minutes: 30
steps:
- name: Checkout
Expand Down
40 changes: 9 additions & 31 deletions docs/macos-ci-runners.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,22 @@
# macOS CI runners

All paid macOS CI/CD jobs pick their runner from two repository variables instead of a hardcoded label:
All paid macOS CI/CD jobs run on Blacksmith. The runner labels are hardcoded in the workflows (git-tracked); there is no repo-variable override.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The docs overstate runner behavior: Blacksmith is the default, but manual workflows can still run on Depot runners.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/macos-ci-runners.md, line 3:

<comment>The docs overstate runner behavior: Blacksmith is the default, but manual workflows can still run on Depot runners.</comment>

<file context>
@@ -1,46 +1,22 @@
 # macOS CI runners
 
-All paid macOS CI/CD jobs pick their runner from two repository variables instead of a hardcoded label:
+All paid macOS CI/CD jobs run on Blacksmith. The runner labels are hardcoded in the workflows (git-tracked); there is no repo-variable override.
 
-- `MACOS_RUNNER_15` for jobs that build the real universal Release app, including nightly, stable release, `release-build`, and the e2e/perf defaults.
</file context>
Suggested change
All paid macOS CI/CD jobs run on Blacksmith. The runner labels are hardcoded in the workflows (git-tracked); there is no repo-variable override.
All paid macOS CI/CD jobs default to Blacksmith. Runner labels are git-tracked in workflows, manual workflows can still choose explicit runner options, and there is no repo-variable override.


- `MACOS_RUNNER_15` for jobs that build the real universal Release app, including nightly, stable release, `release-build`, and the e2e/perf defaults.
- `MACOS_RUNNER_26` for macOS 26 compatibility coverage and jobs that do not need Zig to build the real universal Ghostty CLI helper.
- `blacksmith-6vcpu-macos-15` for jobs that build the universal Ghostty CLI helper (zig 0.15.2 can only cross-link the x86_64 slice against a pre-26 SDK), for the GhosttyKit framework build, for unit/UI tests, and for the nightly app build (which uses Xcode 26 on the macOS 15 host).
- `blacksmith-6vcpu-macos-26` for the app builds that must link the macOS 26 SDK so SDK-gated SwiftUI Liquid Glass code compiles in: stable `release` `build-sign-notarize` and the CI `release-build` SDK-validation lane. `ci-macos-compat.yml` also exercises macOS 26 with `CMUX_SKIP_ZIG_BUILD=1`.

Workflows reference them as `runs-on: ${{ vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x' }}`. If a variable is unset, the job falls back to WarpBuild, so CI is never broken by a missing variable.
Workflows reference these labels directly, e.g. `runs-on: blacksmith-6vcpu-macos-15`.

Nightly and stable release stay on macOS 15 until Zig can link the real universal Ghostty CLI helper on macOS 26. `ci-macos-compat.yml` still covers macOS 26 by setting `CMUX_SKIP_ZIG_BUILD=1`, but publishing workflows cannot use that stub because the signed artifacts must include the real helper.
## Changing the runner

## Switch Blacksmith <-> WarpBuild
To switch providers or roll back to Warp, edit the `runs-on` labels in `.github/workflows/*.yml` and open a PR. There is no variable to flip; the runner choice lives in git so every change is reviewed and recorded.

The switch is a repo-variable change. It takes effect on the next workflow run, with no PR or commit.

Use Blacksmith (default):

```bash
gh variable set MACOS_RUNNER_15 --repo manaflow-ai/cmux -b blacksmith-6vcpu-macos-15
gh variable set MACOS_RUNNER_26 --repo manaflow-ai/cmux -b blacksmith-6vcpu-macos-26
```

Fall back to WarpBuild (e.g. Blacksmith macOS capacity is queuing, as happened in https://github.com/manaflow-ai/cmux/pull/4926):

```bash
gh variable delete MACOS_RUNNER_15 --repo manaflow-ai/cmux
gh variable delete MACOS_RUNNER_26 --repo manaflow-ai/cmux
```

Deleting the variables reverts to the WarpBuild fallback baked into the workflows. You can also set them explicitly to `warp-macos-15-arm64-6x` / `warp-macos-26-arm64-6x`.

Check current values:

```bash
gh variable list --repo manaflow-ai/cmux
```
If a new runner label is introduced, add it to `.github/actionlint.yaml` so `actionlint` recognizes it.

## Manual runs

`perf-activation.yml` and `test-e2e.yml` keep a `runner` choice input that defaults to `auto`. `auto` (and the empty `pull_request` case for perf) follows `MACOS_RUNNER_15` then the Warp fallback, so flipping the repo variable also redirects these workflows. An explicit choice wins over the variable; both dropdowns expose `warp-macos-15-arm64-6x` / `warp-macos-26-arm64-6x` so an operator can pick Warp directly during a Blacksmith outage. `test-e2e.yml` also keeps `depot-macos-*` choices and a Depot identity guard for GUI-activation runs.
`perf-activation.yml` and `test-e2e.yml` keep a `runner` choice input that defaults to `auto`. `auto` (and the empty `pull_request` case for perf) resolves to the hardcoded Blacksmith default; an explicit choice wins. `test-e2e.yml` also keeps `depot-macos-*` choices and a Depot identity guard for GUI-activation runs.

## Guard

`tests/test_ci_self_hosted_guard.sh` (run by the `workflow-guard-tests` job) asserts every paid macOS job references `vars.MACOS_RUNNER_*` or a Blacksmith/Warp label, so a job can never silently fall back to a free GitHub-hosted runner. Keep new labels in `.github/actionlint.yaml`.
`tests/test_ci_self_hosted_guard.sh` (run by the `workflow-guard-tests` job) asserts every paid macOS job references a Blacksmith label, so a job can never silently fall back to a free GitHub-hosted runner. `tests/test_ci_release_sdk_lane.sh` asserts the release/CI app builds use macOS 26 with a macOS-15-built helper.
8 changes: 4 additions & 4 deletions tests/test_ci_release_sdk_lane.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,25 @@ require_job_contains() {
require_job_contains \
"$RELEASE_FILE" \
"build-ghostty-cli-helper" \
'runs-on: ${{ vars.MACOS_RUNNER_15 || '\''warp-macos-15-arm64-6x'\'' }}' \
'runs-on: blacksmith-6vcpu-macos-15' \
"release must build the real Ghostty CLI helper on macOS 15"

require_job_contains \
"$RELEASE_FILE" \
"build-sign-notarize" \
'runs-on: ${{ vars.MACOS_RUNNER_26 || '\''warp-macos-26-arm64-6x'\'' }}' \
'runs-on: blacksmith-6vcpu-macos-26' \
"release must build the app on macOS 26"

require_job_contains \
"$CI_FILE" \
"release-ghostty-cli-helper" \
'runs-on: ${{ vars.MACOS_RUNNER_15 || '\''warp-macos-15-arm64-6x'\'' }}' \
'runs-on: blacksmith-6vcpu-macos-15' \
"CI must build the real Ghostty CLI helper on macOS 15"

require_job_contains \
"$CI_FILE" \
"release-build" \
'runs-on: ${{ vars.MACOS_RUNNER_26 || '\''warp-macos-26-arm64-6x'\'' }}' \
'runs-on: blacksmith-6vcpu-macos-26' \
"CI release-build must compile the app on macOS 26"

for workflow in "$CI_FILE" "$RELEASE_FILE"; do
Expand Down
17 changes: 8 additions & 9 deletions tests/test_ci_self_hosted_guard.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
#!/usr/bin/env bash
# Regression test for https://github.com/manaflow-ai/cmux/issues/385.
# Ensures paid CI jobs use a paid macOS runner (Blacksmith or WarpBuild, routed
# through the MACOS_RUNNER_15 / MACOS_RUNNER_26 repo variables), never a free
# GitHub-hosted runner. Flip Blacksmith<->Warp by editing those repo variables;
# see docs/macos-ci-runners.md.
# Ensures paid CI jobs use a paid macOS runner (a Blacksmith label), never a
# free GitHub-hosted runner. Runners are hardcoded in the workflows; see
# docs/macos-ci-runners.md.
# Fork PRs are gated by GitHub's built-in "Require approval for outside
# collaborators" setting, so workflow-level fork guards are not needed.
set -euo pipefail
Expand All @@ -19,11 +18,11 @@ check_macos_runner() {
if ! awk -v job="$job" '
$0 ~ "^ "job":" { in_job=1; next }
in_job && /^ [^[:space:]#][^:]*:[[:space:]]*(#.*)?$/ { in_job=0 }
in_job && /runs-on:.*(vars\.MACOS_RUNNER|blacksmith-[0-9]+vcpu-macos-|warp-macos-[0-9]+-arm64)/ { saw=1 }
in_job && /os:.*(vars\.MACOS_RUNNER|blacksmith-[0-9]+vcpu-macos-|warp-macos-[0-9]+-arm64)/ { saw=1 }
in_job && /runs-on:.*blacksmith-[0-9]+vcpu-macos-/ { saw=1 }
in_job && /os:.*blacksmith-[0-9]+vcpu-macos-/ { saw=1 }
END { exit !(saw) }
' "$file"; then
echo "FAIL: $job in $(basename "$file") must run on a paid macOS runner (vars.MACOS_RUNNER_* or a Blacksmith/Warp label), not a GitHub-hosted runner"
echo "FAIL: $job in $(basename "$file") must run on a paid macOS runner (a Blacksmith label), not a GitHub-hosted runner"
exit 1
fi
echo "PASS: $job in $(basename "$file") uses a paid macOS runner"
Expand Down Expand Up @@ -61,7 +60,7 @@ check_e2e_runner_fallbacks() {
exit 1
fi

if ! grep -Fq "startsWith((!inputs.runner || inputs.runner == 'auto') && (vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x') || inputs.runner, 'depot-macos-')" "$E2E_FILE"; then
if ! grep -Fq "startsWith((!inputs.runner || inputs.runner == 'auto') && 'blacksmith-6vcpu-macos-15' || inputs.runner, 'depot-macos-')" "$E2E_FILE"; then
echo "FAIL: test-e2e.yml must validate all Depot macOS runner choices"
exit 1
fi
Expand Down Expand Up @@ -133,7 +132,7 @@ check_macos_runner "$CI_FILE" "ui-regressions"
# build-ghosttykit.yml
check_macos_runner "$GHOSTTYKIT_FILE" "build-ghosttykit"

# ci-macos-compat.yml (matrix.os routed through the MACOS_RUNNER_* repo vars)
# ci-macos-compat.yml (matrix.os uses hardcoded Blacksmith labels)
check_macos_runner "$COMPAT_FILE" "compat-tests"

# test-e2e.yml is manual, so keep the Depot GUI runner choices but cancel
Expand Down
Loading