Skip to content

Commit 607b2e9

Browse files
authored
fix(ci): debounce canonical main runner admission (#95681)
Compacts canonical pull request CI to 18 bounded Node jobs, preserves isolated subprocess execution, and delays canonical main runner admission to smooth GitHub runner-registration bursts. Verification: focused CI planner/workflow tests passed; fresh autoreview clean. Hosted CI had two pre-existing runtime-config failures on the current main baseline; merged with explicit maintainer override.
1 parent aed6f0a commit 607b2e9

5 files changed

Lines changed: 261 additions & 61 deletions

File tree

.github/workflows/ci.yml

Lines changed: 74 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,32 @@ env:
4141
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
4242

4343
jobs:
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

Comments
 (0)