fix: handle parallel installs (#84)

* fix: handle parallel installs

* build: skip unnecessary shims
This commit is contained in:
Kristoffer K 2022-02-02 23:25:33 +01:00 committed by GitHub
parent f17384e620
commit 5cfc6c9df0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 186 additions and 47 deletions

View File

@ -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

45
.pnp.cjs generated
View File

@ -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.

View File

@ -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.`);
}

View File

@ -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": [

View File

@ -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) {

View File

@ -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();
}
});
});
}
}

View File

@ -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.

View File

@ -10,6 +10,7 @@
"lib": ["dom", "es2017", "esnext.asynciterable"],
"module": "commonjs",
"resolveJsonModule": true,
"skipLibCheck": true,
"target": "es2017"
}
}

View File

@ -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: