mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
ci: unify Windows CI on native self-hosted runners
Replace the Wine-emulated windows job and the independent windows-native job with a single required native Windows job that runs check:ci:windows-complete on the hosted larger runner, falling back to the self-hosted [self-hosted, dsh-win-ci, windows] pool under failover (DSH_CI_FAILOVER=selfhosted). The serial-windows standby mirrors serial-linux-selfhosted: master-only, unsharded, self-hosted, absent from all-checks-passed. Remove wine-apt-cache, the check:windows-wine script reference, and the dual-lane topology the two Windows notes described.
This commit is contained in:
2
.github/AGENTS.md
vendored
2
.github/AGENTS.md
vendored
@@ -1,3 +1,3 @@
|
||||
# AGENTS.md — GitHub Actions
|
||||
|
||||
Run jobs on Windows runners (`windows-*` labels) under native `pwsh`. The pull-request `windows` job is the deliberate exception: it runs Windows Node under Wine on hosted Linux and blocks `all checks passed`; `windows-native` runs automatically on `windows-2025` but reports independently — see the [dual-lane Agent Note](../.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md).
|
||||
Run jobs on Windows runners (`windows-*` labels) under native `pwsh`. The required pull-request `windows` job and the master `serial-windows` standby both run on real Windows; the self-hosted pool uses labels `[self-hosted, dsh-win-ci, windows]`. Under failover (`DSH_CI_FAILOVER=selfhosted`) the `windows` job retargets onto the self-hosted pool.
|
||||
|
||||
177
.github/workflows/ci.yml
vendored
177
.github/workflows/ci.yml
vendored
@@ -313,123 +313,18 @@ jobs:
|
||||
- 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 independent
|
||||
# windows-native job below keeps the complete native-kernel inventory —
|
||||
# including the observational portability gates this lane does not run —
|
||||
# on real windows-2025. This job only provisions runner state (caches,
|
||||
# apt); scripts/wine-windows-gates.sh owns the gate logic and is the same
|
||||
# script the optional local gate `pnpm run check:windows-wine` runs.
|
||||
# Current topology and fidelity limits live in
|
||||
# .agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md
|
||||
# Required pull-request Windows signal on real Windows. Under normal
|
||||
# operation this runs on the hosted larger runner; under failover
|
||||
# (DSH_CI_FAILOVER=selfhosted) it retargets onto the in-house self-hosted
|
||||
# Windows pool. Dependabot PRs are excluded from the self-hosted pool and
|
||||
# stay queued for the hosted runner — see the failover runbook.
|
||||
windows:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
name: windows node 24 / wine blocking
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
dest: ${{ runner.temp }}/setup-pnpm
|
||||
|
||||
- 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-
|
||||
|
||||
# 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 }}
|
||||
|
||||
# Runner provisioning only — a developer machine installs Wine through
|
||||
# its own package manager; the gate script assumes a wine binary and
|
||||
# fails loud without one. 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.
|
||||
- name: Install Wine
|
||||
run: |
|
||||
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
|
||||
|
||||
- name: Run the Wine Windows gates
|
||||
run: bash scripts/wine-windows-gates.sh
|
||||
|
||||
- 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"
|
||||
|
||||
# Every pull request also gets a real Windows-kernel signal. This job keeps
|
||||
# its own unmasked conclusion but is deliberately absent from
|
||||
# all-checks-passed.needs, so it never delays or changes that required verdict.
|
||||
# See the dual Wine/native pull-request CI decision:
|
||||
# .agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md
|
||||
windows-native:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: dsh-windows-2025-16core
|
||||
runs-on: >-
|
||||
${{ vars.DSH_CI_FAILOVER == '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 complete
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -455,8 +350,10 @@ jobs:
|
||||
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 the gates.
|
||||
# Extracting the many-file pnpm store cache is slower than a clean
|
||||
# install on hosted Windows runners, and saving it adds latency after
|
||||
# the gates. The self-hosted VM's persistent store makes caching
|
||||
# redundant.
|
||||
- name: Install (immutable)
|
||||
shell: pwsh
|
||||
run: pnpm install --frozen-lockfile
|
||||
@@ -621,14 +518,22 @@ jobs:
|
||||
DSH_SNAPSHOT_MAX_CONCURRENCY: '1'
|
||||
run: pnpm run check:ci
|
||||
|
||||
# Hot-standby drill for the in-house self-hosted Windows pool: every master
|
||||
# move re-runs the complete unsharded Windows gate inventory on the persistent
|
||||
# VM, continuously proving that environment can take over the required
|
||||
# `windows` lane if the hosted pool degrades (the switch is 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. Non-blocking for pull requests; absent
|
||||
# from all-checks-passed.needs by design — the required `windows` job owns
|
||||
# the PR verdict. 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-windows:
|
||||
# Real-Windows signal on the in-house self-hosted Windows pool. This lane
|
||||
# runs the complete primary Node CI on actual Windows (in contrast to the
|
||||
# Wine-emulated `windows` job), so win32-native output is exercised where
|
||||
# it matters. Selector matches our self-hosted runners by label.
|
||||
if: github.event_name == 'pull_request'
|
||||
name: serial / windows
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||
name: serial / windows (self-hosted standby)
|
||||
runs-on: [self-hosted, dsh-win-ci, windows]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
@@ -646,20 +551,30 @@ jobs:
|
||||
with:
|
||||
node-version: ${{ env.PRIMARY_NODE_VERSION }}
|
||||
|
||||
- name: Configure persistent pnpm store
|
||||
shell: pwsh
|
||||
run: |
|
||||
$storeRoot = "$env:LOCALAPPDATA\pnpm\store"
|
||||
echo "PNPM_CONFIG_STORE_DIR=$storeRoot" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Install (immutable)
|
||||
shell: pwsh
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run complete unsharded primary Node CI serially
|
||||
# The persistent VM image owns Playwright's Windows system dependencies;
|
||||
# this step also proves that browser provisioning remains usable for
|
||||
# failover.
|
||||
- name: Install Playwright Chromium
|
||||
shell: pwsh
|
||||
run: pnpm --filter @deepseek-ai/dsh-frontend exec playwright install chromium
|
||||
|
||||
- name: Run complete unsharded Windows gate inventory serially
|
||||
shell: pwsh
|
||||
env:
|
||||
DSH_COVERAGE_MAX_WORKERS: '1'
|
||||
DSH_E2E_MAX_WORKERS: '1'
|
||||
DSH_GATE_CONCURRENCY: '1'
|
||||
DSH_OXLINT_THREADS: '1'
|
||||
DSH_PUBLINT_CONCURRENCY: '1'
|
||||
DSH_SNAPSHOT_MAX_CONCURRENCY: '1'
|
||||
run: pnpm run check:ci
|
||||
run: pnpm run check:ci:windows-complete
|
||||
|
||||
# Manual, bounded comparison of the actual critical Linux and Windows lanes.
|
||||
# The named pools are restricted at the organization level to this repository.
|
||||
@@ -870,9 +785,7 @@ jobs:
|
||||
# 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`. The required Wine job is listed as `windows`; `windows-native` is
|
||||
# deliberately absent so its independent result never delays or changes this
|
||||
# verdict. (`needs` cannot reach across workflow files; e2e.yml stays its own
|
||||
# `needs`. (`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
|
||||
@@ -889,7 +802,7 @@ jobs:
|
||||
&& 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-consumers, node-compat, python-sdk, windows, serial-windows]
|
||||
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
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
"check:ci:windows-blocking": "tsx scripts/run-gates.ts ci-windows-blocking",
|
||||
"check:ci:windows-complete": "tsx scripts/run-gates.ts ci-windows-complete",
|
||||
"check:ci:windows-observational": "tsx scripts/run-gates.ts ci-windows-observational",
|
||||
"check:windows-wine": "bash scripts/wine-windows-gates.sh",
|
||||
"check:node-compat": "tsx scripts/run-gates.ts node-compat",
|
||||
"knip": "knip --treat-config-hints-as-errors",
|
||||
"publint": "tsx scripts/publint-all.ts",
|
||||
|
||||
Reference in New Issue
Block a user