diff --git a/.github/workflows/platform-vitest-main.yaml b/.github/workflows/platform-vitest-main.yaml index f3a9c87a956..79ffb5d479b 100644 --- a/.github/workflows/platform-vitest-main.yaml +++ b/.github/workflows/platform-vitest-main.yaml @@ -79,7 +79,11 @@ jobs: macos-vitest: runs-on: macos-26 - timeout-minutes: 60 + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + shard: [1, 2, 3, 4] steps: - name: Checkout uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 @@ -143,11 +147,15 @@ jobs: npm run build - name: Run full Vitest suite on macOS - run: npx vitest run --testTimeout 60000 + run: npx vitest run --testTimeout 60000 --shard="${{ matrix.shard }}/4" wsl-vitest: runs-on: windows-latest timeout-minutes: 90 + strategy: + fail-fast: false + matrix: + shard: [1, 2, 3, 4] env: WSL_DISTRO: Ubuntu WSL_TEST_USER: nemoclaw-ci @@ -305,7 +313,7 @@ jobs: cd '$env:WSL_WORKDIR' export NEMOCLAW_EXEC_TIMEOUT=60000 export NEMOCLAW_TEST_TIMEOUT=60000 - npx vitest run --testTimeout 60000 + npx vitest run --testTimeout 60000 --shard='${{ matrix.shard }}/4' "@ $tmp = "$env:RUNNER_TEMP\wsl-step.sh" [IO.File]::WriteAllText($tmp, ($script -replace "`r",""), (New-Object System.Text.UTF8Encoding $false)) @@ -313,6 +321,7 @@ jobs: wsl -d $env:WSL_DISTRO --user $env:WSL_TEST_USER -- bash -l $wslTmp - name: Run root-required Vitest contracts in WSL + if: ${{ matrix.shard == 1 }} shell: powershell run: | $script = @" diff --git a/test/e2e/README.md b/test/e2e/README.md index 527992f12ea..aaed8c2556a 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -24,6 +24,13 @@ The former top-level `test/e2e/test-*.sh` suite has been removed. Keep real shell, installer, process, Docker, OpenShell, `/proc`, and sandbox boundaries in E2E tests when those boundaries are the behavior under test. +## Platform Vitest main watch + +`.github/workflows/platform-vitest-main.yaml` runs the full Vitest suite in +four independent shards on each of macOS and WSL, with `fail-fast` disabled. +Each macOS shard has a 30-minute budget and each WSL shard has a 90-minute +budget. The additional root-required WSL contracts run only on shard 1. + ## Credential-free tests Credential-free tests that can use the standard Ubuntu runner, CLI build, and diff --git a/test/e2e/docs/README.md b/test/e2e/docs/README.md index 4ea968b8a34..dddbc5ad0f6 100644 --- a/test/e2e/docs/README.md +++ b/test/e2e/docs/README.md @@ -283,7 +283,11 @@ test/e2e/ `test/e2e/risk-signal-reporter.ts` to live Vitest invocations and suppress PR reporting and scorecards. The workflow boundary requires every selected job shard to upload its evidence artifact. -- `.github/workflows/e2e-branch-validation.yaml`, `macos-e2e.yaml`, +- `.github/workflows/platform-vitest-main.yaml` runs the full Vitest suite in + four independent shards on each of macOS and WSL. Each macOS shard has a + 30-minute budget, each WSL shard has a 90-minute budget, and WSL runs its + additional root-required contracts on shard 1 only. + `.github/workflows/e2e-branch-validation.yaml`, `macos-e2e.yaml`, `wsl-e2e.yaml`, and `regression-e2e.yaml` call focused E2E targets directly for their E2E coverage. Individual repository-hosted targets, including `ollama-auth-proxy`, are selected through `.github/workflows/e2e.yaml`. diff --git a/test/platform-vitest-main-workflow.test.ts b/test/platform-vitest-main-workflow.test.ts index 2425c42a8d6..db0550e8572 100644 --- a/test/platform-vitest-main-workflow.test.ts +++ b/test/platform-vitest-main-workflow.test.ts @@ -36,7 +36,11 @@ describe("platform Vitest main workflow", () => { const install = step("macos-vitest", "Install macOS test dependencies"); const run = install.run ?? ""; - expect(job("macos-vitest")["timeout-minutes"]).toBe(60); + expect(job("macos-vitest")["timeout-minutes"]).toBe(30); + expect(job("macos-vitest").strategy).toMatchObject({ + "fail-fast": false, + matrix: { shard: [1, 2, 3, 4] }, + }); expect(checkout.with).toMatchObject({ "fetch-depth": 0, "persist-credentials": false, @@ -62,6 +66,9 @@ describe("platform Vitest main workflow", () => { expect(run).toContain("--only-binary=:all:"); expect(run).toContain("--require-hashes"); expect(run).toContain(`--requirement ${MACOS_REQUIREMENTS_PATH}`); + expect(step("macos-vitest", "Run full Vitest suite on macOS").run).toContain( + '--shard="${{ matrix.shard }}/4"', + ); const requirements = readRepoText(MACOS_REQUIREMENTS_PATH); expect(requirements).toContain("pyyaml==6.0.3"); @@ -83,6 +90,10 @@ describe("platform Vitest main workflow", () => { const rootSuite = step("wsl-vitest", "Run root-required Vitest contracts in WSL").run ?? ""; expect(job("wsl-vitest")["timeout-minutes"]).toBe(90); + expect(job("wsl-vitest").strategy).toMatchObject({ + "fail-fast": false, + matrix: { shard: [1, 2, 3, 4] }, + }); expect(checkout.with).toMatchObject({ "fetch-depth": 0, "persist-credentials": false, @@ -96,8 +107,12 @@ describe("platform Vitest main workflow", () => { expect(fullSuite).toContain("--user $env:WSL_TEST_USER"); expect(fullSuite).toContain("NEMOCLAW_EXEC_TIMEOUT=60000"); expect(fullSuite).toContain("NEMOCLAW_TEST_TIMEOUT=60000"); + expect(fullSuite).toContain("--shard='${{ matrix.shard }}/4'"); expect(fullSuite).not.toMatch(/\bsudo\b|sudoers|NOPASSWD/u); expect(rootSuite).toContain("--user root"); + expect(step("wsl-vitest", "Run root-required Vitest contracts in WSL").if).toBe( + "${{ matrix.shard == 1 }}", + ); expect([...rootSuite.matchAll(/-t '([^']+)'/gu)].map((match) => match[1])).toEqual([ "keeps the locked Hermes entry sticky-protected|lets a sandbox-group peer create state", "requires both fixed files to match|reclaims a root-owned collapsed config|leaves a root-owned recovery baseline untouched",