From 321fdf0b881716b564b6692e5e9c82eebf535672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Fri, 7 Nov 2025 17:18:57 +0100 Subject: [PATCH 1/3] Fixes concurrent installs --- sources/corepackUtils.ts | 9 +++++++-- sources/nodeUtils.ts | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/sources/corepackUtils.ts b/sources/corepackUtils.ts index 5f510dd39..e8e128ac4 100644 --- a/sources/corepackUtils.ts +++ b/sources/corepackUtils.ts @@ -184,7 +184,12 @@ async function download(installTarget: string, url: string, algo: string, binPat if ((err as nodeUtils.NodeError)?.code === `ENOENT`) throw new Error(`Cannot locate '${binPath}' in downloaded tarball`, {cause: err}); - throw err; + // It's alright if another process downloaded the same binary in parallel + if (nodeUtils.isExistError(err as nodeUtils.NodeError)) { + await fs.promises.rm(downloadedBin); + } else { + throw err; + } } // Calculate the hash of the bin file @@ -316,7 +321,7 @@ export async function installVersion(installTarget: string, locator: Locator, {s await renameSafe(tmpFolder, installFolder); } catch (err) { if ( - (err as nodeUtils.NodeError).code === `ENOTEMPTY` || + nodeUtils.isExistError(err as nodeUtils.NodeError) || // On Windows the error code is EPERM so we check if it is a directory ((err as nodeUtils.NodeError).code === `EPERM` && (await fs.promises.stat(installFolder)).isDirectory()) ) { diff --git a/sources/nodeUtils.ts b/sources/nodeUtils.ts index 35956fbc4..b69390f8e 100644 --- a/sources/nodeUtils.ts +++ b/sources/nodeUtils.ts @@ -4,6 +4,10 @@ export interface NodeError extends Error { code: string; } +export function isExistError(err: NodeError) { + return err.code === `EEXIST` || err.code === `ENOTEMPTY`; +} + function getEndOfLine(content: string) { const matches = content.match(/\r?\n/g); if (matches === null) From 4692b7d86dce3d9e7441f16f3c6b6baa39b26308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Fri, 7 Nov 2025 18:07:15 +0100 Subject: [PATCH 2/3] Adds isNodeError --- sources/corepackUtils.ts | 18 ++++++++---------- sources/nodeUtils.ts | 4 ++++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/sources/corepackUtils.ts b/sources/corepackUtils.ts index e8e128ac4..05fe07456 100644 --- a/sources/corepackUtils.ts +++ b/sources/corepackUtils.ts @@ -84,7 +84,7 @@ export async function findInstalledVersion(installTarget: string, descriptor: De try { cacheDirectory = await fs.promises.opendir(installFolder); } catch (error) { - if ((error as nodeUtils.NodeError).code === `ENOENT`) { + if (nodeUtils.isNodeError(error) && error.code === `ENOENT`) { return null; } else { throw error; @@ -181,11 +181,11 @@ async function download(installTarget: string, url: string, algo: string, binPat try { await renameSafe(downloadedBin, outputFile); } catch (err) { - if ((err as nodeUtils.NodeError)?.code === `ENOENT`) + if (nodeUtils.isNodeError(err) && err.code === `ENOENT`) throw new Error(`Cannot locate '${binPath}' in downloaded tarball`, {cause: err}); // It's alright if another process downloaded the same binary in parallel - if (nodeUtils.isExistError(err as nodeUtils.NodeError)) { + if (nodeUtils.isNodeError(err) && nodeUtils.isExistError(err)) { await fs.promises.rm(downloadedBin); } else { throw err; @@ -226,7 +226,7 @@ export async function installVersion(installTarget: string, locator: Locator, {s bin: corepackData.bin, }; } catch (err) { - if ((err as nodeUtils.NodeError)?.code !== `ENOENT`) { + if (nodeUtils.isNodeError(err) && err.code !== `ENOENT`) { throw err; } } @@ -321,9 +321,10 @@ export async function installVersion(installTarget: string, locator: Locator, {s await renameSafe(tmpFolder, installFolder); } catch (err) { if ( - nodeUtils.isExistError(err as nodeUtils.NodeError) || + nodeUtils.isNodeError(err) && ( + nodeUtils.isExistError(err) || // On Windows the error code is EPERM so we check if it is a directory - ((err as nodeUtils.NodeError).code === `EPERM` && (await fs.promises.stat(installFolder)).isDirectory()) + (err.code === `EPERM` && (await fs.promises.stat(installFolder)).isDirectory())) ) { debugUtils.log(`Another instance of corepack installed ${locator.name}@${locator.reference}`); await fs.promises.rm(tmpFolder, {recursive: true, force: true}); @@ -370,10 +371,7 @@ async function renameUnderWindows(oldPath: fs.PathLike, newPath: fs.PathLike) { break; } catch (err) { if ( - ( - (err as nodeUtils.NodeError).code === `ENOENT` || - (err as nodeUtils.NodeError).code === `EPERM` - ) && + nodeUtils.isNodeError(err) && (err.code === `ENOENT` || err.code === `EPERM`) && i < (retries - 1) ) { await setTimeoutPromise(100 * 2 ** i); diff --git a/sources/nodeUtils.ts b/sources/nodeUtils.ts index b69390f8e..a99620d29 100644 --- a/sources/nodeUtils.ts +++ b/sources/nodeUtils.ts @@ -4,6 +4,10 @@ export interface NodeError extends Error { code: string; } +export function isNodeError(err: any): err is NodeError { + return `code` in err; +} + export function isExistError(err: NodeError) { return err.code === `EEXIST` || err.code === `ENOTEMPTY`; } From 5583ef04649ef0e6c2ec673db429fc1e28388667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Fri, 7 Nov 2025 19:04:33 +0100 Subject: [PATCH 3/3] Update sources/nodeUtils.ts Co-authored-by: Antoine du Hamel --- sources/nodeUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/nodeUtils.ts b/sources/nodeUtils.ts index a99620d29..c83f59bb9 100644 --- a/sources/nodeUtils.ts +++ b/sources/nodeUtils.ts @@ -5,7 +5,7 @@ export interface NodeError extends Error { } export function isNodeError(err: any): err is NodeError { - return `code` in err; + return !!err?.code; } export function isExistError(err: NodeError) {