mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-14 04:01:35 +00:00
The managed-scope disposal cases in subprocess-local and bash-sandbox run past the 5000ms default when this lane's four partitions, six workers per gate, and sibling gates share one host. The same commit stays inside the budget on the in-house pool: the hosted image exceeded it (run 34449848541) and the lane is green with 90000 (run 34457655892). The Windows coverage lane already grants the same budget for the same class of teardown.
719 lines
32 KiB
YAML
719 lines
32 KiB
YAML
name: CI
|
|
|
|
on:
|
|
pull_request:
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
PRIMARY_NODE_VERSION: '24'
|
|
# CI runs must never report to the production telemetry endpoint baked
|
|
# into apps/cli/cordis.yml (AppCLIEntry disables the row when set).
|
|
DSH_TELEMETRY_DISABLED: '1'
|
|
|
|
# Cancel a superseded pull-request run on a new push so a fresh head does not
|
|
# queue a second full 9-job run behind a stale one (paid enterprise runners
|
|
# would otherwise stack with no auto-cancellation).
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
|
|
# Three enterprise jobs isolate coverage, static analysis, and the
|
|
# build-backed consumer tail. The consumer job owns the only Linux build so
|
|
# all three jobs enter runner allocation independently.
|
|
#
|
|
# FAILOVER (Linux): each Linux enterprise job resolves its pool through the
|
|
# DSH_CI_FAILOVER_LINUX 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.
|
|
# Setting the variable to 'blacksmith' instead routes the same jobs onto
|
|
# Blacksmith's hosted runners at the matching vCPU size (see
|
|
# .agents/notes/implemented/process/2026-09-09-blacksmith-failover-leg.md). The
|
|
# in-house pool's readiness is re-proven on every master push by the
|
|
# serial-linux-selfhosted standby lane in ci-master.yml. The Windows failover
|
|
# switch is the separate DSH_CI_FAILOVER_WINDOWS variable on the windows-native
|
|
# job below.
|
|
node-24:
|
|
if: github.event_name == 'pull_request'
|
|
runs-on: >-
|
|
${{ vars.DSH_CI_FAILOVER_LINUX == 'blacksmith' && 'blacksmith-16vcpu-ubuntu-2404'
|
|
|| vars.DSH_CI_FAILOVER_LINUX == 'selfhosted'
|
|
&& github.event.pull_request.user.login != 'dependabot[bot]'
|
|
&& fromJSON('["self-hosted", "linux", "x64", "vm-backup"]')
|
|
|| 'dsh-ubuntu-24-04-16core' }}
|
|
name: node 24 / static
|
|
env:
|
|
DSH_GATE_CONCURRENCY: '8'
|
|
# Stop the aggregate at the first blocking gate failure so a red run
|
|
# does not keep burning enterprise runner time on the remaining gates.
|
|
DSH_GATE_FAIL_FAST: '1'
|
|
steps:
|
|
# Runner cleanup owns tool caches and fixtures, including cancelled runs.
|
|
- name: Use runner-owned temporary storage
|
|
run: |
|
|
echo "TMPDIR=${{ runner.temp }}" >> "$GITHUB_ENV"
|
|
|
|
# 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
|
|
|
|
# Redirect the Node compile cache (enabled by pnpm and TypeScript) off
|
|
# the root partition's /tmp before the first pnpm call in this lane —
|
|
# see .agents/notes/implemented/process/2026-08-28-ci-node-compile-cache-data-disk.md.
|
|
- name: Redirect Node compile cache to runner temp
|
|
run: echo "NODE_COMPILE_CACHE=${{ runner.temp }}/node-compile-cache" >> "$GITHUB_ENV"
|
|
|
|
- uses: pnpm/action-setup@v4
|
|
with:
|
|
dest: ${{ runner.temp }}/setup-pnpm-${{ github.run_id }}-${{ github.run_attempt }}
|
|
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: ${{ env.PRIMARY_NODE_VERSION }}
|
|
|
|
- name: Configure pnpm store path
|
|
id: pnpm-store
|
|
run: |
|
|
store_root="$HOME/.local/share/pnpm/store"
|
|
echo "PNPM_CONFIG_STORE_DIR=$store_root" >> "$GITHUB_ENV"
|
|
store_path=$(PNPM_CONFIG_STORE_DIR="$store_root" pnpm store path --silent)
|
|
echo "path=$store_path" >> "$GITHUB_OUTPUT"
|
|
|
|
# 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_LINUX != 'selfhosted' || github.event.pull_request.user.login == 'dependabot[bot]'
|
|
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 static gates
|
|
env:
|
|
DSH_ARCHIVE_BASE_REF: ${{ github.event.pull_request.base.sha }}
|
|
run: pnpm run check:ci:static
|
|
|
|
node-24-coverage:
|
|
if: github.event_name == 'pull_request'
|
|
runs-on: >-
|
|
${{ vars.DSH_CI_FAILOVER_LINUX == 'blacksmith' && 'blacksmith-16vcpu-ubuntu-2404'
|
|
|| vars.DSH_CI_FAILOVER_LINUX == 'selfhosted'
|
|
&& github.event.pull_request.user.login != 'dependabot[bot]'
|
|
&& fromJSON('["self-hosted", "linux", "x64", "vm-backup"]')
|
|
|| 'dsh-ubuntu-24-04-16core' }}
|
|
name: node 24 / coverage
|
|
env:
|
|
# Partitioning replaces the instrumented share; this budget gives the
|
|
# exempt-heavy gate two workers on both hosted and failover runners.
|
|
DSH_COVERAGE_MAX_WORKERS: '6'
|
|
DSH_COVERAGE_PARTITIONS: '4'
|
|
DSH_GATE_CONCURRENCY: '3'
|
|
# Managed-scope teardown cases run past the default per-test budget when
|
|
# this lane's partitions, workers, and sibling gates share one host: the
|
|
# disposal cases in subprocess-local and bash-sandbox exceeded 5000ms on
|
|
# the hosted image (run 34449848541) while the same commit stayed inside
|
|
# the budget on the in-house pool; the lane is green with this value
|
|
# (run 34457655892). The Windows coverage lane grants the same budget for
|
|
# the same reason.
|
|
DSH_COVERAGE_TEST_TIMEOUT_MS: '90000'
|
|
# A gate failure aborts the sibling gate instead of waiting out its
|
|
# multi-minute instrumented run.
|
|
DSH_GATE_FAIL_FAST: '1'
|
|
steps:
|
|
# Runner cleanup owns tool caches and fixtures, including cancelled runs.
|
|
- name: Use runner-owned temporary storage
|
|
run: |
|
|
echo "TMPDIR=${{ runner.temp }}" >> "$GITHUB_ENV"
|
|
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
persist-credentials: false
|
|
|
|
# Redirect the Node compile cache (enabled by pnpm and TypeScript) off
|
|
# the root partition's /tmp before the first pnpm call in this lane —
|
|
# see .agents/notes/implemented/process/2026-08-28-ci-node-compile-cache-data-disk.md.
|
|
- name: Redirect Node compile cache to runner temp
|
|
run: echo "NODE_COMPILE_CACHE=${{ runner.temp }}/node-compile-cache" >> "$GITHUB_ENV"
|
|
|
|
- uses: pnpm/action-setup@v4
|
|
with:
|
|
dest: ${{ runner.temp }}/setup-pnpm-${{ github.run_id }}-${{ github.run_attempt }}
|
|
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: ${{ env.PRIMARY_NODE_VERSION }}
|
|
|
|
- name: Configure pnpm store path
|
|
id: pnpm-store
|
|
run: |
|
|
store_root="$HOME/.local/share/pnpm/store"
|
|
echo "PNPM_CONFIG_STORE_DIR=$store_root" >> "$GITHUB_ENV"
|
|
store_path=$(PNPM_CONFIG_STORE_DIR="$store_root" pnpm store path --silent)
|
|
echo "path=$store_path" >> "$GITHUB_OUTPUT"
|
|
|
|
# Self-hosted runners reuse the shared persistent pnpm store.
|
|
# Hosted runners restore the remote cache into their ephemeral store.
|
|
- uses: actions/cache/restore@v4
|
|
if: vars.DSH_CI_FAILOVER_LINUX != 'selfhosted' || github.event.pull_request.user.login == 'dependabot[bot]'
|
|
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 dependencies and prepare bubblewrap
|
|
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: Run exhaustive coverage
|
|
run: pnpm run check:ci:coverage
|
|
|
|
node-24-bench:
|
|
if: github.event_name == 'pull_request'
|
|
runs-on: ubuntu-24.04
|
|
name: node 24 / benchmarks
|
|
# Wall-clock budgets need an otherwise idle runner, so this job runs the
|
|
# benchmark lane alone instead of joining a concurrent gate aggregate.
|
|
timeout-minutes: 15
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- uses: pnpm/action-setup@v4
|
|
with:
|
|
dest: ${{ runner.temp }}/setup-pnpm-${{ github.run_id }}-${{ github.run_attempt }}
|
|
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: ${{ env.PRIMARY_NODE_VERSION }}
|
|
|
|
- name: Configure pnpm store path
|
|
id: pnpm-store
|
|
run: |
|
|
store_root="$HOME/.local/share/pnpm/store"
|
|
echo "PNPM_CONFIG_STORE_DIR=$store_root" >> "$GITHUB_ENV"
|
|
store_path=$(PNPM_CONFIG_STORE_DIR="$store_root" pnpm store path --silent)
|
|
echo "path=$store_path" >> "$GITHUB_OUTPUT"
|
|
|
|
- uses: actions/cache/restore@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-
|
|
|
|
- name: Install (immutable)
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Install benchmark browser and hosted dependencies
|
|
run: pnpm --filter @deepseek-ai/dsh-benchmarks exec playwright install --with-deps chromium
|
|
|
|
- name: Run performance benchmarks
|
|
env:
|
|
DSH_GATE_VERBOSE: '1'
|
|
run: pnpm run check:ci:bench
|
|
|
|
node-24-consumers:
|
|
if: github.event_name == 'pull_request'
|
|
runs-on: >-
|
|
${{ vars.DSH_CI_FAILOVER_LINUX == 'blacksmith' && 'blacksmith-16vcpu-ubuntu-2404'
|
|
|| vars.DSH_CI_FAILOVER_LINUX == 'selfhosted'
|
|
&& github.event.pull_request.user.login != 'dependabot[bot]'
|
|
&& fromJSON('["self-hosted", "linux", "x64", "vm-backup"]')
|
|
|| 'dsh-ubuntu-24-04-16core' }}
|
|
name: node 24 / snapshots and artifacts
|
|
env:
|
|
DSH_GATE_CONCURRENCY: '10'
|
|
DSH_NODE_COMPAT_SKIP_TYPECHECK: '1'
|
|
DSH_OXLINT_THREADS: '8'
|
|
DSH_PUBLINT_CONCURRENCY: '8'
|
|
DSH_WEB_SNAPSHOT_WORKERS: '6'
|
|
# A failing gate aborts its running siblings: a failing build stops the
|
|
# independent Node compatibility smoke, and a failing reader (e.g.
|
|
# publint) stops the remaining artifact consumers.
|
|
DSH_GATE_FAIL_FAST: '1'
|
|
# Failover halves snapshot concurrency for the shared 64-core VM.
|
|
DSH_SNAPSHOT_MAX_CONCURRENCY: ${{ vars.DSH_CI_FAILOVER_LINUX == 'selfhosted' && github.event.pull_request.user.login != 'dependabot[bot]' && '12' || '32' }}
|
|
steps:
|
|
# Runner cleanup owns tool caches and fixtures, including cancelled runs.
|
|
- name: Use runner-owned temporary storage
|
|
run: |
|
|
echo "TMPDIR=${{ runner.temp }}" >> "$GITHUB_ENV"
|
|
echo "PLAYWRIGHT_BROWSERS_PATH=${RUNNER_TEMP%/*}/ms-playwright" >> "$GITHUB_ENV"
|
|
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
persist-credentials: false
|
|
|
|
# Redirect the Node compile cache (enabled by pnpm and TypeScript) off
|
|
# the root partition's /tmp before the first pnpm call in this lane —
|
|
# see .agents/notes/implemented/process/2026-08-28-ci-node-compile-cache-data-disk.md.
|
|
- name: Redirect Node compile cache to runner temp
|
|
run: echo "NODE_COMPILE_CACHE=${{ runner.temp }}/node-compile-cache" >> "$GITHUB_ENV"
|
|
|
|
- uses: pnpm/action-setup@v4
|
|
with:
|
|
dest: ${{ runner.temp }}/setup-pnpm-${{ github.run_id }}-${{ github.run_attempt }}
|
|
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: ${{ env.PRIMARY_NODE_VERSION }}
|
|
|
|
- name: Configure pnpm store path
|
|
id: pnpm-store
|
|
run: |
|
|
store_root="$HOME/.local/share/pnpm/store"
|
|
echo "PNPM_CONFIG_STORE_DIR=$store_root" >> "$GITHUB_ENV"
|
|
store_path=$(PNPM_CONFIG_STORE_DIR="$store_root" pnpm store path --silent)
|
|
echo "path=$store_path" >> "$GITHUB_OUTPUT"
|
|
|
|
# Pull requests restore the pnpm store and Playwright caches without paying
|
|
# compression and upload on the required path. No master job saves these
|
|
# hosted cache keys, so each restore-keys fallback hits the matching archived
|
|
# entry until it evicts, after which the store is cold. Self-hosted runners
|
|
# reuse the shared persistent store instead of restoring the remote archive.
|
|
- uses: actions/cache/restore@v4
|
|
if: vars.DSH_CI_FAILOVER_LINUX != 'selfhosted' || github.event.pull_request.user.login == 'dependabot[bot]'
|
|
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-
|
|
|
|
# Self-hosted runners retain their own browser cache between jobs.
|
|
- uses: actions/cache/restore@v4
|
|
if: vars.DSH_CI_FAILOVER_LINUX != 'selfhosted' || github.event.pull_request.user.login == 'dependabot[bot]'
|
|
with:
|
|
path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }}
|
|
key: ${{ runner.os }}-playwright-${{ hashFiles('pnpm-lock.yaml') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-playwright-
|
|
|
|
- name: Install dependencies and prepare bubblewrap
|
|
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 Playwright Chromium and hosted dependencies
|
|
if: vars.DSH_CI_FAILOVER_LINUX != 'selfhosted' || github.event.pull_request.user.login == 'dependabot[bot]'
|
|
run: pnpm --filter @deepseek-ai/dsh-web-frontend exec playwright install --with-deps chromium
|
|
|
|
# The persistent VM image owns Playwright's Linux system packages; do
|
|
# not mutate the shared host with apt on every failover run.
|
|
- name: Install Playwright Chromium on the failover VM
|
|
if: vars.DSH_CI_FAILOVER_LINUX == 'selfhosted' && github.event.pull_request.user.login != 'dependabot[bot]'
|
|
run: pnpm --filter @deepseek-ai/dsh-web-frontend exec playwright install chromium
|
|
|
|
- name: Run compatibility, snapshot, and artifact gates
|
|
run: pnpm run check:ci:consumers
|
|
|
|
|
|
node-compat:
|
|
if: github.event_name == 'pull_request'
|
|
# Under the selfhosted leg this job admits only repository-owned PR code
|
|
# to the persistent shared VM; the blacksmith branch targets ephemeral
|
|
# runners and carries none of those predicates.
|
|
runs-on: >-
|
|
${{ vars.DSH_CI_FAILOVER_LINUX == 'blacksmith' && 'blacksmith-4vcpu-ubuntu-2404'
|
|
|| vars.DSH_CI_FAILOVER_LINUX == 'selfhosted'
|
|
&& github.event.pull_request.head.repo.full_name == github.repository
|
|
&& github.event.pull_request.head.repo.fork == false
|
|
&& github.event.pull_request.user.login != 'dependabot[bot]'
|
|
&& fromJSON('["self-hosted", "linux", "x64", "vm-backup"]')
|
|
|| matrix.runner }}
|
|
name: ${{ matrix.name }}
|
|
env:
|
|
DSH_GATE_CONCURRENCY: ${{ matrix.gate_concurrency }}
|
|
DSH_NODE_COMPAT_SKIP_TYPECHECK: '1'
|
|
# A failed smoke aborts the remaining compatibility gates instead of
|
|
# letting the build-backed legs run against an already-red aggregate.
|
|
DSH_GATE_FAIL_FAST: '1'
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- node: '22.19'
|
|
name: node 22.19
|
|
runner: ubuntu-latest
|
|
gate_concurrency: '1'
|
|
# Pinned inside 24.0-24.11.1: those releases carry the v1 internal
|
|
# loader while reporting major 24, and every other job tracks the
|
|
# latest 24, which is v2. A bare `24` here would retest that same v2.
|
|
- node: '24.9'
|
|
name: node 24.9
|
|
runner: ubuntu-latest
|
|
gate_concurrency: '1'
|
|
- node: 26
|
|
name: node 26
|
|
runner: ubuntu-latest
|
|
gate_concurrency: '1'
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
persist-credentials: false
|
|
|
|
# Shared hosts keep version installs and generated caches inside runner temp.
|
|
- name: Isolate compatibility caches
|
|
if: runner.environment == 'self-hosted'
|
|
run: |
|
|
echo "NODE_COMPILE_CACHE=$RUNNER_TEMP/node-compile-cache" >> "$GITHUB_ENV"
|
|
echo "npm_config_devdir=$RUNNER_TEMP/node-gyp" >> "$GITHUB_ENV"
|
|
echo "PNPM_CONFIG_STORE_DIR=$HOME/.local/share/pnpm/store" >> "$GITHUB_ENV"
|
|
|
|
- uses: pnpm/action-setup@v4
|
|
with:
|
|
dest: ${{ runner.temp }}/setup-pnpm-${{ github.run_id }}-${{ github.run_attempt }}
|
|
|
|
- uses: actions/setup-node@v6
|
|
env:
|
|
# The runner overwrites RUNNER_* step env before starting JavaScript actions.
|
|
NODE_OPTIONS: ${{ runner.environment == 'self-hosted' && '--import=./scripts/ci-compatible-toolcache.mjs' || '' }}
|
|
with:
|
|
node-version: ${{ matrix.node }}
|
|
cache: ${{ runner.environment == 'github-hosted' && 'pnpm' || '' }}
|
|
package-manager-cache: false
|
|
|
|
- name: Verify isolated Node installation
|
|
if: runner.environment == 'self-hosted'
|
|
run: |
|
|
node_path=$(node -p process.execPath)
|
|
echo "Node executable: $node_path"
|
|
case "$node_path" in
|
|
"$RUNNER_TEMP/node-compat-toolcache/"*) ;;
|
|
*) echo "::error::Node compatibility installation is outside runner temp"; exit 1 ;;
|
|
esac
|
|
|
|
- name: Install (immutable)
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Run compatibility smokes
|
|
env:
|
|
DSH_BUILD_CLIENT_PROFILE: official
|
|
run: pnpm run check:node-compat
|
|
|
|
# Kept out of the gate aggregate: the shape a Node release carries only
|
|
# changes with the Node version, so this belongs to the version matrix
|
|
# rather than to every commit's checks.
|
|
- name: Check Loader internal shape detection
|
|
run: pnpm exec vitest run packages/boot/app-boot/tests/loader-shape.compat.spec.ts
|
|
|
|
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.3.0
|
|
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 reusable builder owns each published executable, wheel, clean-install,
|
|
# keyless black-box, and trusted real-API path. Linux/Windows x64 block PRs;
|
|
# Linux ARM64 and both macOS architectures run in ci-master.yml.
|
|
python-runtime:
|
|
if: github.event_name == 'pull_request'
|
|
name: python runtime / release-shaped matrix
|
|
uses: ./.github/workflows/build-exe-for-python-sdk.yml
|
|
with:
|
|
targets: node24-linux-x64,node24-win-x64
|
|
ci: true
|
|
secrets:
|
|
DEEPSEEK_API_KEY_EXTERNAL: ${{ secrets.DEEPSEEK_API_KEY_EXTERNAL }}
|
|
|
|
# Every pull request also gets real Windows-kernel signals. The former
|
|
# monolithic windows-native job is split into smaller jobs so one slow
|
|
# coverage gate does not hold up build/static results, while the total
|
|
# per-job process count stays lower. Observational checks are non-blocking.
|
|
# Dependabot PRs are excluded from the self-hosted pool and stay queued for
|
|
# the hosted runner.
|
|
windows-build:
|
|
if: github.event_name == 'pull_request'
|
|
runs-on: >-
|
|
${{ vars.DSH_CI_FAILOVER_WINDOWS == 'blacksmith' && 'blacksmith-16vcpu-windows-2025'
|
|
|| vars.DSH_CI_FAILOVER_WINDOWS == 'selfhosted'
|
|
&& github.event.pull_request.user.login != 'dependabot[bot]'
|
|
&& fromJSON('["self-hosted", "dsh-win-ci", "windows"]')
|
|
|| 'dsh-windows-2025-16core' }}
|
|
name: windows node 24 / build
|
|
timeout-minutes: 60
|
|
env:
|
|
# A failing build or site aborts the sibling gate on the same runner.
|
|
DSH_GATE_FAIL_FAST: '1'
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
persist-credentials: false
|
|
- 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: pnpm/action-setup@v4
|
|
with:
|
|
dest: ${{ runner.temp }}/setup-pnpm-js-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.job }}
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: ${{ env.PRIMARY_NODE_VERSION }}
|
|
- name: Install (immutable)
|
|
shell: pwsh
|
|
# See 2026-08-30-windows-refs-store-block-clone-install for the
|
|
# ReFS block-clone rationale; detect the workspace filesystem and
|
|
# pass --package-import-method=clone only on ReFS.
|
|
run: >-
|
|
$drive = (Split-Path -Qualifier $env:GITHUB_WORKSPACE).TrimEnd(':');
|
|
$fs = (Get-Volume -DriveLetter $drive).FileSystem;
|
|
if ($fs -eq 'ReFS') {
|
|
corepack pnpm install --frozen-lockfile --package-import-method=clone
|
|
} else {
|
|
pnpm install --frozen-lockfile
|
|
}
|
|
- name: Run blocking Windows builds
|
|
shell: pwsh
|
|
run: pnpm run check:ci:windows-blocking
|
|
|
|
windows-coverage:
|
|
if: github.event_name == 'pull_request'
|
|
runs-on: >-
|
|
${{ vars.DSH_CI_FAILOVER_WINDOWS == 'blacksmith' && 'blacksmith-16vcpu-windows-2025'
|
|
|| vars.DSH_CI_FAILOVER_WINDOWS == 'selfhosted'
|
|
&& github.event.pull_request.user.login != 'dependabot[bot]'
|
|
&& fromJSON('["self-hosted", "dsh-win-ci", "windows"]')
|
|
|| 'dsh-windows-2025-16core' }}
|
|
name: windows node 24 / coverage
|
|
timeout-minutes: 120
|
|
env:
|
|
DSH_COVERAGE_MAX_WORKERS: '6'
|
|
DSH_COVERAGE_PARTITIONS: '4'
|
|
DSH_COVERAGE_TEST_TIMEOUT_MS: '90000'
|
|
DSH_GATE_CONCURRENCY: '3'
|
|
# A gate failure aborts the sibling gate instead of waiting out its
|
|
# multi-minute instrumented run.
|
|
DSH_GATE_FAIL_FAST: '1'
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
persist-credentials: false
|
|
# Checkout removes the gitignored duration history, so restore it from
|
|
# the GitHub cache before coverage and save the updated one afterwards.
|
|
# The coordinator then weights partitions by measured durations on the
|
|
# second run instead of degrading to a file-count split. GitHub cache
|
|
# entries are immutable, so the save key is unique per run and the
|
|
# restore matches the newest entry through the stable prefix.
|
|
- name: Restore coverage duration history
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: .coverage-times.json
|
|
key: coverage-times-${{ github.run_id }}
|
|
restore-keys: |
|
|
coverage-times-
|
|
- 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: pnpm/action-setup@v4
|
|
with:
|
|
dest: ${{ runner.temp }}/setup-pnpm-js-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.job }}
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: ${{ env.PRIMARY_NODE_VERSION }}
|
|
- name: Install (immutable)
|
|
shell: pwsh
|
|
# See 2026-08-30-windows-refs-store-block-clone-install for the
|
|
# ReFS block-clone rationale; detect the workspace filesystem and
|
|
# pass --package-import-method=clone only on ReFS.
|
|
run: >-
|
|
$drive = (Split-Path -Qualifier $env:GITHUB_WORKSPACE).TrimEnd(':');
|
|
$fs = (Get-Volume -DriveLetter $drive).FileSystem;
|
|
if ($fs -eq 'ReFS') {
|
|
corepack pnpm install --frozen-lockfile --package-import-method=clone
|
|
} else {
|
|
pnpm install --frozen-lockfile
|
|
}
|
|
# No build before coverage, matching the Linux lane: workspace imports
|
|
# resolve to src through the tsconfig paths map, and the lib-consuming
|
|
# suites (webworker-packer image-loadable, webworker-runtime
|
|
# transform-corpus, client ui-trajectory client-bundle) self-skip on
|
|
# unbuilt checkouts.
|
|
- name: Run Windows coverage
|
|
shell: pwsh
|
|
run: pnpm run check:ci:coverage
|
|
- name: Save coverage duration history
|
|
# Coverage flakes must not prevent the cache from building; the
|
|
# measured durations remain useful even when a partition failed. The
|
|
# per-run key keeps every save a fresh immutable cache entry.
|
|
if: ${{ !cancelled() }}
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: .coverage-times.json
|
|
key: coverage-times-${{ github.run_id }}
|
|
|
|
windows-native-tests:
|
|
if: github.event_name == 'pull_request'
|
|
runs-on: >-
|
|
${{ vars.DSH_CI_FAILOVER_WINDOWS == 'blacksmith' && 'blacksmith-16vcpu-windows-2025'
|
|
|| vars.DSH_CI_FAILOVER_WINDOWS == 'selfhosted'
|
|
&& github.event.pull_request.user.login != 'dependabot[bot]'
|
|
&& fromJSON('["self-hosted", "dsh-win-ci", "windows"]')
|
|
|| 'dsh-windows-2025-16core' }}
|
|
name: windows node 24 / native tests
|
|
timeout-minutes: 60
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
persist-credentials: false
|
|
- 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: pnpm/action-setup@v4
|
|
with:
|
|
dest: ${{ runner.temp }}/setup-pnpm-js-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.job }}
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: ${{ env.PRIMARY_NODE_VERSION }}
|
|
- name: Install (immutable)
|
|
shell: pwsh
|
|
# See 2026-08-30-windows-refs-store-block-clone-install for the
|
|
# ReFS block-clone rationale; detect the workspace filesystem and
|
|
# pass --package-import-method=clone only on ReFS.
|
|
run: >-
|
|
$drive = (Split-Path -Qualifier $env:GITHUB_WORKSPACE).TrimEnd(':');
|
|
$fs = (Get-Volume -DriveLetter $drive).FileSystem;
|
|
if ($fs -eq 'ReFS') {
|
|
corepack pnpm install --frozen-lockfile --package-import-method=clone
|
|
} else {
|
|
pnpm install --frozen-lockfile
|
|
}
|
|
- name: Run Windows-specific native tests
|
|
shell: pwsh
|
|
run: >-
|
|
pnpm exec vitest run
|
|
--no-file-parallelism
|
|
--testTimeout 90000
|
|
packages/shell/tool-pwsh/tests/loader.spec.ts
|
|
packages/workflow/workflow-worker-thread/tests/workflow-worker-thread.spec.ts
|
|
packages/workflow/tool-ralph/tests/integration.spec.ts
|
|
packages/subprocess/subprocess-local/tests/process-exit.spec.ts
|
|
|
|
windows-observational:
|
|
if: github.event_name == 'pull_request'
|
|
continue-on-error: true
|
|
runs-on: >-
|
|
${{ vars.DSH_CI_FAILOVER_WINDOWS == 'blacksmith' && 'blacksmith-16vcpu-windows-2025'
|
|
|| vars.DSH_CI_FAILOVER_WINDOWS == 'selfhosted'
|
|
&& github.event.pull_request.user.login != 'dependabot[bot]'
|
|
&& fromJSON('["self-hosted", "dsh-win-ci", "windows"]')
|
|
|| 'dsh-windows-2025-16core' }}
|
|
name: windows node 24 / observational
|
|
timeout-minutes: 60
|
|
env:
|
|
DSH_PUBLINT_CONCURRENCY: '8'
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
persist-credentials: false
|
|
- 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: pnpm/action-setup@v4
|
|
with:
|
|
dest: ${{ runner.temp }}/setup-pnpm-js-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.job }}
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: ${{ env.PRIMARY_NODE_VERSION }}
|
|
- name: Install (immutable)
|
|
shell: pwsh
|
|
# See 2026-08-30-windows-refs-store-block-clone-install for the
|
|
# ReFS block-clone rationale; detect the workspace filesystem and
|
|
# pass --package-import-method=clone only on ReFS.
|
|
run: >-
|
|
$drive = (Split-Path -Qualifier $env:GITHUB_WORKSPACE).TrimEnd(':');
|
|
$fs = (Get-Volume -DriveLetter $drive).FileSystem;
|
|
if ($fs -eq 'ReFS') {
|
|
corepack pnpm install --frozen-lockfile --package-import-method=clone
|
|
} else {
|
|
pnpm install --frozen-lockfile
|
|
}
|
|
- name: Run Windows observational gates
|
|
shell: pwsh
|
|
run: pnpm run check:ci:windows-observational
|
|
|
|
# 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`. Native Windows build and process checks are required; Wine and
|
|
# the deferred Python runtime targets live in ci-master.yml and do not
|
|
# participate in this PR verdict. `needs` cannot cross workflow files.
|
|
# An explicit status function runs the verdict after failed/skipped needs
|
|
# without keeping a cancelled workflow alive for an obsolete verdict.
|
|
all-checks-passed:
|
|
name: all checks passed
|
|
# This bookkeeping-only verdict must not depend on custom-pool
|
|
# provisioning — and under Linux failover it follows the same selector as
|
|
# the worker jobs it aggregates, so a standard-hosted outage cannot strand
|
|
# the branch-protection verdict either. It retargets with the Linux switch
|
|
# (DSH_CI_FAILOVER_LINUX), not the Windows one, because it aggregates the
|
|
# required Linux workers and runs on the vm-backup pool. Under the
|
|
# 'blacksmith' value the verdict shares Blacksmith's pool with its workers
|
|
# through the same selector, so a Blacksmith outage strands both together —
|
|
# the accepted consequence of the explicit opt-in switch.
|
|
runs-on: >-
|
|
${{ vars.DSH_CI_FAILOVER_LINUX == 'blacksmith' && 'blacksmith-4vcpu-ubuntu-2404'
|
|
|| vars.DSH_CI_FAILOVER_LINUX == 'selfhosted'
|
|
&& github.event.pull_request.user.login != 'dependabot[bot]'
|
|
&& fromJSON('["self-hosted", "linux", "x64", "vm-backup"]')
|
|
|| 'ubuntu-latest' }}
|
|
needs: [node-24, node-24-coverage, node-24-bench, node-24-consumers, node-compat, python-sdk, python-runtime, windows-build, windows-native-tests]
|
|
if: ${{ !cancelled() && 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, ', ') }})"
|