Skip to content

Commit 9d9cb3e

Browse files
arcanisaduh95
andauthored
Fixes concurrent downloads of the same Yarn binary (#764)
* Fixes concurrent installs * Adds isNodeError * Update sources/nodeUtils.ts Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com> --------- Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent d04d483 commit 9d9cb3e

File tree

2 files changed

+21
-10
lines changed

2 files changed

+21
-10
lines changed

sources/corepackUtils.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export async function findInstalledVersion(installTarget: string, descriptor: De
8484
try {
8585
cacheDirectory = await fs.promises.opendir(installFolder);
8686
} catch (error) {
87-
if ((error as nodeUtils.NodeError).code === `ENOENT`) {
87+
if (nodeUtils.isNodeError(error) && error.code === `ENOENT`) {
8888
return null;
8989
} else {
9090
throw error;
@@ -181,10 +181,15 @@ async function download(installTarget: string, url: string, algo: string, binPat
181181
try {
182182
await renameSafe(downloadedBin, outputFile);
183183
} catch (err) {
184-
if ((err as nodeUtils.NodeError)?.code === `ENOENT`)
184+
if (nodeUtils.isNodeError(err) && err.code === `ENOENT`)
185185
throw new Error(`Cannot locate '${binPath}' in downloaded tarball`, {cause: err});
186186

187-
throw err;
187+
// It's alright if another process downloaded the same binary in parallel
188+
if (nodeUtils.isNodeError(err) && nodeUtils.isExistError(err)) {
189+
await fs.promises.rm(downloadedBin);
190+
} else {
191+
throw err;
192+
}
188193
}
189194

190195
// Calculate the hash of the bin file
@@ -221,7 +226,7 @@ export async function installVersion(installTarget: string, locator: Locator, {s
221226
bin: corepackData.bin,
222227
};
223228
} catch (err) {
224-
if ((err as nodeUtils.NodeError)?.code !== `ENOENT`) {
229+
if (nodeUtils.isNodeError(err) && err.code !== `ENOENT`) {
225230
throw err;
226231
}
227232
}
@@ -316,9 +321,10 @@ export async function installVersion(installTarget: string, locator: Locator, {s
316321
await renameSafe(tmpFolder, installFolder);
317322
} catch (err) {
318323
if (
319-
(err as nodeUtils.NodeError).code === `ENOTEMPTY` ||
324+
nodeUtils.isNodeError(err) && (
325+
nodeUtils.isExistError(err) ||
320326
// On Windows the error code is EPERM so we check if it is a directory
321-
((err as nodeUtils.NodeError).code === `EPERM` && (await fs.promises.stat(installFolder)).isDirectory())
327+
(err.code === `EPERM` && (await fs.promises.stat(installFolder)).isDirectory()))
322328
) {
323329
debugUtils.log(`Another instance of corepack installed ${locator.name}@${locator.reference}`);
324330
await fs.promises.rm(tmpFolder, {recursive: true, force: true});
@@ -365,10 +371,7 @@ async function renameUnderWindows(oldPath: fs.PathLike, newPath: fs.PathLike) {
365371
break;
366372
} catch (err) {
367373
if (
368-
(
369-
(err as nodeUtils.NodeError).code === `ENOENT` ||
370-
(err as nodeUtils.NodeError).code === `EPERM`
371-
) &&
374+
nodeUtils.isNodeError(err) && (err.code === `ENOENT` || err.code === `EPERM`) &&
372375
i < (retries - 1)
373376
) {
374377
await setTimeoutPromise(100 * 2 ** i);

sources/nodeUtils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ export interface NodeError extends Error {
44
code: string;
55
}
66

7+
export function isNodeError(err: any): err is NodeError {
8+
return !!err?.code;
9+
}
10+
11+
export function isExistError(err: NodeError) {
12+
return err.code === `EEXIST` || err.code === `ENOTEMPTY`;
13+
}
14+
715
function getEndOfLine(content: string) {
816
const matches = content.match(/\r?\n/g);
917
if (matches === null)

0 commit comments

Comments
 (0)