mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
test(windows): split native job into build/coverage/native-tests/observational
Keep the 4-partition coverage profile, split the monolithic windows-native job into smaller required jobs (build, coverage, native-tests) plus a non-blocking observational job. Update ci-workflow.spec for the new topology.
This commit is contained in:
+122
-33
@@ -397,63 +397,152 @@ jobs:
|
||||
if: always()
|
||||
run: wineserver -k 2>/dev/null || true
|
||||
|
||||
# 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. Under normal operation it runs on the hosted larger runner; under
|
||||
# Windows failover (DSH_CI_FAILOVER_WINDOWS=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. This Windows switch is independent of the Linux
|
||||
# DSH_CI_FAILOVER_LINUX variable that retargets the three required Linux jobs
|
||||
# and the all-checks-passed verdict above.
|
||||
windows-native:
|
||||
# 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 == '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: 120
|
||||
env:
|
||||
DSH_COVERAGE_MAX_WORKERS: '6'
|
||||
DSH_COVERAGE_PARTITIONS: '4'
|
||||
# Instrumented process and polling fixtures can exceed Vitest's defaults
|
||||
# under the complete lane's concurrent gate load.
|
||||
DSH_COVERAGE_TEST_TIMEOUT_MS: '30000'
|
||||
DSH_GATE_CONCURRENCY: '4'
|
||||
DSH_PUBLINT_CONCURRENCY: '8'
|
||||
name: windows node 24 / build
|
||||
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
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.PRIMARY_NODE_VERSION }}
|
||||
|
||||
# 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
|
||||
|
||||
- name: Run complete native Windows gate inventory
|
||||
- name: Run blocking Windows builds
|
||||
shell: pwsh
|
||||
run: pnpm run check:ci:windows-complete
|
||||
run: pnpm run check:ci:windows-blocking
|
||||
|
||||
windows-coverage:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: >-
|
||||
${{ 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: '30000'
|
||||
DSH_GATE_CONCURRENCY: '3'
|
||||
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
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.PRIMARY_NODE_VERSION }}
|
||||
- name: Install (immutable)
|
||||
shell: pwsh
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build before coverage
|
||||
shell: pwsh
|
||||
run: pnpm run build
|
||||
- name: Run Windows coverage
|
||||
shell: pwsh
|
||||
run: pnpm run check:ci:coverage
|
||||
|
||||
windows-native-tests:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: >-
|
||||
${{ 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
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.PRIMARY_NODE_VERSION }}
|
||||
- name: Install (immutable)
|
||||
shell: pwsh
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Run Windows-specific native tests
|
||||
shell: pwsh
|
||||
run: >-
|
||||
pnpm exec vitest run
|
||||
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
|
||||
packages/session/session-persistence-sqlite/tests/differential.spec.ts
|
||||
|
||||
windows-observational:
|
||||
if: github.event_name == 'pull_request'
|
||||
continue-on-error: true
|
||||
runs-on: >-
|
||||
${{ 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
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.PRIMARY_NODE_VERSION }}
|
||||
- name: Install (immutable)
|
||||
shell: pwsh
|
||||
run: 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
|
||||
@@ -479,7 +568,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, python-runtime, windows]
|
||||
needs: [node-24, node-24-coverage, node-24-consumers, node-compat, python-sdk, python-runtime, windows, windows-build, windows-coverage, windows-native-tests]
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
steps:
|
||||
- name: Fail if any needed job did not succeed
|
||||
|
||||
+56
-24
@@ -27,21 +27,24 @@ describe('CI workflow', () => {
|
||||
for (const { jobName, step } of setups) {
|
||||
expect(step, `${jobName} must not share pnpm/action-setup's default destination`).toMatchObject({
|
||||
with: {
|
||||
dest: jobName === 'windows-native'
|
||||
dest: jobName.startsWith('windows-')
|
||||
? nativeWindowsPnpmDestination
|
||||
: runnerPrivatePnpmDestination,
|
||||
},
|
||||
})
|
||||
if (jobName === 'windows-native') expect(step).not.toMatchObject({ with: { standalone: true } })
|
||||
if (jobName.startsWith('windows-')) expect(step).not.toMatchObject({ with: { standalone: true } })
|
||||
}
|
||||
})
|
||||
|
||||
it('keeps a required Wine Windows job, a non-blocking native Windows job with failover, and a master-only standby', () => {
|
||||
it('keeps required Wine and split native Windows jobs with failover, plus a master-only standby', () => {
|
||||
const workflow = loadWorkflow('.github/workflows/ci.yml')
|
||||
const masterWorkflow = loadWorkflow('.github/workflows/ci-master.yml')
|
||||
if (!isRecord(workflow.jobs)
|
||||
|| !isRecord(workflow.jobs.windows)
|
||||
|| !isRecord(workflow.jobs['windows-native'])
|
||||
|| !isRecord(workflow.jobs['windows-build'])
|
||||
|| !isRecord(workflow.jobs['windows-coverage'])
|
||||
|| !isRecord(workflow.jobs['windows-native-tests'])
|
||||
|| !isRecord(workflow.jobs['windows-observational'])
|
||||
|| !isRecord(workflow.jobs['node-24'])
|
||||
|| !isRecord(workflow.jobs['node-24-coverage'])
|
||||
|| !isRecord(workflow.jobs['node-24-consumers'])
|
||||
@@ -49,11 +52,14 @@ describe('CI workflow', () => {
|
||||
|| !isRecord(masterWorkflow.jobs)
|
||||
|| !isRecord(masterWorkflow.jobs['wine-apt-cache'])
|
||||
|| !isRecord(masterWorkflow.jobs['serial-windows'])) {
|
||||
throw new TypeError('CI workflow must define windows, windows-native, node-24, node-24-coverage, node-24-consumers, and all-checks-passed; ci-master must define wine-apt-cache and serial-windows')
|
||||
throw new TypeError('CI workflow must define windows, windows-build, windows-coverage, windows-native-tests, windows-observational, node-24, node-24-coverage, node-24-consumers, and all-checks-passed; ci-master must define wine-apt-cache and serial-windows')
|
||||
}
|
||||
|
||||
const windows = workflow.jobs.windows
|
||||
const windowsNative = workflow.jobs['windows-native']
|
||||
const windowsBuild = workflow.jobs['windows-build']
|
||||
const windowsCoverage = workflow.jobs['windows-coverage']
|
||||
const windowsNativeTests = workflow.jobs['windows-native-tests']
|
||||
const windowsObservational = workflow.jobs['windows-observational']
|
||||
const wineAptCache = masterWorkflow.jobs['wine-apt-cache']
|
||||
const serialWindows = masterWorkflow.jobs['serial-windows']
|
||||
const node24 = workflow.jobs['node-24']
|
||||
@@ -73,24 +79,46 @@ describe('CI workflow', () => {
|
||||
expect(windows.if).toBe("github.event_name == 'pull_request'")
|
||||
expect(commandSteps.some(step => step.run.includes('wine-windows-gates.sh'))).toBe(true)
|
||||
|
||||
// windows-native: non-blocking native job with failover, runs windows-complete.
|
||||
// Its pool is resolved by the Windows-specific switch.
|
||||
expect(typeof windowsNative['runs-on']).toBe('string')
|
||||
expect(windowsNative['runs-on']).toContain('DSH_CI_FAILOVER_WINDOWS')
|
||||
expect(windowsNative['runs-on']).not.toContain('DSH_CI_FAILOVER_LINUX')
|
||||
expect(windowsNative['runs-on']).toContain('self-hosted')
|
||||
expect(windowsNative['runs-on']).toContain('dsh-win-ci')
|
||||
expect(windowsNative['runs-on']).toContain('dsh-windows-2025-16core')
|
||||
expect(windowsNative.name).toBe('windows node 24 / native complete')
|
||||
expect(windowsNative.if).toBe("github.event_name == 'pull_request'")
|
||||
expect(windowsNative.env).toMatchObject({
|
||||
DSH_COVERAGE_TEST_TIMEOUT_MS: '30000',
|
||||
})
|
||||
const nativeSteps = windowsNative.steps as unknown[]
|
||||
const nativeCommandSteps = nativeSteps.filter((step): step is Record<string, unknown> & { run: string } => (
|
||||
// The split native jobs all resolve their pool through the Windows switch.
|
||||
for (const [jobName, job] of [['windows-build', windowsBuild], ['windows-coverage', windowsCoverage], ['windows-native-tests', windowsNativeTests], ['windows-observational', windowsObservational]] as const) {
|
||||
expect(typeof job['runs-on']).toBe('string')
|
||||
expect(job['runs-on'], `${jobName} runs-on must use the Windows failover switch`).toContain('DSH_CI_FAILOVER_WINDOWS')
|
||||
expect(job['runs-on'], `${jobName} runs-on must not use the Linux failover switch`).not.toContain('DSH_CI_FAILOVER_LINUX')
|
||||
expect(job['runs-on']).toContain('self-hosted')
|
||||
expect(job['runs-on']).toContain('dsh-win-ci')
|
||||
expect(job['runs-on']).toContain('dsh-windows-2025-16core')
|
||||
expect(job.if).toBe("github.event_name == 'pull_request'")
|
||||
}
|
||||
|
||||
// windows-build runs the blocking build/site pair.
|
||||
expect(windowsBuild.name).toBe('windows node 24 / build')
|
||||
const buildSteps = windowsBuild.steps as unknown[]
|
||||
const buildCommands = buildSteps.filter((step): step is Record<string, unknown> & { run: string } => (
|
||||
isRecord(step) && typeof step.run === 'string'
|
||||
))
|
||||
expect(nativeCommandSteps.map(step => step.run)).toContain('pnpm run check:ci:windows-complete')
|
||||
expect(buildCommands.map(step => step.run)).toContain('pnpm run check:ci:windows-blocking')
|
||||
|
||||
// windows-coverage uses the lower 4-partition profile.
|
||||
expect(windowsCoverage.name).toBe('windows node 24 / coverage')
|
||||
expect(windowsCoverage.env).toMatchObject({ DSH_COVERAGE_PARTITIONS: '4' })
|
||||
const coverageSteps = windowsCoverage.steps as unknown[]
|
||||
const coverageCommands = coverageSteps.filter((step): step is Record<string, unknown> & { run: string } => (
|
||||
isRecord(step) && typeof step.run === 'string'
|
||||
))
|
||||
expect(coverageCommands.map(step => step.run)).toContain('pnpm run check:ci:coverage')
|
||||
|
||||
// windows-native-tests runs the Windows-specific specs.
|
||||
expect(windowsNativeTests.name).toBe('windows node 24 / native tests')
|
||||
const nativeTestSteps = windowsNativeTests.steps as unknown[]
|
||||
const nativeTestCommands = nativeTestSteps.filter((step): step is Record<string, unknown> & { run: string } => (
|
||||
isRecord(step) && typeof step.run === 'string'
|
||||
))
|
||||
expect(nativeTestCommands.map(step => step.run).join('\n')).toContain('tool-pwsh/tests/loader.spec.ts')
|
||||
expect(nativeTestCommands.map(step => step.run).join('\n')).toContain('workflow-worker-thread.spec.ts')
|
||||
|
||||
// windows-observational is non-blocking.
|
||||
expect(windowsObservational.name).toBe('windows node 24 / observational')
|
||||
expect(windowsObservational['continue-on-error']).toBe(true)
|
||||
|
||||
// wine-apt-cache: master-only, seeds the Wine apt cache, lives in ci-master.
|
||||
expect(wineAptCache.if).toBe("github.event_name == 'push' && github.ref == 'refs/heads/master'")
|
||||
@@ -101,9 +129,13 @@ describe('CI workflow', () => {
|
||||
expect(serialWindows['runs-on']).toEqual(['self-hosted', 'dsh-win-ci', 'windows'])
|
||||
expect(serialWindows.name).toBe('serial / windows (self-hosted standby)')
|
||||
|
||||
// Aggregate: Wine `windows` required, native `windows-native` excluded.
|
||||
// Aggregate: Wine and the three required split native jobs are needed;
|
||||
// observational stays out of the verdict.
|
||||
expect(aggregate.needs).toContain('windows')
|
||||
expect(aggregate.needs).not.toContain('windows-native')
|
||||
expect(aggregate.needs).toContain('windows-build')
|
||||
expect(aggregate.needs).toContain('windows-coverage')
|
||||
expect(aggregate.needs).toContain('windows-native-tests')
|
||||
expect(aggregate.needs).not.toContain('windows-observational')
|
||||
expect(aggregate.needs).not.toContain('serial-windows')
|
||||
|
||||
// Linux failover is a separate switch: the three required Linux workers
|
||||
|
||||
Reference in New Issue
Block a user