4141 FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 : " true"
4242
4343jobs :
44+ # Keep the canonical main queue quiet long enough for a follow-up push to
45+ # cancel this run before it registers the Blacksmith matrix.
46+ runner-admission :
47+ permissions :
48+ contents : read
49+ runs-on : ubuntu-24.04
50+ timeout-minutes : 3
51+ env :
52+ OPENCLAW_MAIN_CI_DEBOUNCE_SECONDS : " 90"
53+ steps :
54+ - name : Debounce canonical main pushes
55+ if : github.event_name == 'push' && github.repository == 'openclaw/openclaw' && github.ref == 'refs/heads/main'
56+ run : |
57+ set -euo pipefail
58+ echo "Waiting ${OPENCLAW_MAIN_CI_DEBOUNCE_SECONDS}s for a superseding main push before Blacksmith admission"
59+ sleep "${OPENCLAW_MAIN_CI_DEBOUNCE_SECONDS}"
60+ - name : Admit non-main CI runs immediately
61+ if : github.event_name != 'push' || github.repository != 'openclaw/openclaw' || github.ref != 'refs/heads/main'
62+ run : echo "No canonical main debounce required"
63+
4464 # Preflight: establish routing truth and job matrices once, then let real
4565 # work fan out from a single source of truth.
4666 preflight :
4767 permissions :
4868 contents : read
69+ needs : [runner-admission]
4970 if : github.event_name != 'pull_request' || !github.event.pull_request.draft
5071 runs-on : ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-24.04') }}
5172 timeout-minutes : 20
@@ -272,18 +293,22 @@ jobs:
272293 }
273294 }
274295
296+ const compactPullRequest = isCanonicalRepository && eventName === "pull_request";
275297 const nodeTestShards = runNodeFull
276298 ? createNodeTestShardBundles({
277299 includeReleaseOnlyPluginShards: false,
300+ compact: compactPullRequest,
278301 }).map((shard) => ({
279302 check_name: shard.checkName,
280303 runtime: "node",
281304 task: "test-shard",
282305 shard_name: shard.shardName,
306+ groups: shard.groups,
283307 configs: shard.configs,
284308 includePatterns: shard.includePatterns,
285309 requires_dist: shard.requiresDist,
286310 runner: shard.runner,
311+ timeout_minutes: shard.timeoutMinutes,
287312 }))
288313 : [];
289314 const nodeTestNonDistShards = nodeTestShards.filter((shard) => !shard.requires_dist);
@@ -361,6 +386,7 @@ jobs:
361386 security-fast :
362387 permissions :
363388 contents : read
389+ needs : [runner-admission]
364390 if : github.event_name != 'pull_request' || !github.event.pull_request.draft
365391 runs-on : ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-24.04') }}
366392 timeout-minutes : 20
@@ -826,7 +852,7 @@ jobs:
826852 timeout-minutes : 60
827853 strategy :
828854 fail-fast : false
829- max-parallel : 4
855+ max-parallel : 8
830856 matrix : ${{ fromJson(needs.preflight.outputs.checks_fast_core_matrix) }}
831857 steps :
832858 - name : Checkout
@@ -916,7 +942,7 @@ jobs:
916942 timeout-minutes : 60
917943 strategy :
918944 fail-fast : false
919- max-parallel : 4
945+ max-parallel : 8
920946 matrix : ${{ fromJson(needs.preflight.outputs.plugin_contracts_matrix) }}
921947 steps :
922948 - name : Checkout
@@ -997,7 +1023,7 @@ jobs:
9971023 timeout-minutes : 60
9981024 strategy :
9991025 fail-fast : false
1000- max-parallel : 4
1026+ max-parallel : 8
10011027 matrix : ${{ fromJson(needs.preflight.outputs.channel_contracts_matrix) }}
10021028 steps :
10031029 - name : Checkout
@@ -1147,10 +1173,10 @@ jobs:
11471173 needs : [preflight]
11481174 if : needs.preflight.outputs.run_checks_node_core_nondist == 'true'
11491175 runs-on : ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && (matrix.runner || 'blacksmith-4vcpu-ubuntu-2404') || 'ubuntu-24.04') }}
1150- timeout-minutes : 60
1176+ timeout-minutes : ${{ matrix.timeout_minutes || 60 }}
11511177 strategy :
11521178 fail-fast : false
1153- max-parallel : 6
1179+ max-parallel : 12
11541180 matrix : ${{ fromJson(needs.preflight.outputs.checks_node_core_nondist_matrix) }}
11551181 steps :
11561182 - name : Checkout
@@ -1209,6 +1235,7 @@ jobs:
12091235 - name : Run Node test shard
12101236 env :
12111237 NODE_OPTIONS : --max-old-space-size=8192
1238+ OPENCLAW_NODE_TEST_GROUPS_JSON : ${{ toJson(matrix.groups || null) }}
12121239 OPENCLAW_NODE_TEST_CONFIGS_JSON : ${{ toJson(matrix.configs) }}
12131240 OPENCLAW_NODE_TEST_INCLUDE_PATTERNS_JSON : ${{ toJson(matrix.includePatterns) }}
12141241 OPENCLAW_VITEST_SHARD_NAME : ${{ matrix.shard_name }}
@@ -1223,28 +1250,47 @@ jobs:
12231250 import { writeFileSync } from "node:fs";
12241251 import { join } from "node:path";
12251252
1226- const configs = JSON.parse(process.env.OPENCLAW_NODE_TEST_CONFIGS_JSON ?? "[]");
1227- if (!Array.isArray(configs) || configs.length === 0) {
1228- console.error("Missing node test shard configs");
1229- process.exit(1);
1230- }
1231- const includePatterns = JSON.parse(process.env.OPENCLAW_NODE_TEST_INCLUDE_PATTERNS_JSON ?? "null");
1232- const childEnv = { ...process.env };
1233- if (Array.isArray(includePatterns) && includePatterns.length > 0) {
1234- const includeFile = join(
1235- process.env.RUNNER_TEMP ?? ".",
1236- `node-test-include-${process.env.GITHUB_JOB ?? "local"}-${Date.now()}.json`,
1253+ const groups = JSON.parse(process.env.OPENCLAW_NODE_TEST_GROUPS_JSON ?? "null");
1254+ const plans = Array.isArray(groups) && groups.length > 0
1255+ ? groups
1256+ : [{
1257+ configs: JSON.parse(process.env.OPENCLAW_NODE_TEST_CONFIGS_JSON ?? "[]"),
1258+ includePatterns: JSON.parse(
1259+ process.env.OPENCLAW_NODE_TEST_INCLUDE_PATTERNS_JSON ?? "null",
1260+ ),
1261+ shard_name: process.env.OPENCLAW_VITEST_SHARD_NAME,
1262+ }];
1263+ for (const plan of plans) {
1264+ const configs = plan.configs;
1265+ if (!Array.isArray(configs) || configs.length === 0) {
1266+ console.error("Missing node test shard configs");
1267+ process.exit(1);
1268+ }
1269+ const childEnv = {
1270+ ...process.env,
1271+ ...(plan.shard_name ? { OPENCLAW_VITEST_SHARD_NAME: plan.shard_name } : {}),
1272+ };
1273+ if (Array.isArray(plan.includePatterns) && plan.includePatterns.length > 0) {
1274+ const includeFile = join(
1275+ process.env.RUNNER_TEMP ?? ".",
1276+ `node-test-include-${process.env.GITHUB_JOB ?? "local"}-${Date.now()}.json`,
1277+ );
1278+ writeFileSync(includeFile, JSON.stringify(plan.includePatterns), "utf8");
1279+ childEnv.OPENCLAW_VITEST_INCLUDE_FILE = includeFile;
1280+ } else {
1281+ delete childEnv.OPENCLAW_VITEST_INCLUDE_FILE;
1282+ }
1283+ const result = spawnSync(
1284+ "pnpm",
1285+ ["exec", "node", "scripts/test-projects.mjs", ...configs],
1286+ {
1287+ env: childEnv,
1288+ stdio: "inherit",
1289+ },
12371290 );
1238- writeFileSync(includeFile, JSON.stringify(includePatterns), "utf8");
1239- childEnv.OPENCLAW_VITEST_INCLUDE_FILE = includeFile;
1240- }
1241-
1242- const result = spawnSync("pnpm", ["exec", "node", "scripts/test-projects.mjs", ...configs], {
1243- env: childEnv,
1244- stdio: "inherit",
1245- });
1246- if ((result.status ?? 1) !== 0) {
1247- process.exit(result.status ?? 1);
1291+ if ((result.status ?? 1) !== 0) {
1292+ process.exit(result.status ?? 1);
1293+ }
12481294 }
12491295 EOF
12501296
@@ -1259,7 +1305,7 @@ jobs:
12591305 timeout-minutes : 20
12601306 strategy :
12611307 fail-fast : false
1262- max-parallel : 4
1308+ max-parallel : 8
12631309 matrix :
12641310 include :
12651311 - check_name : check-guards
@@ -1401,7 +1447,7 @@ jobs:
14011447 timeout-minutes : 20
14021448 strategy :
14031449 fail-fast : false
1404- max-parallel : 4
1450+ max-parallel : 8
14051451 matrix :
14061452 include :
14071453 - check_name : check-additional-boundaries-a
0 commit comments