One semantic conflict on the `all-checks-passed` verdict job. Master moved it to `ubuntu-latest`; this branch had routed it through the failover selector so a hosted-pool outage could not leave the required verdict queued. Master's resolution satisfies that requirement more directly: standard hosted capacity is independent of both custom pools, so the verdict is reachable whichever pool is degraded, and the selector is no longer needed. Kept `ubuntu-latest` and folded the failover reasoning into its comment. The failover runbook accordingly documents three failover jobs (the three required Linux workers), not four, and states why the verdict stays on standard hosted capacity in both states.
799 lines
30 KiB
YAML
799 lines
30 KiB
YAML
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; writer-manageable
|
||
# repository state — 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'
|
||
&& github.event.pull_request.user.login != 'dependabot[bot]'
|
||
&& 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' || github.event.pull_request.user.login == 'dependabot[bot]'
|
||
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'
|
||
&& github.event.pull_request.user.login != 'dependabot[bot]'
|
||
&& 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' && github.event.pull_request.user.login != 'dependabot[bot]' && '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' || github.event.pull_request.user.login == 'dependabot[bot]'
|
||
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'
|
||
&& github.event.pull_request.user.login != 'dependabot[bot]'
|
||
&& 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' && github.event.pull_request.user.login != 'dependabot[bot]' && '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' || github.event.pull_request.user.login == 'dependabot[bot]'
|
||
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 writer-manageable
|
||
# DSH_CI_FAILOVER variable — see the failover runbook, no merge required).
|
||
# Push-triggered, so this lane always executes the base branch's own
|
||
# workflow definition. (Under failover, pull_request jobs do reach these
|
||
# runners with the PR merge ref's workflow — the boundary there is
|
||
# repository membership: private, forking disabled, Dependabot excluded.)
|
||
# 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:
|
||
# Full history + DSH_ARCHIVE_BASE_REF below: same frozen-archive
|
||
# comparison as serial-linux. Depth 2 would miss github.event.before
|
||
# on multi-commit or force pushes; full fetch is cheap here because
|
||
# checkout resolves against the VM's local mirror.
|
||
- uses: actions/checkout@v6
|
||
with:
|
||
fetch-depth: 0
|
||
|
||
- 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_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
|
||
|
||
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
|
||
# This bookkeeping-only verdict must not depend on custom-pool provisioning.
|
||
# Standard hosted capacity is also what keeps it out of the failover path:
|
||
# pinned to either custom pool, an outage of that pool would leave the
|
||
# branch-protection verdict queued forever even after every worker job
|
||
# passed on the other pool.
|
||
runs-on: ubuntu-latest
|
||
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, ', ') }})"
|