Files
deepseek-harness/.github/workflows/ci.yml
T
Chinesezjc fe246e4a0a ci: failover round — aggregate follows the selector, tighter shared-VM bounds
- all-checks-passed now resolves its pool through the same
  DSH_CI_FAILOVER expression as the worker jobs it aggregates.
  Pinned to the hosted pool it would leave the branch-protection
  verdict queued on the failed pool after every failover job passed —
  observed live during the 2026-07-27 outage as a required check
  looping against dead capacity.
- Coverage worker bound under failover drops 12 → 8 and snapshot
  concurrency 16 → 12: the pool now runs six always-on instances (the
  spare tier was retired), so worst case is 6 × 8 = 48 coverage
  workers on the shared 64-core VM.
2026-07-27 15:17:48 +08:00

788 lines
29 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: CI
on:
push:
branches: [master]
pull_request:
workflow_dispatch:
inputs:
suite:
description: Manual CI suite to run
required: true
default: larger-runner-benchmark
type: choice
options:
- larger-runner-benchmark
- consolidated-runner-benchmark
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
env:
PRIMARY_NODE_VERSION: '24'
jobs:
# Three enterprise jobs isolate coverage, static analysis, and the
# build-backed consumer tail. The static job publishes its exact build so
# consumers do not repeat the longest part of their critical path.
#
# FAILOVER: each Linux enterprise job resolves its pool through the
# DSH_CI_FAILOVER repository variable. Unset (normal), the expressions
# pick the hosted enterprise pools below. Setting the variable to
# 'selfhosted' (repo Settings → Actions → Variables; admin-only, not
# PR-editable, no merge required) retargets all three onto the in-house
# vm-backup pool and re-running the failed jobs is the entire switch —
# see .agents/notes/implemented/process/2026-07-26-ci-failover-runbook.md. The
# in-house pool's readiness is re-proven on every master push by the
# serial-linux-selfhosted standby lane below.
node-24:
if: github.event_name == 'pull_request'
runs-on: >-
${{ vars.DSH_CI_FAILOVER == 'selfhosted'
&& fromJSON('["self-hosted", "linux", "x64", "vm-backup"]')
|| 'dsh-enterprise-ubuntu-latest-32core-test' }}
name: node 24 / static
env:
DSH_GATE_CONCURRENCY: '8'
steps:
# Fetch complete history so the archive gate can read the trusted PR base from a reused shallow checkout.
- uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: false
# Pull requests consume the default-branch cache but do not put cache
# compression and upload on the paid latency-critical path. Skipped
# under failover — see the coverage lane's identical rationale.
- uses: actions/cache/restore@v4
if: vars.DSH_CI_FAILOVER != 'selfhosted'
with:
path: /home/runner/.local/share/pnpm/store/v11
key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
- uses: actions/setup-node@v6
with:
node-version: ${{ env.PRIMARY_NODE_VERSION }}
- name: Enable corepack and install dependencies
run: |
corepack enable
pnpm install --frozen-lockfile
- name: Run static gates
env:
DSH_ARCHIVE_BASE_REF: ${{ github.event.pull_request.base.sha }}
run: pnpm run check:ci:static
- name: Pack built tree
run: >-
tar -czf "$RUNNER_TEMP/node-24-built-tree.tar.gz"
apps/*/lib packages/*/*/lib vendor/*/lib
- uses: actions/upload-artifact@v6
with:
name: node-24-built-tree
path: ${{ runner.temp }}/node-24-built-tree.tar.gz
if-no-files-found: error
retention-days: 1
compression-level: 0
node-24-coverage:
if: github.event_name == 'pull_request'
runs-on: >-
${{ vars.DSH_CI_FAILOVER == 'selfhosted'
&& fromJSON('["self-hosted", "linux", "x64", "vm-backup"]')
|| 'dsh-enterprise-ubuntu-24-04-32core-test' }}
name: node 24 / coverage
env:
# Failover shrinks the worker bound: the hosted 32-core runner is
# exclusive to one job, but the failover pool shares one 64-core VM
# across six always-on runner instances, and the timing-sensitive
# process suites have documented aggregate-contention failures.
# 8 × 6 instances = 48 workers worst case on 64 cores.
DSH_COVERAGE_MAX_WORKERS: ${{ vars.DSH_CI_FAILOVER == 'selfhosted' && '8' || '24' }}
DSH_GATE_CONCURRENCY: '8'
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
# Skipped under failover: the self-hosted VM's persistent pnpm store
# serves warm installs directly, and this hosted-path restore would
# spend ~52 s pulling ~180 MB into a path pnpm never reads there.
- uses: actions/cache/restore@v4
if: vars.DSH_CI_FAILOVER != 'selfhosted'
with:
path: /home/runner/.local/share/pnpm/store/v11
key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
- uses: actions/setup-node@v6
with:
node-version: ${{ env.PRIMARY_NODE_VERSION }}
- name: Enable corepack, install dependencies, and prepare bubblewrap
run: |
corepack enable
pnpm install --frozen-lockfile &
install_pid=$!
bash scripts/prepare-ci-bubblewrap.sh &
sandbox_pid=$!
install_status=0
wait "$install_pid" || install_status=$?
sandbox_status=0
wait "$sandbox_pid" || sandbox_status=$?
if (( install_status != 0 )); then exit "$install_status"; fi
exit "$sandbox_status"
- name: Run exhaustive coverage
run: pnpm run check:ci:coverage
node-24-consumers:
needs: node-24
if: github.event_name == 'pull_request'
runs-on: >-
${{ vars.DSH_CI_FAILOVER == 'selfhosted'
&& fromJSON('["self-hosted", "linux", "x64", "vm-backup"]')
|| 'dsh-enterprise-ubuntu-latest-32core-test' }}
name: node 24 / snapshots and artifacts
env:
DSH_ESLINT_CACHE: '1'
DSH_ESLINT_CONCURRENCY: '8'
DSH_GATE_CONCURRENCY: '8'
DSH_NODE_COMPAT_SKIP_TYPECHECK: '1'
DSH_PUBLINT_CONCURRENCY: '8'
# Failover halves snapshot concurrency for the shared 64-core VM.
DSH_SNAPSHOT_MAX_CONCURRENCY: ${{ vars.DSH_CI_FAILOVER == 'selfhosted' && '12' || '32' }}
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: actions/download-artifact@v8
with:
name: node-24-built-tree
path: ${{ runner.temp }}
- name: Restore built tree
run: tar -xzf "$RUNNER_TEMP/node-24-built-tree.tar.gz"
# Skipped under failover — see the coverage lane's identical rationale.
- uses: actions/cache/restore@v4
if: vars.DSH_CI_FAILOVER != 'selfhosted'
with:
path: /home/runner/.local/share/pnpm/store/v11
key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
- uses: actions/cache/restore@v4
with:
path: .cache/eslint
key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-full-${{ hashFiles('pnpm-lock.yaml', 'eslint.config.mjs', 'tsconfig.json', 'tsconfig.base.json', 'tsconfig.base.client.json', 'tsconfig.host.json', 'tsconfig.client.json', 'packages/*/*/tsconfig.json', 'examples/*/tsconfig.json') }}
restore-keys: |
${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-full-
- uses: actions/setup-node@v6
with:
node-version: ${{ env.PRIMARY_NODE_VERSION }}
- name: Enable corepack, install dependencies, and prepare bubblewrap
run: |
corepack enable
pnpm install --frozen-lockfile &
install_pid=$!
bash scripts/prepare-ci-bubblewrap.sh &
sandbox_pid=$!
install_status=0
wait "$install_pid" || install_status=$?
sandbox_status=0
wait "$sandbox_pid" || sandbox_status=$?
if (( install_status != 0 )); then exit "$install_status"; fi
exit "$sandbox_status"
- name: Run compatibility, snapshot, and artifact gates
run: |
pnpm run check:ci:lint &
lint_pid=$!
pnpm run check:node-compat &
compat_pid=$!
DSH_EXAMPLE_MODE=lib pnpm run test:snapshot &
snapshot_pid=$!
pnpm run publint &
publint_pid=$!
pnpm run verify-node-next-types &
node_next_pid=$!
pnpm run verify-built-package-invariants &
built_invariants_pid=$!
DSH_EXAMPLE_MODE=lib pnpm exec vitest run --config vitest.e2e.config.ts \
examples/headless-agent/tests/keyless-smoke.e2e.ts \
examples/tui-agent/tests/tui-keyless-smoke.e2e.ts \
packages/examples/cli-demo/tests/built-bin.e2e.ts \
packages/examples/acp-demo/tests/built-bin.e2e.ts \
packages/ui/jsonrpc/tests/built-scope-carrier.e2e.ts \
packages/workflow/workflow-workerthread/tests/built-worker.e2e.ts \
packages/code-runtime/code-runtime-worker/tests/built-lib.e2e.ts &
built_bin_pid=$!
final_status=0
capture_status() {
local child_status=0
wait "$1" || child_status=$?
if (( final_status == 0 && child_status != 0 )); then
final_status=$child_status
fi
}
for child_pid in \
"$lint_pid" "$compat_pid" "$snapshot_pid" \
"$publint_pid" "$node_next_pid" "$built_invariants_pid" "$built_bin_pid"
do
capture_status "$child_pid"
done
exit "$final_status"
node-compat:
if: github.event_name == 'pull_request'
# Each compatibility contract receives an independent standard hosted job.
runs-on: ${{ matrix.runner }}
name: ${{ matrix.name }}
env:
DSH_GATE_CONCURRENCY: ${{ matrix.gate_concurrency }}
DSH_NODE_COMPAT_SKIP_TYPECHECK: '1'
strategy:
fail-fast: false
matrix:
include:
- node: '22.19'
name: node 22.19
runner: ubuntu-latest
gate_concurrency: '1'
- node: 26
name: node 26
runner: ubuntu-latest
gate_concurrency: '1'
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node }}
- name: Enable corepack and resolve pnpm store path
id: pnpm-store
run: |
corepack enable
echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
- uses: actions/cache@v4
with:
path: ${{ steps.pnpm-store.outputs.path }}
key: ${{ runner.os }}-node-${{ matrix.node }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-node-${{ matrix.node }}-pnpm-
- name: Install (immutable)
run: pnpm install --frozen-lockfile
- name: Run compatibility smokes
run: pnpm run check:node-compat
python-sdk:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
name: python 3.10 / keyless SDK
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: '3.10'
cache: pip
- name: Install uv
run: python -m pip install uv==0.11.23
- name: Run complete keyless Python suite
run: uv run --python 3.10 --group test --project python/sdk pytest
# One standard Windows box shares setup across the required build/site checks
# and the observational portability inventory. Serial worker bounds keep this
# recovery path portable; Linux owns duplicate lint, coverage, and snapshots.
windows:
if: github.event_name == 'pull_request'
runs-on: windows-2025
name: windows node 24 / complete
env:
DSH_COVERAGE_MAX_WORKERS: '1'
DSH_GATE_CONCURRENCY: '1'
DSH_PUBLINT_CONCURRENCY: '1'
steps:
- uses: actions/checkout@v6
- name: Enable Developer Mode (symlink support)
shell: pwsh
run: >-
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
/t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1"
- uses: actions/setup-node@v6
with:
node-version: ${{ env.PRIMARY_NODE_VERSION }}
# Extracting the many-file pnpm store cache is slower than a clean install,
# and saving it adds more latency after gates.
- name: Enable corepack and install (immutable)
shell: pwsh
run: |
corepack enable
pnpm install --frozen-lockfile
- name: Run blocking and observational Windows gates concurrently
shell: pwsh
run: pnpm run check:ci:windows-complete
# Master pushes run only the serial reference jobs below.
# Each host executes the complete, unsharded primary Node aggregate with one
# gate worker, giving reviewers a simple cross-platform oracle for completeness
# and timing.
serial-linux:
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
name: serial / linux
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 2
- uses: actions/setup-node@v6
with:
node-version: ${{ env.PRIMARY_NODE_VERSION }}
- name: Enable corepack and resolve pnpm store path
id: pnpm-store
run: |
corepack enable
echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
# Master refreshes the caches that pull requests restore without saving.
- uses: actions/cache@v4
with:
path: ${{ steps.pnpm-store.outputs.path }}
key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
- uses: actions/cache@v4
with:
path: .cache/eslint
key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-full-${{ hashFiles('pnpm-lock.yaml', 'eslint.config.mjs', 'tsconfig.json', 'tsconfig.base.json', 'tsconfig.base.client.json', 'tsconfig.host.json', 'tsconfig.client.json', 'packages/*/*/tsconfig.json', 'examples/*/tsconfig.json') }}
restore-keys: |
${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-full-
- name: Install (immutable)
run: pnpm install --frozen-lockfile
- name: Prepare bubblewrap (unrestrict userns)
run: bash scripts/prepare-ci-bubblewrap.sh
- name: Run complete unsharded primary Node CI serially
env:
DSH_ARCHIVE_BASE_REF: ${{ github.event.before }}
DSH_COVERAGE_MAX_WORKERS: '1'
DSH_E2E_MAX_WORKERS: '1'
DSH_ESLINT_CACHE: '1'
DSH_GATE_CONCURRENCY: '1'
DSH_PUBLINT_CONCURRENCY: '1'
DSH_SNAPSHOT_MAX_CONCURRENCY: '1'
run: pnpm run check:ci
# Hot-standby drill for the in-house self-hosted pool: every master move
# re-runs the complete unsharded aggregate on the persistent 64-core VM,
# continuously proving that environment can take over a required lane if
# the hosted pools degrade (the switch is then setting the admin-only
# DSH_CI_FAILOVER variable — see the failover runbook, no merge required).
# Push-triggered, so it always executes the base branch's own workflow
# definition — no PR-editable path selects these runners. Non-blocking for
# pull requests; no cache steps because the VM's persistent pnpm store and
# tool caches make them redundant (and saving here would poison the hosted
# cache namespace with self-hosted paths).
serial-linux-selfhosted:
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
name: serial / linux (self-hosted standby)
runs-on: [self-hosted, linux, x64, vm-backup]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${{ env.PRIMARY_NODE_VERSION }}
- name: Enable corepack (pnpm)
run: corepack enable
- name: Install (immutable)
run: pnpm install --frozen-lockfile
- name: Prepare bubblewrap (unrestrict userns)
run: bash scripts/prepare-ci-bubblewrap.sh
- name: Run complete unsharded primary Node CI serially
env:
DSH_COVERAGE_MAX_WORKERS: '1'
DSH_E2E_MAX_WORKERS: '1'
DSH_ESLINT_CACHE: '1'
DSH_GATE_CONCURRENCY: '1'
DSH_PUBLINT_CONCURRENCY: '1'
DSH_SNAPSHOT_MAX_CONCURRENCY: '1'
run: pnpm run check:ci
serial-macos:
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
name: serial / macos
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${{ env.PRIMARY_NODE_VERSION }}
- name: Enable corepack (pnpm)
run: corepack enable
- name: Install (immutable)
run: pnpm install --frozen-lockfile
- name: Run complete unsharded primary Node CI serially
env:
DSH_COVERAGE_MAX_WORKERS: '1'
DSH_E2E_MAX_WORKERS: '1'
DSH_GATE_CONCURRENCY: '1'
DSH_PUBLINT_CONCURRENCY: '1'
DSH_SNAPSHOT_MAX_CONCURRENCY: '1'
run: pnpm run check:ci
serial-windows:
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
name: serial / windows
runs-on: windows-2025
steps:
- uses: actions/checkout@v6
- name: Enable Developer Mode (symlink support)
shell: pwsh
run: >-
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
/t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1"
- uses: actions/setup-node@v6
with:
node-version: ${{ env.PRIMARY_NODE_VERSION }}
- name: Enable corepack (pnpm)
shell: pwsh
run: corepack enable
# Master refreshes the small cache that pull requests restore without
# putting package-store extraction back on the Windows critical path.
- uses: actions/cache@v4
with:
path: .cache/eslint
key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-full-${{ hashFiles('pnpm-lock.yaml', 'eslint.config.mjs', 'tsconfig.json', 'tsconfig.base.json', 'tsconfig.base.client.json', 'tsconfig.host.json', 'tsconfig.client.json', 'packages/*/*/tsconfig.json', 'examples/*/tsconfig.json') }}
restore-keys: |
${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-full-
- name: Install (immutable)
shell: pwsh
run: pnpm install --frozen-lockfile
- name: Run complete unsharded primary Node CI serially
shell: pwsh
env:
DSH_COVERAGE_MAX_WORKERS: '1'
DSH_E2E_MAX_WORKERS: '1'
DSH_ESLINT_CACHE: '1'
DSH_GATE_CONCURRENCY: '1'
DSH_PUBLINT_CONCURRENCY: '1'
DSH_SNAPSHOT_MAX_CONCURRENCY: '1'
run: pnpm run check:ci
# Manual, bounded comparison of the actual critical Linux and Windows lanes.
# The named pools are restricted at the organization level to this repository.
larger-runner-benchmark:
if: github.event_name == 'workflow_dispatch' && inputs.suite == 'larger-runner-benchmark'
runs-on: ${{ matrix.runner }}
timeout-minutes: 15
strategy:
fail-fast: false
max-parallel: 12
matrix:
include:
- platform: linux
cores: '4'
runner: dsh-ubuntu-24-04-4core
workload: typecheck
- platform: linux
cores: '8'
runner: dsh-ubuntu-24-04-8core
workload: typecheck
- platform: linux
cores: '16'
runner: dsh-ubuntu-24-04-16core
workload: typecheck
- platform: linux
cores: '32'
runner: dsh-ubuntu-24-04-32core
workload: typecheck
- platform: linux
cores: '64'
runner: dsh-ubuntu-24-04-64core
workload: typecheck
- platform: linux
cores: '96'
runner: dsh-ubuntu-24-04-96core
workload: typecheck
- platform: windows
cores: '4'
runner: dsh-windows-2025-4core
workload: production-site
- platform: windows
cores: '8'
runner: dsh-windows-2025-8core
workload: production-site
- platform: windows
cores: '16'
runner: dsh-windows-2025-16core
workload: production-site
- platform: windows
cores: '32'
runner: dsh-windows-2025-32core
workload: production-site
- platform: windows
cores: '64'
runner: dsh-windows-2025-64core
workload: production-site
- platform: windows
cores: '96'
runner: dsh-windows-2025-96core
workload: production-site
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${{ env.PRIMARY_NODE_VERSION }}
- name: Report runner capacity
run: >-
node -e "const os = require('node:os');
console.log(JSON.stringify({ arch: process.arch, cpus: os.cpus().length,
memoryGiB: Math.round(os.totalmem() / 2 ** 30) }))"
- name: Enable corepack (pnpm)
run: corepack enable
- name: Resolve pnpm store path
if: matrix.platform == 'linux'
id: pnpm-store
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
- uses: actions/cache@v4
if: matrix.platform == 'linux'
with:
path: ${{ steps.pnpm-store.outputs.path }}
key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
- name: Install (immutable)
run: pnpm install --frozen-lockfile
- name: Run critical Linux typecheck lane
if: matrix.platform == 'linux'
run: pnpm run typecheck
- name: Run critical Windows production-site lane
if: matrix.platform == 'windows'
run: pnpm run docs:build
# Manual comparison of the intended low-fanout topology. Linux runs the
# complete unsharded primary aggregate with bounded in-runner parallelism;
# Windows runs both blocking build surfaces concurrently through run-gates.
consolidated-runner-benchmark:
if: github.event_name == 'workflow_dispatch' && inputs.suite == 'consolidated-runner-benchmark'
runs-on: ${{ matrix.runner }}
timeout-minutes: 15
strategy:
fail-fast: false
max-parallel: 12
matrix:
include:
- platform: linux
cores: '4'
runner: dsh-ubuntu-24-04-4core
workers: '4'
- platform: linux
cores: '8'
runner: dsh-ubuntu-24-04-8core
workers: '8'
- platform: linux
cores: '16'
runner: dsh-ubuntu-24-04-16core
workers: '16'
- platform: linux
cores: '32'
runner: dsh-ubuntu-24-04-32core
workers: '32'
- platform: linux
cores: '64'
runner: dsh-ubuntu-24-04-64core
workers: '32'
- platform: linux
cores: '96'
runner: dsh-ubuntu-24-04-96core
workers: '32'
- platform: windows
cores: '4'
runner: dsh-windows-2025-4core
workers: '2'
- platform: windows
cores: '8'
runner: dsh-windows-2025-8core
workers: '2'
- platform: windows
cores: '16'
runner: dsh-windows-2025-16core
workers: '2'
- platform: windows
cores: '32'
runner: dsh-windows-2025-32core
workers: '2'
- platform: windows
cores: '64'
runner: dsh-windows-2025-64core
workers: '2'
- platform: windows
cores: '96'
runner: dsh-windows-2025-96core
workers: '2'
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${{ env.PRIMARY_NODE_VERSION }}
- name: Report runner capacity
run: >-
node -e "const os = require('node:os');
console.log(JSON.stringify({ arch: process.arch, cpus: os.cpus().length,
memoryGiB: Math.round(os.totalmem() / 2 ** 30) }))"
- name: Enable corepack (pnpm)
run: corepack enable
- name: Resolve pnpm store path (Linux)
if: matrix.platform == 'linux'
id: pnpm-store-linux
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
- name: Resolve pnpm store path (Windows)
if: matrix.platform == 'windows'
id: pnpm-store-windows
shell: pwsh
run: '"path=$(pnpm store path --silent)" >> $env:GITHUB_OUTPUT'
- uses: actions/cache@v4
with:
path: ${{ steps.pnpm-store-linux.outputs.path || steps.pnpm-store-windows.outputs.path }}
key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
- uses: actions/cache@v4
if: matrix.platform == 'linux'
with:
path: .cache/eslint
key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-full-${{ hashFiles('pnpm-lock.yaml', 'eslint.config.mjs', 'tsconfig.json', 'tsconfig.base.json', 'tsconfig.base.client.json', 'tsconfig.host.json', 'tsconfig.client.json', 'packages/*/*/tsconfig.json', 'examples/*/tsconfig.json') }}
restore-keys: |
${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-full-
- name: Install and prepare Linux
if: matrix.platform == 'linux'
run: |
pnpm install --frozen-lockfile &
install_pid=$!
bash scripts/prepare-ci-bubblewrap.sh &
sandbox_pid=$!
install_status=0
wait "$install_pid" || install_status=$?
sandbox_status=0
wait "$sandbox_pid" || sandbox_status=$?
if (( install_status != 0 )); then exit "$install_status"; fi
exit "$sandbox_status"
- name: Install (immutable)
if: matrix.platform == 'windows'
shell: pwsh
run: pnpm install --frozen-lockfile
- name: Run complete unsharded primary Node CI concurrently
if: matrix.platform == 'linux'
env:
DSH_COVERAGE_MAX_WORKERS: ${{ matrix.workers }}
DSH_ESLINT_CACHE: '1'
DSH_ESLINT_CONCURRENCY: ${{ matrix.workers }}
DSH_GATE_CONCURRENCY: ${{ matrix.workers }}
DSH_PUBLINT_CONCURRENCY: ${{ matrix.workers }}
DSH_SNAPSHOT_MAX_CONCURRENCY: ${{ matrix.workers }}
run: pnpm run check:ci
- name: Run blocking Windows builds concurrently
if: matrix.platform == 'windows'
shell: pwsh
env:
DSH_GATE_CONCURRENCY: ${{ matrix.workers }}
run: pnpm run check:ci:windows-blocking
# Single stable required check for branch protection: require "all checks
# passed" instead of enumerating matrix legs whose names change as lanes and
# node versions evolve. Every blocking job in THIS workflow must be listed in
# `needs`; observational Windows gates share the required Windows job but are
# marked non-blocking inside run-gates. (`needs` cannot reach across workflow
# files; e2e.yml stays its own check.)
# `if: always()` is load-bearing: without it a failed dependency
# would SKIP this job, and GitHub counts a skipped required check as passing
# — so this job always runs and fails on any non-success result, including
# 'cancelled' and 'skipped'.
all-checks-passed:
name: all checks passed
# The required verdict must not add a separate standard-hosted billing
# dependency — and it must follow the failover selector like the worker
# jobs it aggregates: if it stayed pinned to the hosted pool, every
# failover-passed run would still leave the branch-protection verdict
# queued forever on the failed pool.
runs-on: >-
${{ vars.DSH_CI_FAILOVER == 'selfhosted'
&& fromJSON('["self-hosted", "linux", "x64", "vm-backup"]')
|| 'dsh-enterprise-ubuntu-latest-32core-test' }}
needs: [node-24, node-24-coverage, node-24-consumers, node-compat, python-sdk, windows]
if: always() && github.event_name == 'pull_request'
steps:
- name: Fail if any needed job did not succeed
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped')
run: |
echo "::error::Needed job results: ${{ join(needs.*.result, ', ') }}"
exit 1
- name: All checks passed
run: echo "All needed jobs succeeded (${{ join(needs.*.result, ', ') }})"