From 72adf2458bb538b5568bbabe68ff141419cf92a3 Mon Sep 17 00:00:00 2001 From: Josh Avant <830519+joshavant@users.noreply.github.com> Date: Thu, 26 Feb 2026 00:33:36 -0600 Subject: [PATCH] CI: shard Windows test lane for faster CI critical path (#27234) Merged via /review-pr -> /prepare-pr -> /merge-pr. Prepared head SHA: f7c41089e0d5c36f59addd643d2038502bafe933 Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com> Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com> Reviewed-by: @joshavant --- .github/workflows/ci.yml | 19 ++++++++++++++++++- CHANGELOG.md | 6 ++++++ scripts/test-parallel.mjs | 33 ++++++++++++++++++++++++++++----- 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8de4f3882c8..e7bef285a7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -418,12 +418,23 @@ jobs: include: - runtime: node task: lint + shard_index: 0 + shard_count: 1 command: pnpm lint - runtime: node task: test + shard_index: 1 + shard_count: 2 + command: pnpm canvas:a2ui:bundle && pnpm test + - runtime: node + task: test + shard_index: 2 + shard_count: 2 command: pnpm canvas:a2ui:bundle && pnpm test - runtime: node task: protocol + shard_index: 0 + shard_count: 1 command: pnpm protocol:check steps: - name: Checkout @@ -495,6 +506,12 @@ jobs: pnpm -v pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true || pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true + - name: Configure test shard (Windows) + if: matrix.task == 'test' + run: | + echo "OPENCLAW_TEST_SHARDS=${{ matrix.shard_count }}" >> "$GITHUB_ENV" + echo "OPENCLAW_TEST_SHARD_INDEX=${{ matrix.shard_index }}" >> "$GITHUB_ENV" + - name: Configure vitest JSON reports if: matrix.task == 'test' run: echo "OPENCLAW_VITEST_REPORT_DIR=$RUNNER_TEMP/vitest-reports" >> "$GITHUB_ENV" @@ -512,7 +529,7 @@ jobs: if: matrix.task == 'test' uses: actions/upload-artifact@v4 with: - name: vitest-reports-${{ runner.os }}-${{ matrix.runtime }} + name: vitest-reports-${{ runner.os }}-${{ matrix.runtime }}-shard${{ matrix.shard_index }}of${{ matrix.shard_count }} path: | ${{ env.OPENCLAW_VITEST_REPORT_DIR }} ${{ runner.temp }}/vitest-slowest.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f2ce6018e9..ce9d381407f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ Docs: https://docs.openclaw.ai +## 2026.2.26 (Unreleased) + +### Fixes + +- CI/Windows: shard the Windows `checks-windows` test lane into two matrix jobs and honor explicit shard index overrides in `scripts/test-parallel.mjs` to reduce CI critical-path wall time. (#27234) Thanks @joshavant. + ## 2026.2.25 ### Changes diff --git a/scripts/test-parallel.mjs b/scripts/test-parallel.mjs index 35afef83c3f..e866ef712ab 100644 --- a/scripts/test-parallel.mjs +++ b/scripts/test-parallel.mjs @@ -160,11 +160,31 @@ const runs = [ }, ]; const shardOverride = Number.parseInt(process.env.OPENCLAW_TEST_SHARDS ?? "", 10); -const shardCount = isWindowsCi - ? Number.isFinite(shardOverride) && shardOverride > 1 - ? shardOverride - : 2 - : 1; +const configuredShardCount = + Number.isFinite(shardOverride) && shardOverride > 1 ? shardOverride : null; +const shardCount = configuredShardCount ?? (isWindowsCi ? 2 : 1); +const shardIndexOverride = (() => { + const parsed = Number.parseInt(process.env.OPENCLAW_TEST_SHARD_INDEX ?? "", 10); + return Number.isFinite(parsed) && parsed > 0 ? parsed : null; +})(); + +if (shardIndexOverride !== null && shardCount <= 1) { + console.error( + `[test-parallel] OPENCLAW_TEST_SHARD_INDEX=${String( + shardIndexOverride, + )} requires OPENCLAW_TEST_SHARDS>1.`, + ); + process.exit(2); +} + +if (shardIndexOverride !== null && shardIndexOverride > shardCount) { + console.error( + `[test-parallel] OPENCLAW_TEST_SHARD_INDEX=${String( + shardIndexOverride, + )} exceeds OPENCLAW_TEST_SHARDS=${String(shardCount)}.`, + ); + process.exit(2); +} const windowsCiArgs = isWindowsCi ? ["--dangerouslyIgnoreUnhandledErrors"] : []; const silentArgs = process.env.OPENCLAW_TEST_SHOW_PASSED_LOGS === "1" ? [] : ["--silent=passed-only"]; @@ -391,6 +411,9 @@ const run = async (entry) => { if (shardCount <= 1) { return runOnce(entry); } + if (shardIndexOverride !== null) { + return runOnce(entry, ["--shard", `${shardIndexOverride}/${shardCount}`]); + } for (let shardIndex = 1; shardIndex <= shardCount; shardIndex += 1) { // eslint-disable-next-line no-await-in-loop const code = await runOnce(entry, ["--shard", `${shardIndex}/${shardCount}`]);