mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-28 19:11:03 +08:00
fix(ci): reduce Windows CLI test contention
Windows CLI unit tests ran 4-at-a-time on the 4-vCPU runner (custom test-runner default = min(4, cpus)), oversubscribing CPU so heavy real-server test files blew their per-test timeouts. - Cap KILO_TEST_CONCURRENCY=2 on Windows (each file gets ~2 vCPU). - Grow Windows shards 4 -> 6 to absorb the lower per-shard parallelism. - Shard by observed duration instead of file size, so the two heaviest files no longer stack in one shard. - Raise the Windows per-file kill deadline to 600s (KILO_TEST_FILE_TIMEOUT); the heaviest file runs ~270s, only ~30s under the old 300s default. Scoped to the @kilocode/cli custom runner on Windows only. Linux/macOS unchanged; the non-CLI/core suite runs via plain 'bun test' and is not affected (separate lever).
This commit is contained in:
@@ -64,7 +64,10 @@ jobs:
|
||||
exit 0
|
||||
fi
|
||||
echo 'general=true' >> "$GITHUB_OUTPUT"
|
||||
echo 'settings=[{"os":"linux","index":1,"total":2,"host":"blacksmith-4vcpu-ubuntu-2404","run":true,"packages":true},{"os":"linux","index":2,"total":2,"host":"blacksmith-4vcpu-ubuntu-2404","run":true,"packages":false},{"os":"macos","index":1,"total":1,"host":"macos-15","run":true,"packages":true},{"os":"windows","index":1,"total":4,"host":"blacksmith-4vcpu-windows-2025","run":true,"packages":true},{"os":"windows","index":2,"total":4,"host":"blacksmith-4vcpu-windows-2025","run":true,"packages":false},{"os":"windows","index":3,"total":4,"host":"blacksmith-4vcpu-windows-2025","run":true,"packages":false},{"os":"windows","index":4,"total":4,"host":"blacksmith-4vcpu-windows-2025","run":true,"packages":false}]' >> "$GITHUB_OUTPUT"
|
||||
# kilocode_change - Windows is 6 shards (was 4): CLI tests now run at KILO_TEST_CONCURRENCY=2
|
||||
# instead of the default 4 to cut CPU contention on the 4-vCPU runner; more shards keep
|
||||
# per-shard wall-clock within the job timeout despite the lower per-shard parallelism.
|
||||
echo 'settings=[{"os":"linux","index":1,"total":2,"host":"blacksmith-4vcpu-ubuntu-2404","run":true,"packages":true},{"os":"linux","index":2,"total":2,"host":"blacksmith-4vcpu-ubuntu-2404","run":true,"packages":false},{"os":"macos","index":1,"total":1,"host":"macos-15","run":true,"packages":true},{"os":"windows","index":1,"total":6,"host":"blacksmith-4vcpu-windows-2025","run":true,"packages":true},{"os":"windows","index":2,"total":6,"host":"blacksmith-4vcpu-windows-2025","run":true,"packages":false},{"os":"windows","index":3,"total":6,"host":"blacksmith-4vcpu-windows-2025","run":true,"packages":false},{"os":"windows","index":4,"total":6,"host":"blacksmith-4vcpu-windows-2025","run":true,"packages":false},{"os":"windows","index":5,"total":6,"host":"blacksmith-4vcpu-windows-2025","run":true,"packages":false},{"os":"windows","index":6,"total":6,"host":"blacksmith-4vcpu-windows-2025","run":true,"packages":false}]' >> "$GITHUB_OUTPUT"
|
||||
# kilocode_change end
|
||||
unit:
|
||||
# kilocode_change start
|
||||
@@ -172,6 +175,17 @@ jobs:
|
||||
KILO_EXPERIMENTAL_DISABLE_FILEWATCHER: "true" # kilocode_change - was Windows-only; the CLI now starts a watcher per instance, too heavy/racy for unit tests. Watcher tests opt back in.
|
||||
KILO_TEST_PROFILE: ${{ matrix.settings.os == 'macos' && 'darwin' || '' }}
|
||||
KILO_TEST_SHARD: ${{ format('{0}/{1}', matrix.settings.index, matrix.settings.total) }}
|
||||
# kilocode_change - cap parallelism on the 4-vCPU Windows runner. At the default
|
||||
# min(4, cpus)=4, four heavy real-server test files share 4 vCPUs (~1 each) and blow
|
||||
# their per-test timeouts; 2 gives each process real CPU headroom. Windows grows to
|
||||
# 6 shards to absorb the lower per-shard parallelism. Linux/macOS (not timeout
|
||||
# offenders; macOS is a single unsharded job) keep the default.
|
||||
KILO_TEST_CONCURRENCY: ${{ matrix.settings.os == 'windows' && '2' || '' }}
|
||||
# kilocode_change - raise the per-file kill deadline on Windows only. Heavy real-server
|
||||
# files run ~230-270s serially there (vs ~40s on macOS/Linux), leaving only ~30s under
|
||||
# the 300s default; 600s gives healthy-but-slow files real margin without masking hangs
|
||||
# elsewhere (Linux/macOS keep the 300s default).
|
||||
KILO_TEST_FILE_TIMEOUT: ${{ matrix.settings.os == 'windows' && '600000' || '' }}
|
||||
# kilocode_change end
|
||||
|
||||
# kilocode_change start
|
||||
|
||||
@@ -29,9 +29,9 @@ if (argv.includes("--help") || argv.includes("-h")) {
|
||||
"",
|
||||
"Options:",
|
||||
" --ci Enable JUnit XML output to .artifacts/unit/junit.xml",
|
||||
" --concurrency <N> Max parallel processes (default: min(4, CPU count))",
|
||||
" --concurrency <N> Max parallel processes (default: min(4, CPU count), env: KILO_TEST_CONCURRENCY)",
|
||||
" --timeout <ms> Per-test timeout passed to bun test (default: 60000)",
|
||||
" --file-timeout <ms> Per-file process timeout (default: 300000)",
|
||||
" --file-timeout <ms> Per-file process timeout (default: 300000, env: KILO_TEST_FILE_TIMEOUT)",
|
||||
" --retries <N> Extra attempts for failing files (default: 1)",
|
||||
" --profile <name> Run a curated test profile (env: KILO_TEST_PROFILE)",
|
||||
" --shard <N/M> Run one balanced file shard (env: KILO_TEST_SHARD)",
|
||||
@@ -73,9 +73,39 @@ const dots = !verbose && (ci || argv.includes("--dots"))
|
||||
// Cap concurrency at 4 even on bigger runners: the bottleneck is shared
|
||||
// resources (ports, global filesystem like ~/.local/share/kilo), not CPU.
|
||||
// Eight parallel processes was triggering port/FS races, not going faster.
|
||||
const concurrency = opt("concurrency", Math.min(4, os.cpus().length))
|
||||
// kilocode_change start - allow CI to lower concurrency via env. On the 4-vCPU
|
||||
// Windows runner, the default (min(4, cpus)=4) oversubscribes: 4 heavy real-server
|
||||
// test files share 4 vCPUs (~1 each) and blow their per-test timeouts.
|
||||
// `KILO_TEST_CONCURRENCY` lets the workflow throttle Windows without affecting the
|
||||
// local default. An explicit `--concurrency` flag wins.
|
||||
const concurrencyEnv = (() => {
|
||||
const raw = process.env.KILO_TEST_CONCURRENCY?.trim()
|
||||
if (!raw) return undefined
|
||||
const value = Number(raw)
|
||||
if (!Number.isSafeInteger(value) || value < 1) {
|
||||
console.error(`Invalid KILO_TEST_CONCURRENCY "${raw}"; expected a positive integer`)
|
||||
process.exit(2)
|
||||
}
|
||||
return value
|
||||
})()
|
||||
const concurrency = opt("concurrency", concurrencyEnv ?? Math.min(4, os.cpus().length))
|
||||
// kilocode_change end
|
||||
const timeout = opt("timeout", 60000)
|
||||
const deadline = opt("file-timeout", 300000)
|
||||
// kilocode_change start - allow CI to raise the per-file kill deadline via env. On Windows,
|
||||
// heavy real-server files (e.g. config-overlay) legitimately run ~270s serially, only ~30s
|
||||
// under the 300s default; raising it there prevents a slow-but-healthy run from being killed.
|
||||
const fileTimeoutEnv = (() => {
|
||||
const raw = process.env.KILO_TEST_FILE_TIMEOUT?.trim()
|
||||
if (!raw) return undefined
|
||||
const value = Number(raw)
|
||||
if (!Number.isSafeInteger(value) || value < 1) {
|
||||
console.error(`Invalid KILO_TEST_FILE_TIMEOUT "${raw}"; expected a positive integer (ms)`)
|
||||
process.exit(2)
|
||||
}
|
||||
return value
|
||||
})()
|
||||
const deadline = opt("file-timeout", fileTimeoutEnv ?? 300000)
|
||||
// kilocode_change end
|
||||
const retries = opt("retries", 1)
|
||||
const flag = text("profile")
|
||||
const env = process.env.KILO_TEST_PROFILE?.trim() || undefined
|
||||
@@ -156,7 +186,28 @@ if (shard && shard.total > candidates.length) {
|
||||
console.error(`Test shard count ${shard.total} exceeds selected file count ${candidates.length}`)
|
||||
process.exit(2)
|
||||
}
|
||||
const weight = (file: string) => Bun.file(path.join(root, "test", file)).size
|
||||
// kilocode_change start - shard by estimated DURATION, not file size. File size is a poor
|
||||
// proxy: run-process.test.ts is ~7 KB but ~230s, while config-overlay is the single slowest
|
||||
// file — under size-weighting both landed in the same shard, stacking the two heaviest files.
|
||||
// DURATION_HINTS are max observed per-file durations (ms) from real Windows CI runs; the LPT
|
||||
// splitter places the highest-weight files first, so hinted heavy files get spread across
|
||||
// distinct shards. Unhinted files fall back to size (a fine proxy among the fast majority);
|
||||
// hint values (tens of thousands of ms) dominate byte sizes, so heavy files always sort first.
|
||||
// Refresh these from observed CI durations when the suite changes materially.
|
||||
const DURATION_HINTS: Record<string, number> = {
|
||||
"kilocode/server/config-overlay.test.ts": 270_000,
|
||||
"cli/run/run-process.test.ts": 233_000,
|
||||
"snapshot/snapshot.test.ts": 165_000,
|
||||
"session/prompt.test.ts": 128_000,
|
||||
"tool/shell.test.ts": 95_000,
|
||||
"kilocode/background-process.test.ts": 94_000,
|
||||
"provider/provider.test.ts": 90_000,
|
||||
"kilocode/indexing-startup.test.ts": 88_000,
|
||||
"kilocode/daemon.test.ts": 65_000,
|
||||
"tool/task.test.ts": 64_000,
|
||||
}
|
||||
const weight = (file: string) => DURATION_HINTS[file] ?? Bun.file(path.join(root, "test", file)).size
|
||||
// kilocode_change end
|
||||
const files = shard ? TestShard.split(candidates, weight, shard.total)[shard.index - 1] : candidates
|
||||
|
||||
if (files.length === 0) {
|
||||
@@ -189,9 +240,7 @@ const xmldir = ci ? path.join(os.tmpdir(), `opencode-junit-${process.pid}`) : ""
|
||||
if (ci) await fs.mkdir(xmldir, { recursive: true })
|
||||
// kilocode_change start
|
||||
const supplied = process.env[TestCli.ENV]
|
||||
const built = supplied
|
||||
? { binary: supplied, dir: undefined }
|
||||
: { binary: await TestCli.build(root), dir: undefined }
|
||||
const built = supplied ? { binary: supplied, dir: undefined } : { binary: await TestCli.build(root), dir: undefined }
|
||||
|
||||
async function cleanBinary() {
|
||||
if (!built.dir) return
|
||||
|
||||
Reference in New Issue
Block a user