Просмотр исходного кода

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.
Chinesezjc 1 месяц назад
Родитель
Сommit
5d8d79ce92
3 измененных файлов с 46 добавлено и 134 удалено
  1. 1 1
      .github/AGENTS.md
  2. 45 132
      .github/workflows/ci.yml
  3. 0 1
      package.json

+ 1 - 1
.github/AGENTS.md

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

+ 45 - 132
.github/workflows/ci.yml

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

+ 0 - 1
package.json

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