diff --git a/.github/runners.json b/.github/runners.json index 9cdd87874306..01b4f494f601 100644 --- a/.github/runners.json +++ b/.github/runners.json @@ -28,7 +28,7 @@ "fleets": { "blacksmith": { "linux": "blacksmith-4vcpu-ubuntu-2404", - "linux_arm64": "ubuntu-24.04-arm", + "linux_arm64": "blacksmith-4vcpu-ubuntu-2404-arm", "macos_15": "blacksmith-6vcpu-macos-15", "macos_15_gui": "blacksmith-6vcpu-macos-15", "macos_15_sdk15": "blacksmith-6vcpu-macos-15", diff --git a/.github/workflows/auto-triage.yml b/.github/workflows/auto-triage.yml index 6aa81117a227..a1d8a8268d8a 100644 --- a/.github/workflows/auto-triage.yml +++ b/.github/workflows/auto-triage.yml @@ -31,7 +31,7 @@ jobs: contents: read issues: write if: github.event_name == 'issues' - runs-on: ubuntu-24.04 # github-hosted-required: trusted issue-write token + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: trusted issue-write token; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted timeout-minutes: 5 steps: - name: Check out trusted triage code @@ -50,7 +50,7 @@ jobs: contents: read issues: write if: github.event_name == 'workflow_dispatch' && fromJSON(inputs.backfill_limit || '0') > 0 - runs-on: ubuntu-24.04 # github-hosted-required: trusted issue-write token + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: trusted issue-write token; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted timeout-minutes: 30 steps: - name: Check out trusted triage code diff --git a/.github/workflows/build-ghosttykit.yml b/.github/workflows/build-ghosttykit.yml index 778d3e20d860..596124d8c602 100644 --- a/.github/workflows/build-ghosttykit.yml +++ b/.github/workflows/build-ghosttykit.yml @@ -18,7 +18,7 @@ jobs: # Background lane: dispatch-only and off the PR critical path, so it runs # on free GitHub-hosted capacity instead of the shared paid macOS pool. # See docs/ci-runners.md "Background lane". - runs-on: ${{ vars.MACOS_RUNNER_BACKGROUND || 'macos-15' }} + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'macos-26' || vars.MACOS_RUNNER_BACKGROUND || 'blacksmith-6vcpu-macos-15' }} timeout-minutes: 60 env: GHOSTTYKIT_CRASH_REPORT_SUBDIR: cmux/crash diff --git a/.github/workflows/ci-compile-attribution.yml b/.github/workflows/ci-compile-attribution.yml index dfc6bb5f6ec6..530230356ccc 100644 --- a/.github/workflows/ci-compile-attribution.yml +++ b/.github/workflows/ci-compile-attribution.yml @@ -101,7 +101,7 @@ jobs: name: ${{ needs.analyze.outputs.headline || 'report' }} needs: analyze if: ${{ needs.analyze.outputs.state == 'red' || needs.analyze.outputs.state == 'green' }} - runs-on: ubuntu-24.04 # github-hosted-required: attribution must follow a failed compile within a minute + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: attribution must follow a failed compile within a minute; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted timeout-minutes: 10 permissions: actions: write # dispatches main-compile-probe.yml @@ -162,7 +162,7 @@ jobs: name: Fix forward or revert needs: [analyze, report] if: ${{ needs.analyze.outputs.state == 'red' && needs.analyze.outputs.fix == 'true' && !inputs.dry_run && !inputs.head }} - runs-on: ubuntu-24.04 # github-hosted-required: holds the Claude token and the fix PR App key + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: holds the Claude token and the fix PR App key; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted timeout-minutes: 30 concurrency: group: ci-compile-attribution-fix diff --git a/.github/workflows/ci-failure-attribution.yml b/.github/workflows/ci-failure-attribution.yml index 33c4efd1ed34..f7822440a082 100644 --- a/.github/workflows/ci-failure-attribution.yml +++ b/.github/workflows/ci-failure-attribution.yml @@ -42,7 +42,7 @@ jobs: attribute: name: Attribute if: ${{ github.repository == 'manaflow-ai/cmux' && github.event.workflow_run.event == 'pull_request' && (github.event.action == 'requested' || contains(fromJSON('["success","failure","cancelled"]'), github.event.workflow_run.conclusion)) }} - runs-on: ubuntu-24.04 # github-hosted-required: write token; the re-run must follow CI within a minute + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: write token; the re-run must follow CI within a minute; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted timeout-minutes: 10 permissions: actions: write diff --git a/.github/workflows/ci-guard-attribution.yml b/.github/workflows/ci-guard-attribution.yml index c6b9ec64ef0f..4fb296a22d3a 100644 --- a/.github/workflows/ci-guard-attribution.yml +++ b/.github/workflows/ci-guard-attribution.yml @@ -106,7 +106,7 @@ jobs: name: ${{ needs.analyze.outputs.headline || 'report' }} needs: analyze if: ${{ needs.analyze.outputs.state == 'red' || needs.analyze.outputs.state == 'green' }} - runs-on: ubuntu-24.04 # github-hosted-required: attribution must follow the fast guard within a minute + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: attribution must follow the fast guard within a minute; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted timeout-minutes: 10 permissions: contents: read diff --git a/.github/workflows/ci-health-report.yml b/.github/workflows/ci-health-report.yml index 2343bda0b343..b5d6e72bdebd 100644 --- a/.github/workflows/ci-health-report.yml +++ b/.github/workflows/ci-health-report.yml @@ -85,7 +85,7 @@ jobs: # tests/test_runner_label_policy.py fails if a workflow reads a # runner variable this list leaves out. The background lane carries # its fallback because test_ci_self_hosted_guard.sh requires it on - # every read; `macos-15` is an approved label either way. + # every read. # CI_PR_POOL_ORDER is the one list that may also name owned pools. CMUX_CI_RUNNER_VARIABLES: | CI_LIGHT_LANE_RUNNER=${{ vars.CI_LIGHT_LANE_RUNNER }} @@ -102,7 +102,7 @@ jobs: MACOS_RUNNER_15=${{ vars.MACOS_RUNNER_15 }} MACOS_RUNNER_26=${{ vars.MACOS_RUNNER_26 }} MACOS_RUNNER_26_LARGE=${{ vars.MACOS_RUNNER_26_LARGE }} - MACOS_RUNNER_BACKGROUND=${{ vars.MACOS_RUNNER_BACKGROUND || 'macos-15' }} + MACOS_RUNNER_BACKGROUND=${{ vars.MACOS_RUNNER_BACKGROUND || 'blacksmith-6vcpu-macos-15' }} MACOS_RUNNER_DISPLAY=${{ vars.MACOS_RUNNER_DISPLAY }} MACOS_RUNNER_DUAL_XCODE=${{ vars.MACOS_RUNNER_DUAL_XCODE }} MACOS_RUNNER_IOS=${{ vars.MACOS_RUNNER_IOS }} diff --git a/.github/workflows/ci-macos-compat.yml b/.github/workflows/ci-macos-compat.yml index 6979ccc8bf8e..288c76877b7a 100644 --- a/.github/workflows/ci-macos-compat.yml +++ b/.github/workflows/ci-macos-compat.yml @@ -9,15 +9,6 @@ jobs: fail-fast: false matrix: include: - - os: macos-14 - timeout: 60 - run_unit_tests: false - run_mobile_transport_tests: true - startup_smoke: true - virtual_display: true - skip_zig: false - expected_arch: arm64 - expected_os_major: "14" - os: macos-15-intel timeout: 90 run_unit_tests: false @@ -59,9 +50,9 @@ jobs: EXPECTED_OS_MAJOR: ${{ matrix.expected_os_major }} run: | set -euo pipefail - # Pick the latest Xcode installed on the runner. GitHub-hosted macos-14 - # defaults to Xcode 15.4, but the project needs Xcode 16+ (Swift tools - # version 6.0 required by sentry-cocoa). + # Pick the latest Xcode installed on the runner. An image default can + # be older than the Xcode 16+ the project needs (Swift tools version + # 6.0 required by sentry-cocoa). XCODE_APP="$( find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null \ | sort \ diff --git a/.github/workflows/ci-manual-dispatch-guard.yml b/.github/workflows/ci-manual-dispatch-guard.yml index 20361af5f7be..4f886f19e3ec 100644 --- a/.github/workflows/ci-manual-dispatch-guard.yml +++ b/.github/workflows/ci-manual-dispatch-guard.yml @@ -23,7 +23,7 @@ jobs: github.event.workflow_run.path == '.github/workflows/ci.yml' && github.event.workflow_run.event == 'workflow_dispatch' && github.event.workflow_run.head_branch == 'main' - runs-on: ubuntu-24.04 # github-hosted-required: trusted Actions control-plane token + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: trusted Actions control-plane token; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted timeout-minutes: 3 permissions: actions: write diff --git a/.github/workflows/ci-owned-pool-rescue.yml b/.github/workflows/ci-owned-pool-rescue.yml index 1f8db1af6d31..67fc9cab1d2e 100644 --- a/.github/workflows/ci-owned-pool-rescue.yml +++ b/.github/workflows/ci-owned-pool-rescue.yml @@ -44,8 +44,8 @@ run-name: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow # owned pools are on (CI_PR_POOL_OWNED is 1), because a run on an owned pool # has no other way off it; CI_OWNED_POOL_RESCUE=0 turns it off. The switch # gets its default before the comparison: an unset variable is null, and -# null == '0' in an expression. It only polls, so it runs on a GitHub-hosted -# runner rather than holding a slot in CI's Linux pool. +# null == '0' in an expression. It only polls; it runs on an ephemeral +# Blacksmith runner (CI_TRUSTED_RUNNER), not on an owned pool it may rescue. on: schedule: - cron: "17 */2 * * *" @@ -93,7 +93,7 @@ jobs: rescue: name: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' && !inputs.run_id) && 'Sweep runs on persistent pools' || 'Rescue a run stuck on a persistent pool' }} if: ${{ vars.CI_PR_POOL_OWNED == '1' && (vars.CI_OWNED_POOL_RESCUE || '1') != '0' && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || (github.event.workflow_run.path != '.github/workflows/nightly.yml' && github.event.workflow_run.path != '.github/workflows/ios-screenshots.yml' && contains(fromJSON('["pull_request","push","schedule","workflow_dispatch"]'), github.event.workflow_run.event) && (startsWith(vars.CI_SIDE_LANE_RUNNER, 'glaeda-side-') || startsWith(vars.CI_LIGHT_LANE_RUNNER, 'glaeda-side-')) || github.event.workflow_run.path == '.github/workflows/ios-screenshots.yml' && github.event.workflow_run.event == 'workflow_dispatch' || github.event.workflow_run.path == '.github/workflows/nightly.yml' && (github.event.workflow_run.event == 'push' || github.event.workflow_run.event == 'schedule') && github.event.workflow_run.head_branch == 'main' && startsWith(vars.CI_SEED_TRUSTED_POOL, 'glaeda-trusted-') && startsWith(vars.CI_NIGHTLY_TRUSTED_RUNNER, 'glaeda-runner-')) && github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.run_attempt == 1) }} - runs-on: ubuntu-24.04 # github-hosted-required: polls the Actions API; keeps CI's Linux pool free + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: polls the Actions API; keeps CI's Linux pool free; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted # A sweeper adopts runs for 300 minutes and gives a rescue 25 more to # settle (SWEEP_SECONDS, RESCUE_GRACE_SECONDS). A single run's watch is # at most 150 minutes plus the same grace (JOB_TIMEOUT_SECONDS). diff --git a/.github/workflows/ci-repo-variables.yml b/.github/workflows/ci-repo-variables.yml index c051a009cb5b..24046a3c0663 100644 --- a/.github/workflows/ci-repo-variables.yml +++ b/.github/workflows/ci-repo-variables.yml @@ -70,7 +70,7 @@ jobs: MACOS_RUNNER_15=${{ vars.MACOS_RUNNER_15 }} MACOS_RUNNER_26=${{ vars.MACOS_RUNNER_26 }} MACOS_RUNNER_26_LARGE=${{ vars.MACOS_RUNNER_26_LARGE }} - MACOS_RUNNER_BACKGROUND=${{ vars.MACOS_RUNNER_BACKGROUND || 'macos-15' }} + MACOS_RUNNER_BACKGROUND=${{ vars.MACOS_RUNNER_BACKGROUND || 'blacksmith-6vcpu-macos-15' }} MACOS_RUNNER_DISPLAY=${{ vars.MACOS_RUNNER_DISPLAY }} MACOS_RUNNER_DUAL_XCODE=${{ vars.MACOS_RUNNER_DUAL_XCODE }} MACOS_RUNNER_IOS=${{ vars.MACOS_RUNNER_IOS }} diff --git a/.github/workflows/ci-stale-run-janitor.yml b/.github/workflows/ci-stale-run-janitor.yml index a4c26675787d..bf27be950b1a 100644 --- a/.github/workflows/ci-stale-run-janitor.yml +++ b/.github/workflows/ci-stale-run-janitor.yml @@ -36,7 +36,7 @@ jobs: actions: write contents: read pull-requests: read - runs-on: ubuntu-24.04 # github-hosted-required: trusted Actions control-plane token + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: trusted Actions control-plane token; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted timeout-minutes: 15 steps: - name: Check out trusted workflow code diff --git a/.github/workflows/ci-ui-tests.yml b/.github/workflows/ci-ui-tests.yml index c711d0e8d5b3..6d99d09cf7fe 100644 --- a/.github/workflows/ci-ui-tests.yml +++ b/.github/workflows/ci-ui-tests.yml @@ -45,7 +45,7 @@ jobs: # repaired. Re-enable with CI_UI_TESTS_ENABLED=1; await-request also keeps # non-UI diffs out of the downstream test runner. if: ${{ github.repository_owner == 'manaflow-ai' && vars.CI_UI_TESTS_ENABLED == '1' }} - runs-on: ubuntu-24.04 # github-hosted-required: holds actions write and mostly waits; keeps CI's Linux pool free + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: holds actions write and mostly waits; keeps CI's Linux pool free; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted # A request can follow a long compile admission; the dispatch then waits up # to 50 minutes for the compile it adopts, then runs the tests. timeout-minutes: 360 diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 6920c239f89e..0840e4a7efc5 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -33,7 +33,7 @@ jobs: (github.event_name == 'issues' && github.event.issue.user.type == 'User' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) # Anyone can write @claude, and the job starts before the action checks # write access, so no runner variable may pick the machine. - runs-on: ubuntu-24.04 # github-hosted-required: any commenter can start it + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: any commenter can start it; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted timeout-minutes: 15 # The workflow-level group only deduplicates webhook retries by event ID. # The pinned action checks repository write access before it invokes Claude. diff --git a/.github/workflows/cmux-browser.yml b/.github/workflows/cmux-browser.yml index d3e3dd520e98..9baf195affd5 100644 --- a/.github/workflows/cmux-browser.yml +++ b/.github/workflows/cmux-browser.yml @@ -21,7 +21,7 @@ jobs: host-tests: # Browser pull requests are untrusted code and must not run on a # configurable self-hosted runner with private network access. - runs-on: ubuntu-24.04 # github-hosted-required: browser PRs run untrusted code + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: browser PRs run untrusted code; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted timeout-minutes: 5 steps: - name: Checkout diff --git a/.github/workflows/cmux-tui-artifacts.yml b/.github/workflows/cmux-tui-artifacts.yml index 4e1ac62a93f3..16fc603c0dcf 100644 --- a/.github/workflows/cmux-tui-artifacts.yml +++ b/.github/workflows/cmux-tui-artifacts.yml @@ -86,7 +86,7 @@ jobs: # Post-merge publication is off the PR critical path, so its two macOS # Rust legs run on the background lane (free GitHub-hosted capacity) # unless a dispatch names a runner. See docs/ci-runners.md. - macos_runner: ${{ inputs.macos_runner || vars.MACOS_RUNNER_BACKGROUND || 'macos-15' }} + macos_runner: ${{ inputs.macos_runner || github.repository_owner != 'manaflow-ai' && 'macos-26' || vars.MACOS_RUNNER_BACKGROUND || 'blacksmith-6vcpu-macos-15' }} publish: # R2 upload credentials only. A commit-addressed cmux-tui build is not a diff --git a/.github/workflows/cmux-tui-build-package.yml b/.github/workflows/cmux-tui-build-package.yml index 244f53187c8c..f3540fc8af69 100644 --- a/.github/workflows/cmux-tui-build-package.yml +++ b/.github/workflows/cmux-tui-build-package.yml @@ -96,7 +96,7 @@ jobs: PACKAGE_PYPI: ${{ inputs.package_pypi }} MACOS_RUNNER: ${{ github.repository_owner != 'manaflow-ai' && 'macos-26' || (inputs.macos_runner != '' && inputs.macos_runner || vars.CI_PAID_MACOS_OVERFLOW == '1' && vars.MACOS_RUNNER_15 || 'blacksmith-6vcpu-macos-15') }} LINUX_RUNNER: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || (inputs.linux_runner != '' && inputs.linux_runner || vars.LINUX_RUNNER || 'blacksmith-4vcpu-ubuntu-2404') }} - LINUX_ARM64_RUNNER: ${{ vars.LINUX_ARM64_RUNNER || 'ubuntu-24.04-arm' }} + LINUX_ARM64_RUNNER: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04-arm' || vars.LINUX_ARM64_RUNNER || 'blacksmith-4vcpu-ubuntu-2404-arm' }} VERIFY_LINUX_ARM64: ${{ inputs.verify_linux_arm64 }} shell: bash run: | @@ -412,7 +412,7 @@ jobs: build-windows: name: build x86_64-pc-windows-gnu if: inputs.include_windows - runs-on: ${{ inputs.windows_runner != '' && inputs.windows_runner || vars.WINDOWS_RUNNER || 'windows-latest' }} + runs-on: ${{ inputs.windows_runner || github.repository_owner != 'manaflow-ai' && 'windows-2025' || vars.WINDOWS_RUNNER || 'blacksmith-4vcpu-windows-2025' }} timeout-minutes: 60 env: CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUSTFLAGS: -C link-arg=-fuse-ld=lld diff --git a/.github/workflows/cmux-tui.yml b/.github/workflows/cmux-tui.yml index a789fed2e50f..df54f7c2c537 100644 --- a/.github/workflows/cmux-tui.yml +++ b/.github/workflows/cmux-tui.yml @@ -626,7 +626,7 @@ jobs: name: test (windows) needs: validate-inputs if: inputs.mode == 'full' - runs-on: windows-latest + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'windows-2025' || vars.WINDOWS_RUNNER || 'blacksmith-4vcpu-windows-2025' }} timeout-minutes: 40 env: CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUSTFLAGS: -C link-arg=-fuse-ld=lld @@ -869,7 +869,7 @@ jobs: macos_runner: ${{ github.repository_owner != 'manaflow-ai' && 'macos-26' || vars.CI_PR_POOL_OWNED == '1' && contains(fromJSON('["pull_request","push","schedule","workflow_dispatch"]'), github.event_name) && github.run_attempt == 1 && vars.CI_SIDE_LANE_RUNNER || 'blacksmith-6vcpu-macos-15' }} macos_retry_runner: ${{ github.repository_owner != 'manaflow-ai' && 'macos-26' || 'blacksmith-6vcpu-macos-15' }} linux_runner: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && 'blacksmith-4vcpu-ubuntu-2404' || vars.LINUX_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} - windows_runner: windows-latest + windows_runner: ${{ github.repository_owner != 'manaflow-ai' && 'windows-2025' || vars.WINDOWS_RUNNER || 'blacksmith-4vcpu-windows-2025' }} checkout_ref: ${{ inputs.commit }} hosted-verification: diff --git a/.github/workflows/contributor-welcome.yml b/.github/workflows/contributor-welcome.yml index b32134358628..a033922b8ecc 100644 --- a/.github/workflows/contributor-welcome.yml +++ b/.github/workflows/contributor-welcome.yml @@ -15,7 +15,7 @@ jobs: if: >- github.event.pull_request.user.type == 'User' && contains(fromJSON('["FIRST_TIME_CONTRIBUTOR","FIRST_TIMER","NONE"]'), github.event.pull_request.author_association) - runs-on: ubuntu-24.04 # github-hosted-required: trusted pull-request-write token + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: trusted pull-request-write token; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted timeout-minutes: 5 permissions: contents: read diff --git a/.github/workflows/labels-sync.yml b/.github/workflows/labels-sync.yml index 5fdbf3100789..bc642d2b175a 100644 --- a/.github/workflows/labels-sync.yml +++ b/.github/workflows/labels-sync.yml @@ -35,7 +35,7 @@ jobs: apply: if: github.event_name != 'pull_request' - runs-on: ubuntu-24.04 # github-hosted-required: trusted issue-write token + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: trusted issue-write token; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted timeout-minutes: 5 permissions: contents: read diff --git a/.github/workflows/merge-group-fail-fast.yml b/.github/workflows/merge-group-fail-fast.yml index ba47dcacd87e..421050f18a54 100644 --- a/.github/workflows/merge-group-fail-fast.yml +++ b/.github/workflows/merge-group-fail-fast.yml @@ -11,7 +11,7 @@ name: Merge-group fail fast # always comes from the default branch, and a queued pull request cannot change # it. The source workflow runs only for merge groups, so ordinary pull-request CI # never creates a skipped fail-fast run. This workflow checks out nothing and -# runs on a GitHub-hosted runner. +# runs on an ephemeral Blacksmith runner through the CI_TRUSTED_RUNNER selector. on: workflow_run: workflows: [Merge-group policy checks] @@ -32,7 +32,7 @@ env: jobs: watch: name: watch merge group run - runs-on: ubuntu-24.04 # github-hosted-required + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: trusted token; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted timeout-minutes: 360 permissions: actions: write diff --git a/.github/workflows/merge-receipt.yml b/.github/workflows/merge-receipt.yml index c57117dbec5c..e27f122a97c8 100644 --- a/.github/workflows/merge-receipt.yml +++ b/.github/workflows/merge-receipt.yml @@ -16,7 +16,7 @@ permissions: {} jobs: receipt: if: github.event.pull_request.merged == true - runs-on: ubuntu-24.04 # github-hosted-required: trusted pull-request-write token + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: trusted pull-request-write token; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted timeout-minutes: 5 permissions: contents: read diff --git a/.github/workflows/pr-media.yml b/.github/workflows/pr-media.yml index de60f33f0019..a81d8d1c9a77 100644 --- a/.github/workflows/pr-media.yml +++ b/.github/workflows/pr-media.yml @@ -53,7 +53,7 @@ jobs: plan: name: Pick tours if: ${{ github.repository_owner == 'manaflow-ai' && (github.event_name == 'workflow_dispatch' || github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.head_repository.full_name == github.repository) }} - runs-on: ubuntu-24.04 # github-hosted-required: reads the pull request and mostly waits; keeps CI's Linux pool free + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: reads the pull request and mostly waits; keeps CI's Linux pool free; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted # CI has completed, so its reads return at once. timeout-minutes: 10 permissions: @@ -113,7 +113,7 @@ jobs: name: Tour ${{ matrix.tour }} needs: plan if: ${{ needs.plan.outputs.run != '' && needs.plan.outputs.run != '[]' }} - runs-on: ubuntu-24.04 # github-hosted-required: holds actions write and mostly waits for the tour run + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: holds actions write and mostly waits for the tour run; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted # CI's build is done, so a tour waits only for its run # (pr_media.RUN_WAIT_SECONDS, 90 minutes, most of it queueing). timeout-minutes: 120 @@ -225,7 +225,7 @@ jobs: name: Post media needs: [plan, tour] if: ${{ !cancelled() && needs.plan.result == 'success' && needs.plan.outputs.tours != '' && needs.plan.outputs.tours != '[]' }} - runs-on: ubuntu-24.04 # github-hosted-required: trusted contents and pull-request write token + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: trusted contents and pull-request write token; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted timeout-minutes: 10 concurrency: group: pr-media-publish-${{ needs.plan.outputs.pr }} diff --git a/.github/workflows/relay-publish-npm.yml b/.github/workflows/relay-publish-npm.yml index fb7eb1ab2c5b..6e491011c550 100644 --- a/.github/workflows/relay-publish-npm.yml +++ b/.github/workflows/relay-publish-npm.yml @@ -374,6 +374,7 @@ jobs: smoke: name: smoke published launcher (${{ matrix.os }}) + if: github.repository_owner == 'manaflow-ai' needs: - version - publish @@ -381,9 +382,9 @@ jobs: fail-fast: false matrix: include: - - os: ubuntu-latest + - os: blacksmith-4vcpu-ubuntu-2404 package: cmux-relay-linux-x64 - - os: macos-14 + - os: blacksmith-6vcpu-macos-15 package: cmux-relay-darwin-arm64 runs-on: ${{ matrix.os }} timeout-minutes: 15 diff --git a/.github/workflows/resolve-runners.yml b/.github/workflows/resolve-runners.yml index 0e075ede8219..341a21e06864 100644 --- a/.github/workflows/resolve-runners.yml +++ b/.github/workflows/resolve-runners.yml @@ -46,9 +46,10 @@ jobs: resolve: name: Resolve runner capabilities # Not a variable and not the map: the job that resolves the fleet cannot - # depend on the fleet. A Blacksmith fallback here queued forever on a fork, - # the one case this workflow exists for. GitHub-hosted Linux runs anywhere. - runs-on: ubuntu-24.04 # github-hosted-required: resolves the fleet, so cannot run on one + # depend on the fleet. A fork takes the GitHub-hosted owner branch (no + # Blacksmith there); manaflow-ai runs on Blacksmith so that a GitHub + # billing block cannot stop the graph that this job starts. + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: resolves the fleet, so reads no runner variable; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted timeout-minutes: 5 outputs: map: ${{ steps.resolve.outputs.map }} diff --git a/.github/workflows/triage-radar.yml b/.github/workflows/triage-radar.yml index 78ac41a04303..7f8f3be6ff91 100644 --- a/.github/workflows/triage-radar.yml +++ b/.github/workflows/triage-radar.yml @@ -19,7 +19,7 @@ jobs: contents: read issues: write pull-requests: read - runs-on: ubuntu-24.04 # github-hosted-required: trusted issue-write token + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || contains(fromJSON('["ubuntu-24.04","blacksmith-2vcpu-ubuntu-2404","blacksmith-4vcpu-ubuntu-2404"]'), vars.CI_TRUSTED_RUNNER) && vars.CI_TRUSTED_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }} # ephemeral-required: trusted issue-write token; CI_TRUSTED_RUNNER picks Blacksmith (default) or GitHub-hosted timeout-minutes: 5 steps: - name: Check out trusted radar code diff --git a/.github/workflows/web-complexity.yml b/.github/workflows/web-complexity.yml index fe6f179d2a3e..203f25884633 100644 --- a/.github/workflows/web-complexity.yml +++ b/.github/workflows/web-complexity.yml @@ -24,8 +24,8 @@ jobs: complexity: name: Web complexity candidate # Pull requests execute contributor-controlled package install scripts. Keep - # those runs on an ephemeral GitHub-hosted runner. - runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || (github.event_name == 'pull_request' && 'ubuntu-latest' || vars.LINUX_RUNNER || 'blacksmith-4vcpu-ubuntu-2404') }} + # those runs on an ephemeral Blacksmith runner, never a runner variable. + runs-on: ${{ github.repository_owner != 'manaflow-ai' && 'ubuntu-24.04' || (github.event_name == 'pull_request' && 'blacksmith-4vcpu-ubuntu-2404' || vars.LINUX_RUNNER || 'blacksmith-4vcpu-ubuntu-2404') }} timeout-minutes: 10 steps: - name: Checkout diff --git a/docs/ci-runners.md b/docs/ci-runners.md index bc92c44f9b47..28790c732bf5 100644 --- a/docs/ci-runners.md +++ b/docs/ci-runners.md @@ -40,8 +40,9 @@ that takes effect on the next workflow run. Linux uses Blacksmith. macOS uses Blacksmith cloud runners, plus the owned glaeda minis for the lanes the pool picker routes to them. WarpBuild is paid overflow and is -not a steady state for any lane. Non-urgent macOS work also uses free -GitHub-hosted runners through the background lane described below. +not a steady state for any lane. No job in `manaflow-ai` selects a +GitHub-hosted runner, so a GitHub billing block or hosted outage cannot stop CI; +see "Guard" for the few jobs that must stay GitHub-hosted and why. **The table below is the intended steady state, not a live readout.** Repository variables drift, and a stale table is worse than no table. For what is actually @@ -65,7 +66,7 @@ gh variable list --repo manaflow-ai/cmux | `MACOS_RUNNER_DISPLAY` | macOS GUI, XCUITest, and virtual-display tests (`tests-build-and-lag`) | `blacksmith-6vcpu-macos-15` | `blacksmith-6vcpu-macos-15` | | `MACOS_RUNNER_IOS` | the iOS image: simulator tests, TestFlight upload, and `ios-streamed-validate.yml` (`test-ios.yml`, `ios-testflight.yml`) | `blacksmith-6vcpu-macos-26` | `blacksmith-6vcpu-macos-26` | | `CI_PAID_MACOS_OVERFLOW` | the repository-side switch for metered capacity; gates the four paid-overflow variables above (see "Break-glass" below) | unset (free capacity) | unset means the Blacksmith fallback wins | -| `MACOS_RUNNER_BACKGROUND` | non-urgent macOS work only: `build-ghosttykit` and the macOS legs of `cmux-tui-artifacts` (post-merge). See "Background lane" below | unset | `macos-15` (GitHub-hosted, free) | +| `MACOS_RUNNER_BACKGROUND` | non-urgent macOS work only: `build-ghosttykit` and the macOS legs of `cmux-tui-artifacts` (post-merge). See "Background lane" below | `blacksmith-6vcpu-macos-15` | `blacksmith-6vcpu-macos-15` | A runner variable names a **machine capability** — an OS version, a GUI, a simulator, both SDKs, or a larger instance — and every job needing that @@ -688,8 +689,8 @@ contributor can start in the base repository's context (`pull_request_target`, `issue_comment`, `issues`, `pull_request_review`, `pull_request_review_comment`) cannot use this branch: `pull_request_target` carries a write token, and a comment event does not say whether the pull request comes from a fork. Their -jobs pin a literal GitHub-hosted label instead, or use the exact `CI_TRUSTED_RUNNER` -selector above, which can only pick an ephemeral label, and read no other runner variable. +jobs use the `CI_TRUSTED_RUNNER` selector (an ephemeral Blacksmith VM by +default) and read no other runner variable. The guard parses each expression rather than matching text, so this branch nested under another condition (for example the paid-overflow switch) does not count. @@ -730,19 +731,10 @@ xcframework build), and the two macOS Rust legs of `cmux-tui-build-package.yml`; release and full-suite callers keep their own runner). -The fallback is `macos-15`, never `macos-26`: the self-hosted fleet carries a -`macos-26` label and GitHub prefers a matching self-hosted runner. The -`macos-15` image ships Xcode 26.3 (macOS 26.2 SDK) next to its 16.4 default, so -jobs that pin `CMUX_CI_XCODE_APP_MACOS_15` resolve there too. - -An admin can repoint the whole lane with one variable edit, for example back -to Blacksmith if GitHub's macOS queue is ever the slower one: - -```bash -gh variable set MACOS_RUNNER_BACKGROUND --repo manaflow-ai/cmux -b blacksmith-6vcpu-macos-15 -``` - -Leaving it unset is the intended state. +The fallback is `blacksmith-6vcpu-macos-15`, behind the fork branch (a fork +gets GitHub-hosted `macos-26`). It was GitHub-hosted `macos-15` until +2026-10; a GitHub billing block stopped that lane, so it moved to Blacksmith. +An admin can repoint the whole lane with one variable edit. ## Owned Macs for pull request compiles @@ -1034,13 +1026,22 @@ runs. These choices are available only through `workflow_dispatch`. ## Guard `tests/test_ci_self_hosted_guard.sh` (run by the `workflow-guard-tests` job) -asserts that no job pins a bare GitHub-hosted runner (`ubuntu-*` / `macos-NN`): -every job must route through a runner repo variable so the overflow switch stays -a single variable flip. A GitHub-hosted macOS label may appear only as the -`MACOS_RUNNER_BACKGROUND` fallback (`vars.MACOS_RUNNER_BACKGROUND || 'macos-15'`) -in a workflow with no pull request, merge-queue or `workflow_call` trigger, -apart from the pinned macOS 14 / Intel compatibility legs in -`ci-macos-compat.yml` and `relay-publish-npm.yml`. It also asserts every paid macOS job references +asserts (`check_no_github_hosted_runners`) that no runner-selection position +names a GitHub-hosted label (`ubuntu-*`, `macos-*`, `windows-*`), including +matrix values, dispatch defaults and `*RUNNER*` keys, and that the manaflow-ai +fleet in `.github/runners.json` names none. A `# github-hosted-required:` +comment is not an exemption. Allowed: the fork branch +(`github.repository_owner != 'manaflow-ai' && '