mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
The required windows job moves from windows-2025 to ubuntu-latest, running checksum-verified Windows Node under Wine at Linux-job wall clock (2m46s warm vs 7-9min); master's serial-windows native-kernel reference is untouched, and a new master-only wine-apt-cache job seeds the apt cache every pull request restores. The experiment workflow folds into ci.yml, the Agent Note moves to implemented with measured results, and the two CI topology notes update to the shipped facts.
891 lines
33 KiB
YAML
891 lines
33 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.
|
|
node-24:
|
|
if: github.event_name == 'pull_request'
|
|
runs-on: 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.
|
|
- uses: actions/cache/restore@v4
|
|
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: dsh-enterprise-ubuntu-24-04-32core-test
|
|
name: node 24 / coverage
|
|
env:
|
|
DSH_COVERAGE_MAX_WORKERS: '24'
|
|
DSH_GATE_CONCURRENCY: '8'
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- uses: actions/cache/restore@v4
|
|
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: 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'
|
|
DSH_SNAPSHOT_MAX_CONCURRENCY: '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"
|
|
|
|
- uses: actions/cache/restore@v4
|
|
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
|
|
|
|
# The required pull-request Windows signal: the two blocking win32 surfaces
|
|
# (workspace build, production site) execute with real, checksum-verified
|
|
# Windows Node under Wine on standard hosted Linux. The master
|
|
# serial-windows job below keeps the complete native-kernel inventory —
|
|
# including the observational portability gates this lane does not run —
|
|
# on real windows-2025. Direct tool entrypoints stand in for pnpm's cmd
|
|
# shims, which a Linux-side install does not create; layout, fidelity
|
|
# limits, and measured timings live in
|
|
# .agents/notes/implemented/process/2026-07-27-wine-windows-gates-experiment.md
|
|
windows:
|
|
if: github.event_name == 'pull_request'
|
|
runs-on: ubuntu-latest
|
|
name: windows node 24 / wine blocking
|
|
timeout-minutes: 15
|
|
env:
|
|
WINEDEBUG: '-all'
|
|
WINEARCH: win64
|
|
# Skip Wine Mono / Gecko installers: Node needs neither.
|
|
WINEDLLOVERRIDES: 'mscoree,mshtml='
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: ${{ env.PRIMARY_NODE_VERSION }}
|
|
|
|
- uses: actions/cache/restore@v4
|
|
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-
|
|
|
|
# Master's wine-apt-cache job seeds the default-branch scope every pull
|
|
# request can read; a save from this job only reaches reruns of the
|
|
# same merge ref.
|
|
- name: Compose Wine apt cache key
|
|
id: wine-cache-key
|
|
run: echo "key=wine-debs-${ImageOS:-linux}-${ImageVersion:-v0}" >> "$GITHUB_OUTPUT"
|
|
|
|
- uses: actions/cache@v4
|
|
with:
|
|
path: ~/wine-debs
|
|
key: ${{ steps.wine-cache-key.outputs.key }}
|
|
|
|
- name: Install dependencies and provision Wine concurrently
|
|
run: |
|
|
corepack enable
|
|
|
|
# Windows-lane install-time overrides. supportedArchitectures
|
|
# additionally materializes the win32-x64 platform packages
|
|
# (@esbuild/win32-x64, rolldown and rollup MSVC bindings) the
|
|
# Windows toolchain resolves at runtime; nodeLinker: hoisted lays
|
|
# node_modules out flat with real files because Windows Node under
|
|
# Wine does not realpath pnpm's isolated-layout symlinks. Neither
|
|
# override is recorded in the lockfile, so --frozen-lockfile stays
|
|
# valid. --ignore-scripts skips Linux lifecycle scripts no gate in
|
|
# this lane loads; the win32 binaries ship prebuilt.
|
|
cat >> pnpm-workspace.yaml <<'EOF'
|
|
|
|
nodeLinker: hoisted
|
|
supportedArchitectures:
|
|
os: [current, win32]
|
|
cpu: [current, x64]
|
|
EOF
|
|
|
|
pnpm install --frozen-lockfile --ignore-scripts &
|
|
install_pid=$!
|
|
|
|
provision_wine() {
|
|
set -euo pipefail
|
|
# Wine from the apt cache when present; else download the full
|
|
# dependency closure once and keep it for the next run. The
|
|
# `wine` dispatcher package (not bare `wine64`) is what puts a
|
|
# binary on PATH.
|
|
if compgen -G "$HOME/wine-debs/*.deb" > /dev/null; then
|
|
sudo apt-get install -y --no-install-recommends "$HOME"/wine-debs/*.deb
|
|
else
|
|
sudo apt-get update
|
|
sudo apt-get install -y --no-install-recommends --download-only wine
|
|
mkdir -p "$HOME/wine-debs"
|
|
cp /var/cache/apt/archives/*.deb "$HOME/wine-debs/" 2>/dev/null || true
|
|
sudo apt-get install -y --no-install-recommends wine
|
|
fi
|
|
WINE_BIN=''
|
|
for candidate in "$(command -v wine || true)" "$(command -v wine64 || true)" /usr/lib/wine/wine64; do
|
|
if [ -n "$candidate" ] && [ -x "$candidate" ]; then WINE_BIN="$candidate"; break; fi
|
|
done
|
|
[ -n "$WINE_BIN" ] || { echo '::error::no wine binary found after install'; exit 1; }
|
|
echo "WINE_BIN=$WINE_BIN" >> "$GITHUB_ENV"
|
|
|
|
# Windows Node for the repo's primary line, checksum-verified
|
|
# against the same dist directory.
|
|
version=$(curl -fsSL https://nodejs.org/dist/index.json \
|
|
| jq -r --arg p "v${PRIMARY_NODE_VERSION}." '[.[] | select(.version | startswith($p))][0].version')
|
|
echo "Windows Node: $version"
|
|
curl -fsSL -o "$RUNNER_TEMP/node-win.zip" \
|
|
"https://nodejs.org/dist/${version}/node-${version}-win-x64.zip"
|
|
curl -fsSL "https://nodejs.org/dist/${version}/SHASUMS256.txt" \
|
|
| awk -v a="node-${version}-win-x64.zip" '$2 == a { print $1 " '"$RUNNER_TEMP"'/node-win.zip" }' \
|
|
| sha256sum --check -
|
|
unzip -q "$RUNNER_TEMP/node-win.zip" -d "$RUNNER_TEMP/node-win"
|
|
echo "NODE_WIN=$RUNNER_TEMP/node-win/node-${version}-win-x64/node.exe" >> "$GITHUB_ENV"
|
|
|
|
"$WINE_BIN" wineboot --init || true
|
|
wineserver -w || true
|
|
}
|
|
provision_wine &
|
|
wine_pid=$!
|
|
|
|
install_status=0
|
|
wait "$install_pid" || install_status=$?
|
|
wine_status=0
|
|
wait "$wine_pid" || wine_status=$?
|
|
if (( install_status != 0 )); then exit "$install_status"; fi
|
|
exit "$wine_status"
|
|
|
|
- name: Resolve entrypoints, link vue, smoke Windows Node
|
|
run: |
|
|
# Node under Wine cannot attach stdio to the Actions runner's pipes
|
|
# (Socket open EBADF at bootstrap), so every invocation runs through
|
|
# this wrapper: stdio to a regular file, replayed after exit.
|
|
cat > "$RUNNER_TEMP/wine-node.sh" <<'SH'
|
|
#!/usr/bin/env bash
|
|
set -u
|
|
log="$1"; shift
|
|
"$WINE_BIN" "$NODE_WIN" "$@" < /dev/null > "$log" 2>&1
|
|
status=$?
|
|
tail -n 300 "$log"
|
|
exit "$status"
|
|
SH
|
|
chmod +x "$RUNNER_TEMP/wine-node.sh"
|
|
|
|
resolve() {
|
|
local name="$1"; shift
|
|
for p in "$@"; do
|
|
if [ -f "$p" ]; then echo "$name=$PWD/$p" >> "$GITHUB_ENV"; return 0; fi
|
|
done
|
|
echo "::error::$name not found at any of: $*"; return 1
|
|
}
|
|
resolve TSC_JS node_modules/typescript/bin/tsc
|
|
resolve TSDOWN_JS node_modules/tsdown/dist/run.mjs
|
|
resolve VITEPRESS_JS website/node_modules/vitepress/bin/vitepress.js node_modules/vitepress/bin/vitepress.js
|
|
|
|
# VitePress links vue into the site's node_modules at build time;
|
|
# Wine cannot CREATE Windows symlinks (ENOTSUP) but follows
|
|
# pre-existing Unix ones, so lay the link down host-side.
|
|
if [ -d node_modules/vue ] && [ ! -e website/node_modules/vue ]; then
|
|
mkdir -p website/node_modules
|
|
ln -s ../../node_modules/vue website/node_modules/vue
|
|
fi
|
|
|
|
"$RUNNER_TEMP/wine-node.sh" "$RUNNER_TEMP/smoke.log" -p "'smoke: ' + process.platform + ' ' + process.arch + ' ' + process.version"
|
|
|
|
# The two blocking surfaces run concurrently, the same shape run-gates
|
|
# gives ci-windows-blocking on native Windows: `build` = tsc -b then
|
|
# tsdown, `production site` = the VitePress build. Both statuses are
|
|
# captured so one failure cannot hide the other's result.
|
|
- name: Run blocking Windows gates concurrently under Wine
|
|
run: |
|
|
build_gate() {
|
|
"$RUNNER_TEMP/wine-node.sh" "$RUNNER_TEMP/tsc.log" "$TSC_JS" -b --pretty false || return $?
|
|
"$RUNNER_TEMP/wine-node.sh" "$RUNNER_TEMP/tsdown.log" "$TSDOWN_JS"
|
|
}
|
|
site_gate() {
|
|
cd website
|
|
"$RUNNER_TEMP/wine-node.sh" "$RUNNER_TEMP/site.log" "$VITEPRESS_JS" build .
|
|
}
|
|
start=$SECONDS
|
|
build_gate > "$RUNNER_TEMP/build-gate.out" 2>&1 &
|
|
build_pid=$!
|
|
site_gate > "$RUNNER_TEMP/site-gate.out" 2>&1 &
|
|
site_pid=$!
|
|
build_status=0
|
|
wait "$build_pid" || build_status=$?
|
|
site_status=0
|
|
wait "$site_pid" || site_status=$?
|
|
echo "== build gate (exit $build_status, $((SECONDS - start))s elapsed) =="
|
|
tail -n 120 "$RUNNER_TEMP/build-gate.out"
|
|
echo "== production site gate (exit $site_status, $((SECONDS - start))s elapsed) =="
|
|
tail -n 120 "$RUNNER_TEMP/site-gate.out"
|
|
if (( build_status != 0 )); then exit "$build_status"; fi
|
|
exit "$site_status"
|
|
|
|
- name: Shut down wineserver
|
|
if: always()
|
|
run: wineserver -k 2>/dev/null || true
|
|
|
|
# Master seeds the Wine apt-archive cache in the default-branch scope,
|
|
# which every pull request's windows job can restore; saves from
|
|
# pull-request runs are scoped to their own merge ref and help nobody
|
|
# else. Runs in seconds when the image version already has a cache.
|
|
wine-apt-cache:
|
|
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
name: wine apt cache
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- name: Compose Wine apt cache key
|
|
id: wine-cache-key
|
|
run: echo "key=wine-debs-${ImageOS:-linux}-${ImageVersion:-v0}" >> "$GITHUB_OUTPUT"
|
|
|
|
- uses: actions/cache@v4
|
|
id: wine-cache
|
|
with:
|
|
path: ~/wine-debs
|
|
key: ${{ steps.wine-cache-key.outputs.key }}
|
|
|
|
- name: Download the Wine dependency closure
|
|
if: steps.wine-cache.outputs.cache-hit != 'true'
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y --no-install-recommends --download-only wine
|
|
mkdir -p "$HOME/wine-debs"
|
|
cp /var/cache/apt/archives/*.deb "$HOME/wine-debs/"
|
|
du -sh "$HOME/wine-debs"
|
|
|
|
# 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
|
|
|
|
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.
|
|
runs-on: 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, ', ') }})"
|