|
|
@@ -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
|