Skip to content

Commit dbf3494

Browse files
authored
fix(ci): retry the Windows build and skip the redundant test-step rebuild (#173)
1 parent d0ff889 commit dbf3494

3 files changed

Lines changed: 58 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ jobs:
1313
unit-test:
1414
uses: sxzz/workflows/.github/workflows/unit-test.yml@main
1515
with:
16+
build: pnpm run ci:build
17+
# The Build step above already produced a fresh dist/ for this exact
18+
# checkout, so skip `test`'s own `build && vitest` — running plain
19+
# vitest halves the number of full-monorepo `turbo run build` passes
20+
# per job, which is where the flaky Windows native-toolchain crash
21+
# (see scripts/ci-retry.ts) shows up.
22+
test: pnpm exec vitest
1623
lint: pnpm run lint && pnpm run knip
1724

1825
e2e:

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"bugs": "https://github.com/devframes/devframe/issues",
1616
"scripts": {
1717
"build": "turbo run build --concurrency=3",
18+
"ci:build": "tsx scripts/ci-retry.ts pnpm run build",
1819
"watch": "pnpm -r run watch",
1920
"play": "tsx scripts/play.ts",
2021
"dev": "tsx scripts/play.ts",

scripts/ci-retry.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { spawnSync } from 'node:child_process'
2+
import process from 'node:process'
3+
4+
/**
5+
* Windows CI runners intermittently crash while spawning devframe's native
6+
* build toolchain (rolldown, via tsdown/vite) with
7+
* `STATUS_DLL_INIT_FAILED` (exit code -1073741502, surfaced by pnpm/turbo
8+
* as 3221225794) under `turbo run build`'s concurrency — an environment
9+
* fault unrelated to the code under test. `unit-test / test
10+
* (windows-latest, *)` in the "CI" workflow has failed on this signature
11+
* across many unrelated commits and packages.
12+
*
13+
* Retries the given command a bounded number of times instead of failing
14+
* the run on this class of transient crash. A genuine compile error fails
15+
* the same way on every attempt, so it still surfaces after the retry
16+
* budget is spent.
17+
*/
18+
19+
const [, , ...commandParts] = process.argv
20+
if (commandParts.length === 0) {
21+
console.error('Usage: tsx scripts/ci-retry.ts <command...>')
22+
process.exit(1)
23+
}
24+
25+
const command = commandParts.join(' ')
26+
const attempts = Number(process.env.CI_RETRY_ATTEMPTS ?? 3)
27+
const delayMs = Number(process.env.CI_RETRY_DELAY_MS ?? 5000)
28+
29+
function sleep(ms: number): Promise<void> {
30+
return new Promise(resolve => setTimeout(resolve, ms))
31+
}
32+
33+
async function main(): Promise<void> {
34+
for (let attempt = 1; attempt <= attempts; attempt++) {
35+
const result = spawnSync(command, { stdio: 'inherit', shell: true })
36+
if (result.status === 0)
37+
return
38+
39+
const code = result.status ?? result.signal ?? 'unknown'
40+
const isLastAttempt = attempt === attempts
41+
console.error(`\n[ci-retry] \`${command}\` failed (exit ${code}), attempt ${attempt}/${attempts}${isLastAttempt ? '' : ` — retrying in ${delayMs}ms`}\n`)
42+
43+
if (isLastAttempt)
44+
process.exit(typeof result.status === 'number' ? result.status : 1)
45+
46+
await sleep(delayMs)
47+
}
48+
}
49+
50+
main()

0 commit comments

Comments
 (0)