Skip to content

Commit 962fddc

Browse files
committed
Add retry logic for package.json reading to handle filesystem delays
Retry up to 3 times with exponential backoff (200ms, 400ms) to handle filesystem flush delays on CI systems that cause truncated JSON files.
1 parent 52d0e54 commit 962fddc

1 file changed

Lines changed: 32 additions & 13 deletions

File tree

scripts/install-npm-packages.mjs

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -629,23 +629,42 @@ async function installPackage(packageInfo) {
629629
const pkgJsonPath = path.join(extractedPath, 'package.json')
630630

631631
// Verify package.json exists and is not empty.
632-
const pkgJsonStats = await fs.stat(pkgJsonPath)
633-
if (pkgJsonStats.size === 0) {
634-
throw new Error('Extracted package.json is empty')
632+
// Retry up to 3 times to handle filesystem flush delays on slow CI systems.
633+
let editablePkgJson
634+
let lastError
635+
for (let attempt = 1; attempt <= 3; attempt += 1) {
636+
try {
637+
// eslint-disable-next-line no-await-in-loop
638+
const pkgJsonStats = await fs.stat(pkgJsonPath)
639+
if (pkgJsonStats.size === 0) {
640+
throw new Error('Extracted package.json is empty')
641+
}
642+
643+
// Remove the "files" field so pnpm includes all files (including tests).
644+
// Also remove unnecessary lifecycle scripts that could interfere with testing.
645+
// eslint-disable-next-line no-await-in-loop
646+
editablePkgJson = await readPackageJson(pkgJsonPath, {
647+
editable: true,
648+
})
649+
break
650+
} catch (error) {
651+
lastError = error
652+
if (attempt < 3) {
653+
// Wait longer on each retry (200ms, 400ms).
654+
// eslint-disable-next-line no-await-in-loop
655+
await new Promise(resolve =>
656+
setTimeout(resolve, attempt * 200),
657+
)
658+
}
659+
}
635660
}
636661

637-
// Remove the "files" field so pnpm includes all files (including tests).
638-
// Also remove unnecessary lifecycle scripts that could interfere with testing.
639-
let editablePkgJson
640-
try {
641-
editablePkgJson = await readPackageJson(pkgJsonPath, {
642-
editable: true,
643-
})
644-
} catch (parseError) {
645-
// If JSON parsing fails, read the file content to help diagnose the issue.
662+
if (!editablePkgJson) {
663+
// All retries failed, add diagnostic info.
664+
const pkgJsonStats = await fs.stat(pkgJsonPath)
646665
const fileContent = await fs.readFile(pkgJsonPath, 'utf8')
647666
throw new Error(
648-
`Invalid package.json: ${parseError.message}. File size: ${pkgJsonStats.size}, Content preview: ${fileContent.slice(0, 200)}`,
667+
`Invalid package.json after 3 retries: ${lastError.message}. File size: ${pkgJsonStats.size}, Content preview: ${fileContent.slice(0, 200)}`,
649668
)
650669
}
651670
const { scripts } = editablePkgJson.content

0 commit comments

Comments
 (0)