Skip to content

Commit

Permalink
🐛 Retry if npm config get command fails due to segmentation fault
Browse files Browse the repository at this point in the history
  • Loading branch information
sounisi5011 committed Jul 2, 2023
1 parent 08745aa commit 9630d74
Showing 1 changed file with 54 additions and 21 deletions.
75 changes: 54 additions & 21 deletions src/utils/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,68 @@ const isDifferentPackageManagerError = (error: unknown): boolean =>
typeof error['stderr'] === 'string' &&
(corepackErrorRegExp.test(error['stdout']) ||
corepackErrorRegExp.test(error['stderr']));
/**
* @returns Returns `false` if the specified `error` is definitely not a segmentation fault error.
* If `true` is returned, it may not be a segmentation fault error.
* @note
* Windows uses the exit code `STATUS_ACCESS_VIOLATION` instead of the signal `SIGSEGV`.
* This value is typically `0xC0000005`, but the actual value is unknown because it is defined in `NtStatus.h`.
* Therefore, we assume that all errors are segmentation fault errors on Windows.
* See https://learn.microsoft.com/en-us/shows/Inside/C0000005,
* https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/specific-exceptions
*/
const maybySegfaultError: (error: unknown) => boolean =
process.platform === 'win32'
? () => true
: (error) => isObject(error) && error['signal'] === 'SIGSEGV';

/**
* Run the "pnpm config get ..." command to try to get the config.
* @returns Return `null` if npm execution is refused by Corepack. Otherwise, return the value of the config.
* @throws If the "pnpm config get ..." command fails, an error is thrown.
*/
async function tryNpmConfigGet(key: string): Promise<string | null> {
return await execFileAsync('npm', ['config', 'get', key], {
env: {
...process.env,
// In Corepack v0.14 and later, the environment variable "COREPACK_ENABLE_STRICT" can be used.
// This allows npm commands to be used even in projects with non-npm package managers defined.
// see https://github.com/nodejs/corepack/tree/v0.14.0#environment-variables
COREPACK_ENABLE_STRICT: '0',
},
})
.then(({ stdout }) => stdout.replace(/\n$/, ''))
.catch((error: unknown) => {
// If an error occurs that is caused by Corepack, ignore the error.
// Note: This conditional expression is required to support older Corepacks where the environment variable "COREPACK_ENABLE_STRICT" is not available.
if (isDifferentPackageManagerError(error)) {
return null;
async function execNpmConfig(): Promise<string | null> {
return await execFileAsync('npm', ['config', 'get', key], {
env: {
...process.env,
// In Corepack v0.14 and later, the environment variable "COREPACK_ENABLE_STRICT" can be used.
// This allows npm commands to be used even in projects with non-npm package managers defined.
// see https://github.com/nodejs/corepack/tree/v0.14.0#environment-variables
COREPACK_ENABLE_STRICT: '0',
},
})
.then(({ stdout }) => stdout.replace(/\n$/, ''))
.catch((error: unknown) => {
// If an error occurs that is caused by Corepack, ignore the error.
// Note: This conditional expression is required to support older Corepacks where the environment variable "COREPACK_ENABLE_STRICT" is not available.
if (isDifferentPackageManagerError(error)) {
return null;
}
// ///// ↓DEBUG↓ /////
if (error !== null && error !== undefined)
Object.assign(error, { __at: 'tryNpmConfigGet()' });
// ///// ↑DEBUG↑ /////
throw error;
});
}

try {
return await execNpmConfig();
} catch (firstError) {
// If the error is a segmentation fault, retry the "npm config get ..." command.
if (maybySegfaultError(firstError)) {
const SEGFAULT_RETRY = 5;
for (let i = 0; i < SEGFAULT_RETRY; i++) {
try {
return await execNpmConfig();
} catch (error) {
if (!maybySegfaultError(error)) throw error;
}
}
// ///// ↓DEBUG↓ /////
if (error !== null && error !== undefined)
Object.assign(error, { __at: 'tryNpmConfigGet()' });
// ///// ↑DEBUG↑ /////
throw error;
});
}
throw firstError;
}
}

const configListCache = new Map<string, string>();
Expand Down

0 comments on commit 9630d74

Please sign in to comment.