-
-
Notifications
You must be signed in to change notification settings - Fork 10.1k
Build: Retry-mechanism for dts bundle-generation #34212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,9 @@ import type { BuildEntries } from './entry-utils'; | |
|
|
||
| const DIR_CODE = join(import.meta.dirname, '..', '..', '..', 'code'); | ||
|
|
||
| const MAX_DTS_ATTEMPTS = 2; | ||
| const RETRY_DELAY_MS = 500; | ||
|
|
||
| export async function generateTypesFiles(cwd: string, data: BuildEntries) { | ||
| const DIR_CWD = cwd; | ||
| const DIR_REL = relative(DIR_CODE, DIR_CWD); | ||
|
|
@@ -27,64 +30,78 @@ export async function generateTypesFiles(cwd: string, data: BuildEntries) { | |
| await Promise.all( | ||
| dtsEntries.map(async (entryPoint) => { | ||
| return limited(async () => { | ||
| let timer: ReturnType<typeof setTimeout> | undefined; | ||
| const dtsProcess = spawn( | ||
| `"${join(ROOT_DIRECTORY, 'node_modules', '.bin', 'jiti')}"`, | ||
| [`"${join(import.meta.dirname, 'dts-process.ts')}"`, `"${entryPoint}"`], | ||
| { | ||
| shell: true, | ||
| cwd: DIR_CWD, | ||
| stdio: ['ignore', 'inherit', 'pipe'], | ||
| } | ||
| ); | ||
| processes.push(dtsProcess); | ||
| for (let attempt = 1; attempt <= MAX_DTS_ATTEMPTS; attempt++) { | ||
| let timer: ReturnType<typeof setTimeout> | undefined; | ||
| const dtsProcess = spawn( | ||
| `"${join(ROOT_DIRECTORY, 'node_modules', '.bin', 'jiti')}"`, | ||
| [`"${join(import.meta.dirname, 'dts-process.ts')}"`, `"${entryPoint}"`], | ||
| { | ||
| shell: true, | ||
| cwd: DIR_CWD, | ||
| stdio: ['ignore', 'inherit', 'pipe'], | ||
| } | ||
| ); | ||
| processes.push(dtsProcess); | ||
|
|
||
| // Filter stderr to exclude messages containing "are imported from external module", which is an ignorable warning from rollup | ||
| dtsProcess.stderr?.on('data', (data) => { | ||
| const message = data.toString(); | ||
| if (!message.includes('are imported from external module')) { | ||
| process.stderr.write(data); | ||
| } | ||
| }); | ||
| // Filter stderr to exclude messages containing "are imported from external module", which is an ignorable warning from rollup | ||
| dtsProcess.stderr?.on('data', (data) => { | ||
| const message = data.toString(); | ||
| if (!message.includes('are imported from external module')) { | ||
| process.stderr.write(data); | ||
| } | ||
| }); | ||
|
|
||
| await Promise.race([ | ||
| new Promise((resolve) => { | ||
| dtsProcess.on('exit', () => { | ||
| resolve(void 0); | ||
| }); | ||
| dtsProcess.on('error', () => { | ||
| resolve(void 0); | ||
| }); | ||
| dtsProcess.on('close', () => { | ||
| resolve(void 0); | ||
| }); | ||
| }), | ||
| new Promise((resolve) => { | ||
| timer = setTimeout(() => { | ||
| console.log('⌛ Timed out generating d.ts files for', entryPoint); | ||
| await Promise.race([ | ||
| new Promise((resolve) => { | ||
| dtsProcess.on('exit', () => { | ||
| resolve(void 0); | ||
| }); | ||
| dtsProcess.on('error', () => { | ||
| resolve(void 0); | ||
| }); | ||
| dtsProcess.on('close', () => { | ||
| resolve(void 0); | ||
| }); | ||
| }), | ||
| new Promise((resolve) => { | ||
| timer = setTimeout(() => { | ||
| console.log('⌛ Timed out generating d.ts files for', entryPoint); | ||
|
|
||
| dtsProcess.kill(408); // timed out | ||
| resolve(void 0); | ||
| }, 120000); | ||
| }), | ||
| ]); | ||
| dtsProcess.kill(408); // timed out | ||
| resolve(void 0); | ||
| }, 120000); | ||
| }), | ||
| ]); | ||
|
|
||
| if (timer) { | ||
| clearTimeout(timer); | ||
| } | ||
| if (timer) { | ||
| clearTimeout(timer); | ||
| } | ||
|
|
||
| if (dtsProcess.exitCode !== 0) { | ||
| console.error( | ||
| '\n❌ Generating types for', | ||
| picocolors.cyan(relative(cwd, entryPoint)), | ||
| ' failed' | ||
| ); | ||
| // If any fail, kill all the other processes and exit (bail) | ||
| processes.forEach((p) => p.kill()); | ||
| processes = []; | ||
| process.exit(dtsProcess.exitCode || 1); | ||
| } else if (!process.env.CI) { | ||
| console.log('✅ Generated types for', picocolors.cyan(join(DIR_REL, entryPoint))); | ||
| if (dtsProcess.exitCode !== 0) { | ||
| if (attempt < MAX_DTS_ATTEMPTS) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you reach max attempts, could it be worth printing an error message that says "This part of the build is known to be flaky, but failed ${MAX_ATTEMPTS} times in a row. Consider re-running the CI job."? It helps external human contributors if we point out, in CI failures, which ones we suspect to be flaky vs which ones are probably caused by their code.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The build might also actually be broken @Sidnioulz |
||
| // Race: parallel DTS can read a .d.ts another process is still writing → invalid. Retry + delay usually fixes (flake in core:compile:production since #33759). | ||
| console.warn( | ||
| `⚠️ DTS failed for ${picocolors.cyan(relative(cwd, entryPoint))}, retrying (${attempt}/${MAX_DTS_ATTEMPTS})...` | ||
| ); | ||
| processes = processes.filter((p) => p !== dtsProcess); | ||
| await new Promise((r) => setTimeout(r, RETRY_DELAY_MS)); | ||
| continue; | ||
| } | ||
| console.error( | ||
| '\n❌ Generating types for', | ||
| picocolors.cyan(relative(cwd, entryPoint)), | ||
| ' failed' | ||
| ); | ||
| // If any fail after all retries, kill all the other processes and exit (bail) | ||
| processes.forEach((p) => p.kill()); | ||
| processes = []; | ||
| process.exit(dtsProcess.exitCode || 1); | ||
| } | ||
|
|
||
| if (!process.env.CI) { | ||
| console.log('✅ Generated types for', picocolors.cyan(join(DIR_REL, entryPoint))); | ||
| } | ||
| break; | ||
| } | ||
| }); | ||
| }) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: storybookjs/storybook
Length of output: 156
🏁 Script executed:
Repository: storybookjs/storybook
Length of output: 1569
🏁 Script executed:
Repository: storybookjs/storybook
Length of output: 314
🏁 Script executed:
Repository: storybookjs/storybook
Length of output: 273
Replace
kill(408)with a valid Unix signal.The value
408is not a recognized Unix signal and will be silently ignored, causing the timeout mechanism to fail. Standard signals are 1–31 (e.g.,SIGTERM= 15,SIGKILL= 9). Use'SIGTERM'to gracefully terminate the process.Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents