diff --git a/.github/workflows/build-exe-for-python-sdk.yml b/.github/workflows/build-exe-for-python-sdk.yml index 49866ba5ea..7df809d414 100644 --- a/.github/workflows/build-exe-for-python-sdk.yml +++ b/.github/workflows/build-exe-for-python-sdk.yml @@ -63,7 +63,9 @@ jobs: plan: name: plan targets if: inputs.ci || inputs.release || github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest + runs-on: >- + ${{ inputs.ci && !inputs.release && vars.DSH_CI_FAILOVER_LINUX == 'blacksmith' + && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }} timeout-minutes: 5 outputs: matrix: ${{ steps.plan.outputs.matrix }} @@ -123,7 +125,9 @@ jobs: sdk-wheel: needs: plan name: deepseek_harness_sdk-${{ needs.plan.outputs.version }}-py3-none-any.whl - runs-on: ubuntu-latest + runs-on: >- + ${{ inputs.ci && !inputs.release && vars.DSH_CI_FAILOVER_LINUX == 'blacksmith' + && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }} timeout-minutes: 5 steps: - uses: actions/checkout@v6 @@ -151,7 +155,12 @@ jobs: build: needs: [plan, sdk-wheel] name: ${{ matrix.target }} - runs-on: ${{ matrix.runner }} + runs-on: >- + ${{ inputs.ci && !inputs.release && matrix.target == 'node24-linux-x64' + && vars.DSH_CI_FAILOVER_LINUX == 'blacksmith' && 'blacksmith-16vcpu-ubuntu-2404' + || inputs.ci && !inputs.release && matrix.target == 'node24-win-x64' + && vars.DSH_CI_FAILOVER_WINDOWS == 'blacksmith' && 'blacksmith-16vcpu-windows-2025' + || matrix.runner }} timeout-minutes: 45 strategy: fail-fast: false diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 3b7ff878f8..5bc604e7a0 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -53,7 +53,9 @@ env: jobs: e2e: - runs-on: ubuntu-latest + runs-on: >- + ${{ vars.DSH_CI_FAILOVER_LINUX == 'blacksmith' + && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }} name: e2e # Run on every trusted event. Skip untrusted PRs (forks + Dependabot) where # the secret is withheld — they would otherwise hard-fail the preflight. diff --git a/scripts/ci-workflow.spec.ts b/scripts/ci-workflow.spec.ts index 5a7cb17fd8..a1db3a3ba1 100644 --- a/scripts/ci-workflow.spec.ts +++ b/scripts/ci-workflow.spec.ts @@ -4,6 +4,11 @@ import { runInNewContext } from 'node:vm' import * as yaml from 'js-yaml' import { describe, expect, it } from 'vitest' +function evaluateRunsOn(selector: unknown, context: Record): unknown { + if (typeof selector !== 'string') throw new TypeError('Runner selector must be a string') + return runInNewContext(selector.trim().slice(3, -2), context, { timeout: 1000 }) +} + const root = resolve(import.meta.dirname, '..') const runnerPrivatePnpmDestination = /^\$\{\{ runner\.temp \}\}\/setup-pnpm-\$\{\{ github\.run_id \}\}-\$\{\{ github\.run_attempt \}\}$/ const nativeWindowsPnpmDestination = '${{ runner.temp }}/setup-pnpm-js-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.job }}' @@ -329,12 +334,11 @@ describe('CI workflow', () => { windows: windowsBuild['runs-on'] as string, } const evaluate = (expression: string, vars: Record, login = 'maintainer'): unknown => { - const body = expression.trim().slice(3, -2) - return runInNewContext(body, { + return evaluateRunsOn(expression, { vars, fromJSON: JSON.parse, github: { event: { pull_request: { user: { login } } } }, - }, { timeout: 1000 }) + }) } for (const [name, selector, variable, pool, hosted] of [ ['linux gates', selectors.linux, 'DSH_CI_FAILOVER_LINUX', ['self-hosted', 'linux', 'x64', 'vm-backup'], 'dsh-ubuntu-24-04-16core'], @@ -580,6 +584,50 @@ describe('CI workflow', () => { }) }) +describe('Runtime and LLM e2e Blacksmith routing', () => { + it('routes DeepSeek e2e only through the Linux Blacksmith switch', () => { + const job = workflowJob(loadWorkflow('.github/workflows/e2e.yml'), 'e2e') + for (const mode of ['', 'selfhosted', 'unexpected', 'blacksmith']) { + expect(evaluateRunsOn(job['runs-on'], { vars: { DSH_CI_FAILOVER_LINUX: mode, DSH_CI_FAILOVER_WINDOWS: 'blacksmith' } })) + .toBe(mode === 'blacksmith' ? 'blacksmith-4vcpu-ubuntu-2404' : 'ubuntu-latest') + } + }) + + it('keeps native release and dispatch builders hosted while routing x64 CI by platform', () => { + const workflow = loadWorkflow('.github/workflows/build-exe-for-python-sdk.yml') + const build = workflowJob(workflow, 'build') + for (const [target, runner, variable, blacksmith] of [ + ['node24-linux-x64', 'ubuntu-latest', 'DSH_CI_FAILOVER_LINUX', 'blacksmith-16vcpu-ubuntu-2404'], + ['node24-win-x64', 'windows-2025', 'DSH_CI_FAILOVER_WINDOWS', 'blacksmith-16vcpu-windows-2025'], + ['node24-linux-arm64', 'ubuntu-24.04-arm', 'DSH_CI_FAILOVER_LINUX', 'ubuntu-24.04-arm'], + ['node24-macos-arm64', 'macos-latest', 'DSH_CI_FAILOVER_LINUX', 'macos-latest'], + ['node24-macos-x64', 'macos-15-intel', 'DSH_CI_FAILOVER_LINUX', 'macos-15-intel'], + ] as const) { + for (const ci of [false, true]) { + for (const release of [false, true]) { + for (const mode of ['', 'selfhosted', 'unexpected', 'blacksmith']) { + const vars = { DSH_CI_FAILOVER_LINUX: 'blacksmith', DSH_CI_FAILOVER_WINDOWS: 'blacksmith', [variable]: mode } + expect(evaluateRunsOn(build['runs-on'], { inputs: { ci, release }, vars, matrix: { target, runner } }), `${target} ci=${ci} release=${release} mode=${mode}`) + .toBe(ci && !release && mode === 'blacksmith' ? blacksmith : runner) + } + } + } + } + }) + + it.each(['plan', 'sdk-wheel'])('routes runtime %s only for non-release CI', (name) => { + const job = workflowJob(loadWorkflow('.github/workflows/build-exe-for-python-sdk.yml'), name) + for (const ci of [false, true]) { + for (const release of [false, true]) { + for (const mode of ['', 'selfhosted', 'unexpected', 'blacksmith']) { + expect(evaluateRunsOn(job['runs-on'], { inputs: { ci, release }, vars: { DSH_CI_FAILOVER_LINUX: mode } })) + .toBe(ci && !release && mode === 'blacksmith' ? 'blacksmith-4vcpu-ubuntu-2404' : 'ubuntu-latest') + } + } + } + }) +}) + describe('DeepSeek e2e workflow', () => { it('prepares bubblewrap from the pinned payload without a package transaction', () => { const workflow = loadWorkflow('.github/workflows/e2e.yml')