mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
CI: gate Windows checks by windows-relevant scope (#32456)
* CI: add windows scope output for changed-scope * Test: cover windows scope gating in changed-scope * CI: gate checks-windows by windows scope * Docs: update CI windows scope and runner label * CI: move checks-windows to 32 vCPU runner * Docs: align CI windows runner with workflow
This commit is contained in:
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@@ -38,6 +38,7 @@ jobs:
|
|||||||
run_node: ${{ steps.scope.outputs.run_node }}
|
run_node: ${{ steps.scope.outputs.run_node }}
|
||||||
run_macos: ${{ steps.scope.outputs.run_macos }}
|
run_macos: ${{ steps.scope.outputs.run_macos }}
|
||||||
run_android: ${{ steps.scope.outputs.run_android }}
|
run_android: ${{ steps.scope.outputs.run_android }}
|
||||||
|
run_windows: ${{ steps.scope.outputs.run_windows }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -329,8 +330,8 @@ jobs:
|
|||||||
|
|
||||||
checks-windows:
|
checks-windows:
|
||||||
needs: [docs-scope, changed-scope, build-artifacts, check]
|
needs: [docs-scope, changed-scope, build-artifacts, check]
|
||||||
if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true')
|
if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_windows == 'true')
|
||||||
runs-on: blacksmith-16vcpu-windows-2025
|
runs-on: blacksmith-32vcpu-windows-2025
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
env:
|
env:
|
||||||
NODE_OPTIONS: --max-old-space-size=6144
|
NODE_OPTIONS: --max-old-space-size=6144
|
||||||
|
|||||||
28
docs/ci.md
28
docs/ci.md
@@ -13,20 +13,20 @@ The CI runs on every push to `main` and every pull request. It uses smart scopin
|
|||||||
|
|
||||||
## Job Overview
|
## Job Overview
|
||||||
|
|
||||||
| Job | Purpose | When it runs |
|
| Job | Purpose | When it runs |
|
||||||
| ----------------- | ----------------------------------------------- | ------------------------------------------------- |
|
| ----------------- | ------------------------------------------------------- | ------------------------------------------------- |
|
||||||
| `docs-scope` | Detect docs-only changes | Always |
|
| `docs-scope` | Detect docs-only changes | Always |
|
||||||
| `changed-scope` | Detect which areas changed (node/macos/android) | Non-docs PRs |
|
| `changed-scope` | Detect which areas changed (node/macos/android/windows) | Non-docs PRs |
|
||||||
| `check` | TypeScript types, lint, format | Push to `main`, or PRs with Node-relevant changes |
|
| `check` | TypeScript types, lint, format | Push to `main`, or PRs with Node-relevant changes |
|
||||||
| `check-docs` | Markdown lint + broken link check | Docs changed |
|
| `check-docs` | Markdown lint + broken link check | Docs changed |
|
||||||
| `code-analysis` | LOC threshold check (1000 lines) | PRs only |
|
| `code-analysis` | LOC threshold check (1000 lines) | PRs only |
|
||||||
| `secrets` | Detect leaked secrets | Always |
|
| `secrets` | Detect leaked secrets | Always |
|
||||||
| `build-artifacts` | Build dist once, share with other jobs | Non-docs, node changes |
|
| `build-artifacts` | Build dist once, share with other jobs | Non-docs, node changes |
|
||||||
| `release-check` | Validate npm pack contents | After build |
|
| `release-check` | Validate npm pack contents | After build |
|
||||||
| `checks` | Node/Bun tests + protocol check | Non-docs, node changes |
|
| `checks` | Node/Bun tests + protocol check | Non-docs, node changes |
|
||||||
| `checks-windows` | Windows-specific tests | Non-docs, node changes |
|
| `checks-windows` | Windows-specific tests | Non-docs, windows-relevant changes |
|
||||||
| `macos` | Swift lint/build/test + TS tests | PRs with macos changes |
|
| `macos` | Swift lint/build/test + TS tests | PRs with macos changes |
|
||||||
| `android` | Gradle build + tests | Non-docs, android changes |
|
| `android` | Gradle build + tests | Non-docs, android changes |
|
||||||
|
|
||||||
## Fail-Fast Order
|
## Fail-Fast Order
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { execSync } from "node:child_process";
|
import { execSync } from "node:child_process";
|
||||||
import { appendFileSync } from "node:fs";
|
import { appendFileSync } from "node:fs";
|
||||||
|
|
||||||
/** @typedef {{ runNode: boolean; runMacos: boolean; runAndroid: boolean }} ChangedScope */
|
/** @typedef {{ runNode: boolean; runMacos: boolean; runAndroid: boolean; runWindows: boolean }} ChangedScope */
|
||||||
|
|
||||||
const DOCS_PATH_RE = /^(docs\/|.*\.mdx?$)/;
|
const DOCS_PATH_RE = /^(docs\/|.*\.mdx?$)/;
|
||||||
const MACOS_PROTOCOL_GEN_RE =
|
const MACOS_PROTOCOL_GEN_RE =
|
||||||
@@ -10,6 +10,8 @@ const MACOS_NATIVE_RE = /^(apps\/macos\/|apps\/ios\/|apps\/shared\/|Swabble\/)/;
|
|||||||
const ANDROID_NATIVE_RE = /^(apps\/android\/|apps\/shared\/)/;
|
const ANDROID_NATIVE_RE = /^(apps\/android\/|apps\/shared\/)/;
|
||||||
const NODE_SCOPE_RE =
|
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$)/;
|
/^(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\/workflows\/ci\.yml$|\.github\/actions\/setup-node-env\/action\.yml$|\.github\/actions\/setup-pnpm-store-cache\/action\.yml$)/;
|
||||||
const NATIVE_ONLY_RE =
|
const NATIVE_ONLY_RE =
|
||||||
/^(apps\/android\/|apps\/ios\/|apps\/macos\/|apps\/shared\/|Swabble\/|appcast\.xml$)/;
|
/^(apps\/android\/|apps\/ios\/|apps\/macos\/|apps\/shared\/|Swabble\/|appcast\.xml$)/;
|
||||||
|
|
||||||
@@ -19,12 +21,13 @@ const NATIVE_ONLY_RE =
|
|||||||
*/
|
*/
|
||||||
export function detectChangedScope(changedPaths) {
|
export function detectChangedScope(changedPaths) {
|
||||||
if (!Array.isArray(changedPaths) || changedPaths.length === 0) {
|
if (!Array.isArray(changedPaths) || changedPaths.length === 0) {
|
||||||
return { runNode: true, runMacos: true, runAndroid: true };
|
return { runNode: true, runMacos: true, runAndroid: true, runWindows: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
let runNode = false;
|
let runNode = false;
|
||||||
let runMacos = false;
|
let runMacos = false;
|
||||||
let runAndroid = false;
|
let runAndroid = false;
|
||||||
|
let runWindows = false;
|
||||||
let hasNonDocs = false;
|
let hasNonDocs = false;
|
||||||
let hasNonNativeNonDocs = false;
|
let hasNonNativeNonDocs = false;
|
||||||
|
|
||||||
@@ -52,6 +55,10 @@ export function detectChangedScope(changedPaths) {
|
|||||||
runNode = true;
|
runNode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (WINDOWS_SCOPE_RE.test(path)) {
|
||||||
|
runWindows = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!NATIVE_ONLY_RE.test(path)) {
|
if (!NATIVE_ONLY_RE.test(path)) {
|
||||||
hasNonNativeNonDocs = true;
|
hasNonNativeNonDocs = true;
|
||||||
}
|
}
|
||||||
@@ -61,7 +68,7 @@ export function detectChangedScope(changedPaths) {
|
|||||||
runNode = true;
|
runNode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { runNode, runMacos, runAndroid };
|
return { runNode, runMacos, runAndroid, runWindows };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,6 +101,7 @@ export function writeGitHubOutput(scope, outputPath = process.env.GITHUB_OUTPUT)
|
|||||||
appendFileSync(outputPath, `run_node=${scope.runNode}\n`, "utf8");
|
appendFileSync(outputPath, `run_node=${scope.runNode}\n`, "utf8");
|
||||||
appendFileSync(outputPath, `run_macos=${scope.runMacos}\n`, "utf8");
|
appendFileSync(outputPath, `run_macos=${scope.runMacos}\n`, "utf8");
|
||||||
appendFileSync(outputPath, `run_android=${scope.runAndroid}\n`, "utf8");
|
appendFileSync(outputPath, `run_android=${scope.runAndroid}\n`, "utf8");
|
||||||
|
appendFileSync(outputPath, `run_windows=${scope.runWindows}\n`, "utf8");
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDirectRun() {
|
function isDirectRun() {
|
||||||
@@ -123,11 +131,11 @@ if (isDirectRun()) {
|
|||||||
try {
|
try {
|
||||||
const changedPaths = listChangedPaths(args.base, args.head);
|
const changedPaths = listChangedPaths(args.base, args.head);
|
||||||
if (changedPaths.length === 0) {
|
if (changedPaths.length === 0) {
|
||||||
writeGitHubOutput({ runNode: true, runMacos: true, runAndroid: true });
|
writeGitHubOutput({ runNode: true, runMacos: true, runAndroid: true, runWindows: true });
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
writeGitHubOutput(detectChangedScope(changedPaths));
|
writeGitHubOutput(detectChangedScope(changedPaths));
|
||||||
} catch {
|
} catch {
|
||||||
writeGitHubOutput({ runNode: true, runMacos: true, runAndroid: true });
|
writeGitHubOutput({ runNode: true, runMacos: true, runAndroid: true, runWindows: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { detectChangedScope } from "../../scripts/ci-changed-scope.mjs";
|
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
const { detectChangedScope } = require("../../scripts/ci-changed-scope.mjs") as {
|
||||||
|
detectChangedScope: (paths: string[]) => {
|
||||||
|
runNode: boolean;
|
||||||
|
runMacos: boolean;
|
||||||
|
runAndroid: boolean;
|
||||||
|
runWindows: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
describe("detectChangedScope", () => {
|
describe("detectChangedScope", () => {
|
||||||
it("fails safe when no paths are provided", () => {
|
it("fails safe when no paths are provided", () => {
|
||||||
@@ -7,6 +16,7 @@ describe("detectChangedScope", () => {
|
|||||||
runNode: true,
|
runNode: true,
|
||||||
runMacos: true,
|
runMacos: true,
|
||||||
runAndroid: true,
|
runAndroid: true,
|
||||||
|
runWindows: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -15,6 +25,7 @@ describe("detectChangedScope", () => {
|
|||||||
runNode: false,
|
runNode: false,
|
||||||
runMacos: false,
|
runMacos: false,
|
||||||
runAndroid: false,
|
runAndroid: false,
|
||||||
|
runWindows: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -23,6 +34,7 @@ describe("detectChangedScope", () => {
|
|||||||
runNode: true,
|
runNode: true,
|
||||||
runMacos: false,
|
runMacos: false,
|
||||||
runAndroid: false,
|
runAndroid: false,
|
||||||
|
runWindows: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -31,11 +43,13 @@ describe("detectChangedScope", () => {
|
|||||||
runNode: false,
|
runNode: false,
|
||||||
runMacos: true,
|
runMacos: true,
|
||||||
runAndroid: false,
|
runAndroid: false,
|
||||||
|
runWindows: false,
|
||||||
});
|
});
|
||||||
expect(detectChangedScope(["apps/shared/OpenClawKit/Sources/Foo.swift"])).toEqual({
|
expect(detectChangedScope(["apps/shared/OpenClawKit/Sources/Foo.swift"])).toEqual({
|
||||||
runNode: false,
|
runNode: false,
|
||||||
runMacos: true,
|
runMacos: true,
|
||||||
runAndroid: true,
|
runAndroid: true,
|
||||||
|
runWindows: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -45,6 +59,7 @@ describe("detectChangedScope", () => {
|
|||||||
runNode: false,
|
runNode: false,
|
||||||
runMacos: false,
|
runMacos: false,
|
||||||
runAndroid: false,
|
runAndroid: false,
|
||||||
|
runWindows: false,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -54,12 +69,23 @@ describe("detectChangedScope", () => {
|
|||||||
runNode: false,
|
runNode: false,
|
||||||
runMacos: false,
|
runMacos: false,
|
||||||
runAndroid: false,
|
runAndroid: false,
|
||||||
|
runWindows: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(detectChangedScope(["assets/icon.png"])).toEqual({
|
expect(detectChangedScope(["assets/icon.png"])).toEqual({
|
||||||
runNode: true,
|
runNode: true,
|
||||||
runMacos: false,
|
runMacos: false,
|
||||||
runAndroid: false,
|
runAndroid: false,
|
||||||
|
runWindows: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps windows lane off for non-runtime GitHub metadata files", () => {
|
||||||
|
expect(detectChangedScope([".github/labeler.yml"])).toEqual({
|
||||||
|
runNode: true,
|
||||||
|
runMacos: false,
|
||||||
|
runAndroid: false,
|
||||||
|
runWindows: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user