ci: narrow windows check scope

This commit is contained in:
Peter Steinberger
2026-04-22 20:13:18 +01:00
parent bfc72b5256
commit 7ff8f8cef8
3 changed files with 47 additions and 15 deletions

View File

@@ -46,7 +46,7 @@ Jobs are ordered so cheap checks fail before expensive ones run:
Scope logic lives in `scripts/ci-changed-scope.mjs` and is covered by unit tests in `src/scripts/ci-changed-scope.test.ts`.
CI workflow edits validate the Node CI graph plus workflow linting, but do not force Windows, Android, or macOS native builds by themselves; those platform lanes stay scoped to platform source changes.
Windows Node checks are scoped to runtime, package, config, and workflow surfaces; test-only changes stay on the Linux Node lanes so they do not reserve a 16-vCPU Windows worker for coverage that is already exercised by the normal test shards.
Windows Node checks are scoped to Windows-specific process/path wrappers, npm/pnpm/UI runner helpers, package manager config, and the CI workflow surfaces that execute that lane; unrelated source, plugin, install-smoke, and test-only changes stay on the Linux Node lanes so they do not reserve a 16-vCPU Windows worker for coverage that is already exercised by the normal test shards.
The separate `install-smoke` workflow reuses the same scope script through its own `preflight` job. It computes `run_install_smoke` from the narrower changed-smoke signal, so Docker/install smoke only runs for install, packaging, and container-relevant changes. Its QR package smoke forces the Docker `pnpm install` layer to rerun while preserving the BuildKit pnpm store cache, so it still exercises installation without redownloading dependencies on every run. Its gateway-network e2e reuses the runtime image built earlier in the job, so it adds real container-to-container WebSocket coverage without adding another Docker build.
Local changed-lane logic lives in `scripts/changed-lanes.mjs` and is executed by `scripts/check-changed.mjs`. That local gate is stricter about architecture boundaries than the broad CI platform scope: core production changes run core prod typecheck plus core tests, core test-only changes run only core test typecheck/tests, extension production changes run extension prod typecheck plus extension tests, and extension test-only changes run only extension test typecheck/tests. Public Plugin SDK or plugin-contract changes expand to extension validation because extensions depend on those core contracts. Release metadata-only version bumps run targeted version/config/root-dependency checks. Unknown root/config changes fail safe to all lanes.

View File

