mirror of https://github.com/nodejs/corepack.git
fix: handle parallel installs (#84)
* fix: handle parallel installs * build: skip unnecessary shims
This commit is contained in:
parent
f17384e620
commit
5cfc6c9df0
|
|
@ -32,6 +32,9 @@ jobs:
|
|||
env:
|
||||
TARGET_BRANCH: ${{github.event.pull_request.base.ref}}
|
||||
|
||||
- name: 'Check for type errors'
|
||||
run: yarn typecheck
|
||||
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
["@babel/preset-typescript", "virtual:0bda696f47fa4339976f909c007f0dc954386da51b8923bf264010d9929618071aa538c29b76d4b6c53f7388a7d83508f2a5027eb81cfc74b39b9d4a0b1a8c5a#npm:7.13.0"],
|
||||
["@types/debug", "npm:4.1.5"],
|
||||
["@types/jest", "npm:26.0.23"],
|
||||
["@types/node", "npm:13.9.2"],
|
||||
["@types/node", "npm:17.0.10"],
|
||||
["@types/rimraf", "npm:3.0.2"],
|
||||
["@types/semver", "npm:7.1.0"],
|
||||
["@types/tar", "npm:4.0.3"],
|
||||
["@types/which", "npm:1.3.2"],
|
||||
|
|
@ -59,6 +60,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
["eslint-plugin-arca", "npm:0.9.5"],
|
||||
["jest", "npm:26.6.3"],
|
||||
["nock", "npm:13.0.4"],
|
||||
["rimraf", "npm:3.0.2"],
|
||||
["semver", "npm:7.1.3"],
|
||||
["supports-color", "npm:7.1.0"],
|
||||
["tar", "npm:6.0.1"],
|
||||
|
|
@ -1461,6 +1463,17 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
"linkType": "HARD",
|
||||
}]
|
||||
]],
|
||||
["@types/glob", [
|
||||
["npm:7.2.0", {
|
||||
"packageLocation": "./.yarn/cache/@types-glob-npm-7.2.0-772334bf9a-6ae717fedf.zip/node_modules/@types/glob/",
|
||||
"packageDependencies": [
|
||||
["@types/glob", "npm:7.2.0"],
|
||||
["@types/minimatch", "npm:3.0.5"],
|
||||
["@types/node", "npm:13.9.0"]
|
||||
],
|
||||
"linkType": "HARD",
|
||||
}]
|
||||
]],
|
||||
["@types/graceful-fs", [
|
||||
["npm:4.1.5", {
|
||||
"packageLocation": "./.yarn/cache/@types-graceful-fs-npm-4.1.5-91d62e1050-d076bb61f4.zip/node_modules/@types/graceful-fs/",
|
||||
|
|
@ -1527,6 +1540,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
"linkType": "HARD",
|
||||
}]
|
||||
]],
|
||||
["@types/minimatch", [
|
||||
["npm:3.0.5", {
|
||||
"packageLocation": "./.yarn/cache/@types-minimatch-npm-3.0.5-802bb0797f-c41d136f67.zip/node_modules/@types/minimatch/",
|
||||
"packageDependencies": [
|
||||
["@types/minimatch", "npm:3.0.5"]
|
||||
],
|
||||
"linkType": "HARD",
|
||||
}]
|
||||
]],
|
||||
["@types/minipass", [
|
||||
["npm:2.2.0", {
|
||||
"packageLocation": "./.yarn/cache/@types-minipass-npm-2.2.0-78f6142b4d-a6e7d103ce.zip/node_modules/@types/minipass/",
|
||||
|
|
@ -1545,10 +1567,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
],
|
||||
"linkType": "HARD",
|
||||
}],
|
||||
["npm:13.9.2", {
|
||||
"packageLocation": "./.yarn/cache/@types-node-npm-13.9.2-d069dc6c73-58e82c03d3.zip/node_modules/@types/node/",
|
||||
["npm:17.0.10", {
|
||||
"packageLocation": "./.yarn/cache/@types-node-npm-17.0.10-c0e46c1462-979e83d642.zip/node_modules/@types/node/",
|
||||
"packageDependencies": [
|
||||
["@types/node", "npm:13.9.2"]
|
||||
["@types/node", "npm:17.0.10"]
|
||||
],
|
||||
"linkType": "HARD",
|
||||
}]
|
||||
|
|
@ -1571,6 +1593,17 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
"linkType": "HARD",
|
||||
}]
|
||||
]],
|
||||
["@types/rimraf", [
|
||||
["npm:3.0.2", {
|
||||
"packageLocation": "./.yarn/cache/@types-rimraf-npm-3.0.2-dd6eb5de99-b47fa302f4.zip/node_modules/@types/rimraf/",
|
||||
"packageDependencies": [
|
||||
["@types/rimraf", "npm:3.0.2"],
|
||||
["@types/glob", "npm:7.2.0"],
|
||||
["@types/node", "npm:13.9.0"]
|
||||
],
|
||||
"linkType": "HARD",
|
||||
}]
|
||||
]],
|
||||
["@types/semver", [
|
||||
["npm:7.1.0", {
|
||||
"packageLocation": "./.yarn/cache/@types-semver-npm-7.1.0-ce58bbc3b4-1f10d876b1.zip/node_modules/@types/semver/",
|
||||
|
|
@ -3059,7 +3092,8 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
["@babel/preset-typescript", "virtual:0bda696f47fa4339976f909c007f0dc954386da51b8923bf264010d9929618071aa538c29b76d4b6c53f7388a7d83508f2a5027eb81cfc74b39b9d4a0b1a8c5a#npm:7.13.0"],
|
||||
["@types/debug", "npm:4.1.5"],
|
||||
["@types/jest", "npm:26.0.23"],
|
||||
["@types/node", "npm:13.9.2"],
|
||||
["@types/node", "npm:17.0.10"],
|
||||
["@types/rimraf", "npm:3.0.2"],
|
||||
["@types/semver", "npm:7.1.0"],
|
||||
["@types/tar", "npm:4.0.3"],
|
||||
["@types/which", "npm:1.3.2"],
|
||||
|
|
@ -3075,6 +3109,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
["eslint-plugin-arca", "npm:0.9.5"],
|
||||
["jest", "npm:26.6.3"],
|
||||
["nock", "npm:13.0.4"],
|
||||
["rimraf", "npm:3.0.2"],
|
||||
["semver", "npm:7.1.3"],
|
||||
["supports-color", "npm:7.1.0"],
|
||||
["tar", "npm:6.0.1"],
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
21
mkshims.ts
21
mkshims.ts
|
|
@ -5,9 +5,14 @@ import path from 'path';
|
|||
import {Engine} from './sources/Engine';
|
||||
import {SupportedPackageManagerSet} from './sources/types';
|
||||
|
||||
const EXCLUDE_SHIMS = new Set([
|
||||
`vcc.js`,
|
||||
]);
|
||||
function shouldGenerateShim(name: string) {
|
||||
if (name === 'vcc.js') {
|
||||
return false;
|
||||
} else if (name.startsWith('vendors')) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const engine = new Engine();
|
||||
|
||||
|
|
@ -38,7 +43,7 @@ async function main() {
|
|||
}
|
||||
|
||||
for (const binaryName of fs.readdirSync(distDir)) {
|
||||
if (EXCLUDE_SHIMS.has(binaryName))
|
||||
if (shouldGenerateShim(binaryName) === false)
|
||||
continue;
|
||||
|
||||
await cmdShim(path.join(distDir, binaryName), path.join(shimsDir, path.basename(binaryName, `.js`)), {createCmdFile: true});
|
||||
|
|
@ -55,12 +60,16 @@ async function main() {
|
|||
const remapPath = (p: string) => path.resolve(__dirname, path.relative(virtualNodewinDir, p));
|
||||
|
||||
const easyStatFs = Object.assign(Object.create(fs), {
|
||||
readFile: (p: string, encoding: string, cb: (err: any, str: string) => void) => fs.readFile(remapPath(p), encoding, cb),
|
||||
readFile: (p: string, encoding: BufferEncoding, cb: (err: any, str: string) => void) => fs.readFile(remapPath(p), encoding, cb),
|
||||
stat: (p: string, cb: () => void) => fs.stat(remapPath(p), cb),
|
||||
});
|
||||
|
||||
for (const binaryName of fs.readdirSync(distDir))
|
||||
for (const binaryName of fs.readdirSync(distDir)) {
|
||||
if (shouldGenerateShim(binaryName) === false)
|
||||
continue;
|
||||
|
||||
await cmdShim(path.join(virtualNodewinDir, `dist/${binaryName}`), path.join(physicalNodewinDir, path.basename(binaryName, `.js`)), {createCmdFile: true, fs: easyStatFs});
|
||||
}
|
||||
|
||||
console.log(`All shims have been generated.`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@
|
|||
"@babel/preset-typescript": "^7.13.0",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/jest": "^26.0.23",
|
||||
"@types/node": "^13.9.2",
|
||||
"@types/node": "^17.0.10",
|
||||
"@types/rimraf": "^3.0.2",
|
||||
"@types/semver": "^7.1.0",
|
||||
"@types/tar": "^4.0.3",
|
||||
"@types/which": "^1.3.2",
|
||||
|
|
@ -43,6 +44,7 @@
|
|||
"eslint-plugin-arca": "^0.9.5",
|
||||
"jest": "^26.0.0",
|
||||
"nock": "^13.0.4",
|
||||
"rimraf": "^3.0.2",
|
||||
"semver": "^7.1.3",
|
||||
"supports-color": "^7.1.0",
|
||||
"tar": "^6.0.1",
|
||||
|
|
@ -56,10 +58,11 @@
|
|||
"which": "^2.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rm -rf dist && webpack && ts-node ./mkshims.ts",
|
||||
"build": "rm -rf dist shims && webpack && ts-node ./mkshims.ts",
|
||||
"corepack": "ts-node ./sources/main.ts",
|
||||
"prepack": "node ./.yarn/releases/*.*js build",
|
||||
"postpack": "rm -rf dist shims",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "yarn jest"
|
||||
},
|
||||
"files": [
|
||||
|
|
|
|||
|
|
@ -87,39 +87,50 @@ export async function installVersion(installTarget: string, locator: Locator, {s
|
|||
const url = spec.url.replace(`{}`, locator.reference);
|
||||
debugUtils.log(`Installing ${locator.name}@${locator.reference} from ${url}`);
|
||||
|
||||
return await fsUtils.mutex(installFolder, async () => {
|
||||
// Creating a temporary folder inside the install folder means that we
|
||||
// are sure it'll be in the same drive as the destination, so we can
|
||||
// just move it there atomically once we are done
|
||||
// Creating a temporary folder inside the install folder means that we
|
||||
// are sure it'll be in the same drive as the destination, so we can
|
||||
// just move it there atomically once we are done
|
||||
|
||||
const tmpFolder = folderUtils.getTemporaryFolder(installTarget);
|
||||
const stream = await httpUtils.fetchUrlStream(url);
|
||||
const tmpFolder = folderUtils.getTemporaryFolder(installTarget);
|
||||
const stream = await httpUtils.fetchUrlStream(url);
|
||||
|
||||
const parsedUrl = new URL(url);
|
||||
const ext = path.posix.extname(parsedUrl.pathname);
|
||||
const parsedUrl = new URL(url);
|
||||
const ext = path.posix.extname(parsedUrl.pathname);
|
||||
|
||||
let outputFile: string | null = null;
|
||||
let outputFile: string | null = null;
|
||||
|
||||
let sendTo: any;
|
||||
if (ext === `.tgz`) {
|
||||
sendTo = tar.x({strip: 1, cwd: tmpFolder});
|
||||
} else if (ext === `.js`) {
|
||||
outputFile = path.join(tmpFolder, path.posix.basename(parsedUrl.pathname));
|
||||
sendTo = fs.createWriteStream(outputFile);
|
||||
}
|
||||
let sendTo: any;
|
||||
if (ext === `.tgz`) {
|
||||
sendTo = tar.x({strip: 1, cwd: tmpFolder});
|
||||
} else if (ext === `.js`) {
|
||||
outputFile = path.join(tmpFolder, path.posix.basename(parsedUrl.pathname));
|
||||
sendTo = fs.createWriteStream(outputFile);
|
||||
}
|
||||
|
||||
stream.pipe(sendTo);
|
||||
stream.pipe(sendTo);
|
||||
|
||||
await new Promise(resolve => {
|
||||
sendTo.on(`finish`, resolve);
|
||||
});
|
||||
|
||||
await fs.promises.mkdir(path.dirname(installFolder), {recursive: true});
|
||||
await fs.promises.rename(tmpFolder, installFolder);
|
||||
|
||||
debugUtils.log(`Install finished`);
|
||||
return installFolder;
|
||||
await new Promise(resolve => {
|
||||
sendTo.on(`finish`, resolve);
|
||||
});
|
||||
|
||||
await fs.promises.mkdir(path.dirname(installFolder), {recursive: true});
|
||||
try {
|
||||
await fs.promises.rename(tmpFolder, installFolder);
|
||||
} catch (err) {
|
||||
if (
|
||||
err.code === `ENOTEMPTY` ||
|
||||
// On Windows the error code is EPERM so we check if it is a directory
|
||||
(err.code === `EPERM` && (await fs.promises.stat(installFolder)).isDirectory())
|
||||
) {
|
||||
debugUtils.log(`Another instance of corepack installed ${locator.name}@${locator.reference}`);
|
||||
await fsUtils.rimraf(tmpFolder);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
debugUtils.log(`Install finished`);
|
||||
return installFolder;
|
||||
}
|
||||
|
||||
export async function runVersion(installSpec: { location: string, spec: PackageManagerSpec }, locator: Locator, binName: string, args: Array<string>, context: Context) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,24 @@
|
|||
export async function mutex<T>(p: string, cb: () => Promise<T>) {
|
||||
return await cb();
|
||||
import fs from 'fs';
|
||||
|
||||
export async function rimraf(path: string) {
|
||||
const [major, minor] = process.versions.node.split(`.`).map(section => Number(section));
|
||||
|
||||
if (major > 14 || (major === 14 && minor >= 14)) {
|
||||
// rm was added in v14.14.0
|
||||
return fs.promises.rm(path, {recursive: true});
|
||||
} else if (major > 12 || (major === 12 && minor >= 10)) {
|
||||
// rmdir got support for recursive in v12.10.0 and was deprecated in v14.14.0
|
||||
return fs.promises.rmdir(path, {recursive: true});
|
||||
} else {
|
||||
const rimraf = await import(`rimraf`);
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
rimraf.default(path, err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -328,3 +328,30 @@ it(`should support running package managers with bin array`, async () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`should handle parallel installs`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
packageManager: `yarn@2.2.2`,
|
||||
});
|
||||
|
||||
await expect(Promise.all([
|
||||
runCli(cwd, [`yarn`, `--version`]),
|
||||
runCli(cwd, [`yarn`, `--version`]),
|
||||
runCli(cwd, [`yarn`, `--version`]),
|
||||
])).resolves.toMatchObject([
|
||||
{
|
||||
stdout: `2.2.2\n`,
|
||||
exitCode: 0,
|
||||
},
|
||||
{
|
||||
stdout: `2.2.2\n`,
|
||||
exitCode: 0,
|
||||
},
|
||||
{
|
||||
stdout: `2.2.2\n`,
|
||||
exitCode: 0,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -10,6 +10,7 @@
|
|||
"lib": ["dom", "es2017", "esnext.asynciterable"],
|
||||
"module": "commonjs",
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"target": "es2017"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
41
yarn.lock
41
yarn.lock
|
|
@ -1065,6 +1065,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/glob@npm:*":
|
||||
version: 7.2.0
|
||||
resolution: "@types/glob@npm:7.2.0"
|
||||
dependencies:
|
||||
"@types/minimatch": "*"
|
||||
"@types/node": "*"
|
||||
checksum: 6ae717fedfdfdad25f3d5a568323926c64f52ef35897bcac8aca8e19bc50c0bd84630bbd063e5d52078b2137d8e7d3c26eabebd1a2f03ff350fff8a91e79fc19
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/graceful-fs@npm:^4.1.2":
|
||||
version: 4.1.5
|
||||
resolution: "@types/graceful-fs@npm:4.1.5"
|
||||
|
|
@ -1123,6 +1133,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/minimatch@npm:*":
|
||||
version: 3.0.5
|
||||
resolution: "@types/minimatch@npm:3.0.5"
|
||||
checksum: c41d136f67231c3131cf1d4ca0b06687f4a322918a3a5adddc87ce90ed9dbd175a3610adee36b106ae68c0b92c637c35e02b58c8a56c424f71d30993ea220b92
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/minipass@npm:*":
|
||||
version: 2.2.0
|
||||
resolution: "@types/minipass@npm:2.2.0"
|
||||
|
|
@ -1139,10 +1156,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:^13.9.2":
|
||||
version: 13.9.2
|
||||
resolution: "@types/node@npm:13.9.2"
|
||||
checksum: 58e82c03d3e51981520119ec5fd104c43762d180106a3ebc7c2c28f1d567b5f505d3d527acc22e7dae8ca9fdaaeb562d3cd77f801f55425eb664afb73c63039b
|
||||
"@types/node@npm:^17.0.10":
|
||||
version: 17.0.10
|
||||
resolution: "@types/node@npm:17.0.10"
|
||||
checksum: 979e83d642a2b4f18fa1a4233f884822c05abc7acd0836024aa964187f8446432b21f7913e72fe2b3927c4a811c27a0b6cd60ac7c4ac6a6762cfbab70782aa6a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -1160,6 +1177,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/rimraf@npm:^3.0.2":
|
||||
version: 3.0.2
|
||||
resolution: "@types/rimraf@npm:3.0.2"
|
||||
dependencies:
|
||||
"@types/glob": "*"
|
||||
"@types/node": "*"
|
||||
checksum: b47fa302f46434cba704d20465861ad250df79467d3d289f9d6490d3aeeb41e8cb32dd80bd1a8fd833d1e185ac719fbf9be12e05ad9ce9be094d8ee8f1405347
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/semver@npm:^7.1.0":
|
||||
version: 7.1.0
|
||||
resolution: "@types/semver@npm:7.1.0"
|
||||
|
|
@ -2365,7 +2392,8 @@ __metadata:
|
|||
"@babel/preset-typescript": ^7.13.0
|
||||
"@types/debug": ^4.1.5
|
||||
"@types/jest": ^26.0.23
|
||||
"@types/node": ^13.9.2
|
||||
"@types/node": ^17.0.10
|
||||
"@types/rimraf": ^3.0.2
|
||||
"@types/semver": ^7.1.0
|
||||
"@types/tar": ^4.0.3
|
||||
"@types/which": ^1.3.2
|
||||
|
|
@ -2381,6 +2409,7 @@ __metadata:
|
|||
eslint-plugin-arca: ^0.9.5
|
||||
jest: ^26.0.0
|
||||
nock: ^13.0.4
|
||||
rimraf: ^3.0.2
|
||||
semver: ^7.1.3
|
||||
supports-color: ^7.1.0
|
||||
tar: ^6.0.1
|
||||
|
|
@ -6116,7 +6145,7 @@ resolve@^1.3.2:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rimraf@npm:^3.0.0":
|
||||
"rimraf@npm:^3.0.0, rimraf@npm:^3.0.2":
|
||||
version: 3.0.2
|
||||
resolution: "rimraf@npm:3.0.2"
|
||||
dependencies:
|
||||
|
|
|
|||
Loading…
Reference in New Issue