@@ -14,7 +14,9 @@ const ANDROID_NATIVE_RE = /^(apps\/android\/|apps\/shared\/)/;
const NODE_SCOPE_RE =
/^(src\/|test\/|extensions\/|packages\/|scripts\/|ui\/|\.github\/|openclaw\.mjs$|package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|tsconfig.*\.json$|vitest.*\.ts$|tsdown\.config\.ts$|\.oxlintrc\.json$|\.oxfmtrc\.jsonc$)/;
const WINDOWS_SCOPE_RE =
/^(src\/|test\/|extensions\/|packages\/|scripts\/|ui\/|openclaw\.mjs$|package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|tsconfig.*\.json$|vitest.*\.ts$|tsdown\.config\.ts$|\.github\/actions\/setup-node-env\/action\.yml$|\.github\/actions\/setup-pnpm-store-cache\/action\.yml$)/;
/^(src\/process\/|src\/infra\/windows-install-roots\.ts$|scripts\/(?:npm-runner|pnpm-runner|ui|vitest-process-group)\.(?:mjs|js)$|test\/scripts\/(?:npm-runner|pnpm-runner|ui|vitest-process-group)\.test\.ts$|package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|\.github\/workflows\/ci\.yml$|\.github\/actions\/setup-node-env\/action\.yml$|\.github\/actions\/setup-pnpm-store-cache\/action\.yml$)/;
const WINDOWS_TEST_SCOPE_RE =
/^(src\/process\/(?:exec\.windows|windows-command)\.test\.ts$|src\/infra\/windows-install-roots\.test\.ts$|test\/scripts\/(?:npm-runner|pnpm-runner|ui|vitest-process-group)\.test\.ts$)/;
const TEST_ONLY_PATH_RE =
/(^test\/|\/test\/|\/tests\/|(?:^|\/)[^/]+\.(?:test|spec|test-utils|test-support|test-harness|e2e-harness)\.[cm]?[jt]sx?$)/;
const CONTROL_UI_I18N_SCOPE_RE =
@@ -83,7 +85,10 @@ export function detectChangedScope(changedPaths) {
runNode = true;
}
if (WINDOWS_SCOPE_RE.test(path) && !TEST_ONLY_PATH_RE.test(path)) {
if (
WINDOWS_SCOPE_RE.test(path) &&
(!TEST_ONLY_PATH_RE.test(path) || WINDOWS_TEST_SCOPE_RE.test(path))
) {
runWindows = true;
}

View File

@@ -59,7 +59,7 @@ describe("detectChangedScope", () => {
runNode: true,
runMacos: false,
runAndroid: false,
runWindows: true,
runWindows: false,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: false,
@@ -175,14 +175,14 @@ describe("detectChangedScope", () => {
runNode: true,
runMacos: false,
runAndroid: false,
runWindows: false,
runWindows: true,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: false,
});
});
it("does not run Windows for test-only changes", () => {
it("runs Windows only for Windows-relevant changes", () => {
expect(detectChangedScope(["extensions/memory-lancedb/index.test.ts"])).toEqual({
runNode: true,
runMacos: false,
@@ -192,7 +192,7 @@ describe("detectChangedScope", () => {
runChangedSmoke: false,
runControlUiI18n: false,
});
expect(detectChangedScope(["test/helpers/windows-paths.test-utils.ts"])).toEqual({
expect(detectChangedScope(["src/auto-reply/reply/streaming-directives.ts"])).toEqual({
runNode: true,
runMacos: false,
runAndroid: false,
@@ -201,6 +201,33 @@ describe("detectChangedScope", () => {
runChangedSmoke: false,
runControlUiI18n: false,
});
expect(detectChangedScope(["src/process/exec.ts"])).toEqual({
runNode: true,
runMacos: false,
runAndroid: false,
runWindows: true,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: false,
});
expect(detectChangedScope(["src/process/exec.windows.test.ts"])).toEqual({
runNode: true,
runMacos: false,
runAndroid: false,
runWindows: true,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: false,
});
expect(detectChangedScope(["scripts/npm-runner.mjs"])).toEqual({
runNode: true,
runMacos: false,
runAndroid: false,
runWindows: true,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: false,
});
});
it("runs changed-smoke for install and packaging surfaces", () => {
@@ -208,7 +235,7 @@ describe("detectChangedScope", () => {
runNode: true,
runMacos: false,
runAndroid: false,
runWindows: true,
runWindows: false,
runSkillsPython: false,
runChangedSmoke: true,
runControlUiI18n: false,
@@ -217,7 +244,7 @@ describe("detectChangedScope", () => {
runNode: true,
runMacos: false,
runAndroid: false,
runWindows: true,
runWindows: false,
runSkillsPython: false,
runChangedSmoke: true,
runControlUiI18n: false,
@@ -235,7 +262,7 @@ describe("detectChangedScope", () => {
runNode: true,
runMacos: false,
runAndroid: false,
runWindows: true,
runWindows: false,
runSkillsPython: false,
runChangedSmoke: true,
runControlUiI18n: false,
@@ -244,7 +271,7 @@ describe("detectChangedScope", () => {
runNode: true,
runMacos: false,
runAndroid: false,
runWindows: true,
runWindows: false,
runSkillsPython: false,
runChangedSmoke: true,
runControlUiI18n: false,
@@ -253,7 +280,7 @@ describe("detectChangedScope", () => {
runNode: true,
runMacos: false,
runAndroid: false,
runWindows: true,
runWindows: false,
runSkillsPython: false,
runChangedSmoke: true,
runControlUiI18n: false,
@@ -262,7 +289,7 @@ describe("detectChangedScope", () => {
runNode: true,
runMacos: false,
runAndroid: false,
runWindows: true,
runWindows: false,
runSkillsPython: false,
runChangedSmoke: true,
runControlUiI18n: false,
@@ -274,7 +301,7 @@ describe("detectChangedScope", () => {
runNode: true,
runMacos: false,
runAndroid: false,
runWindows: true,
runWindows: false,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: true,
@@ -284,7 +311,7 @@ describe("detectChangedScope", () => {
runNode: true,
runMacos: false,
runAndroid: false,
runWindows: true,
runWindows: false,
runSkillsPython: false,
runChangedSmoke: false,
runControlUiI18n: true,