mirror of https://github.com/nodejs/corepack.git
Compare commits
39 Commits
Author | SHA1 | Date |
---|---|---|
|
15498ddb9a | |
|
9a1794a59f | |
|
783a42fbe3 | |
|
98fd966176 | |
|
5fc3691354 | |
|
b45b3a3244 | |
|
77fff3c1f3 | |
|
273237a110 | |
|
2b43f26135 | |
|
aefde28a63 | |
|
0b94797f96 | |
|
679bcefda5 | |
|
633764f7c4 | |
|
19e3c6861a | |
|
b83bb5ec15 | |
|
7deb39ead0 | |
|
9b95b46f05 | |
|
b456268851 | |
|
53b1fe75c4 | |
|
4be72f6941 | |
|
bb16184b7b | |
|
b0608d1a60 | |
|
351d86c202 | |
|
48ce89827c | |
|
da96687e69 | |
|
f8de7214e5 | |
|
7f7336b9bd | |
|
d26a552daa | |
|
7b193d8498 | |
|
91ea527475 | |
|
c388c64805 | |
|
12e77e5069 | |
|
b0c46078f1 | |
|
bae0839794 | |
|
d5e938795b | |
|
9317593dda | |
|
8163608e49 | |
|
721eeec8c3 | |
|
a7cb9f603c |
|
@ -39,27 +39,26 @@ jobs:
|
|||
run: corepack yarn typecheck
|
||||
|
||||
- name: "Check for linting errors"
|
||||
run: corepack yarn lint
|
||||
run: corepack yarn lint --max-warnings=0
|
||||
|
||||
build-and-test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node:
|
||||
- 18
|
||||
- 20
|
||||
- 22
|
||||
- 23
|
||||
- 24
|
||||
platform:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
- windows-latest
|
||||
|
||||
# Temporarily skipping Node.js 23 under Windows due to issue
|
||||
# https://github.com/nodejs/corepack/issues/597
|
||||
# ci vitest fails "handle integrity checks" on Windows Node.js 23
|
||||
# Temporarily skipping Node.js 24 under Windows due to issue
|
||||
# https://github.com/nodejs/corepack/issues/715
|
||||
# vitest fails "handle integrity checks" on Windows with Node.js 24.x
|
||||
exclude:
|
||||
- node: 23
|
||||
- node: 24
|
||||
platform: windows-latest
|
||||
|
||||
name: "${{matrix.platform}} w/ Node.js ${{matrix.node}}.x"
|
||||
|
|
|
@ -8,12 +8,14 @@ env:
|
|||
YARN_ENABLE_GLOBAL_CACHE: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
release-please:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
outputs:
|
||||
release_created: ${{ steps.release.outputs.release_created }}
|
||||
release_tag: ${{ steps.release.outputs.tag_name }}
|
||||
|
@ -29,6 +31,9 @@ jobs:
|
|||
needs: release-please
|
||||
if: ${{ needs.release-please.outputs.release_created }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
@ -48,10 +53,10 @@ jobs:
|
|||
restore-keys: |
|
||||
${{runner.os}}-yarn-
|
||||
|
||||
- run: corepack yarn install --immutable
|
||||
|
||||
- name: Publish to the npm registry
|
||||
run: |
|
||||
corepack yarn install --immutable
|
||||
corepack yarn npm publish
|
||||
run: corepack yarn npm publish --provenance
|
||||
env:
|
||||
YARN_NPM_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"**/.yarn": true,
|
||||
"**/.pnp.*": true
|
||||
},
|
||||
"eslint.experimental.useFlatConfig": true,
|
||||
"eslint.useFlatConfig": true,
|
||||
"eslint.nodePath": ".yarn/sdks",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
|
|
|
@ -1,20 +1,32 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require eslint/bin/eslint.js
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real eslint/bin/eslint.js your application uses
|
||||
module.exports = absRequire(`eslint/bin/eslint.js`);
|
||||
module.exports = wrapWithUserWrapper(absRequire(`eslint/bin/eslint.js`));
|
||||
|
|
|
@ -1,20 +1,32 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require eslint
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real eslint your application uses
|
||||
module.exports = absRequire(`eslint`);
|
||||
module.exports = wrapWithUserWrapper(absRequire(`eslint`));
|
||||
|
|
|
@ -1,20 +1,32 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require eslint/use-at-your-own-risk
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real eslint/use-at-your-own-risk your application uses
|
||||
module.exports = absRequire(`eslint/use-at-your-own-risk`);
|
||||
module.exports = wrapWithUserWrapper(absRequire(`eslint/use-at-your-own-risk`));
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "eslint",
|
||||
"version": "8.56.0-sdk",
|
||||
"version": "8.57.0-sdk",
|
||||
"main": "./lib/api.js",
|
||||
"type": "commonjs",
|
||||
"bin": {
|
||||
|
|
|
@ -1,20 +1,32 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/bin/tsc
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real typescript/bin/tsc your application uses
|
||||
module.exports = absRequire(`typescript/bin/tsc`);
|
||||
module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsc`));
|
||||
|
|
|
@ -1,20 +1,32 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/bin/tsserver
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real typescript/bin/tsserver your application uses
|
||||
module.exports = absRequire(`typescript/bin/tsserver`);
|
||||
module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsserver`));
|
||||
|
|
|
@ -1,20 +1,32 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/lib/tsc.js
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real typescript/lib/tsc.js your application uses
|
||||
module.exports = absRequire(`typescript/lib/tsc.js`);
|
||||
module.exports = wrapWithUserWrapper(absRequire(`typescript/lib/tsc.js`));
|
||||
|
|
|
@ -1,15 +1,38 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const moduleWrapper = tsserver => {
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/lib/tsserver.js
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
const moduleWrapper = exports => {
|
||||
return wrapWithUserWrapper(moduleWrapperFn(exports));
|
||||
};
|
||||
|
||||
const moduleWrapperFn = tsserver => {
|
||||
if (!process.versions.pnp) {
|
||||
return tsserver;
|
||||
}
|
||||
|
@ -214,11 +237,11 @@ const moduleWrapper = tsserver => {
|
|||
return tsserver;
|
||||
};
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/lib/tsserver.js
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10));
|
||||
// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well.
|
||||
// Ref https://github.com/microsoft/TypeScript/pull/55326
|
||||
if (major > 5 || (major === 5 && minor >= 5)) {
|
||||
moduleWrapper(absRequire(`typescript`));
|
||||
}
|
||||
|
||||
// Defer to the real typescript/lib/tsserver.js your application uses
|
||||
|
|
|
@ -1,15 +1,38 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const moduleWrapper = tsserver => {
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/lib/tsserverlibrary.js
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
const moduleWrapper = exports => {
|
||||
return wrapWithUserWrapper(moduleWrapperFn(exports));
|
||||
};
|
||||
|
||||
const moduleWrapperFn = tsserver => {
|
||||
if (!process.versions.pnp) {
|
||||
return tsserver;
|
||||
}
|
||||
|
@ -214,11 +237,11 @@ const moduleWrapper = tsserver => {
|
|||
return tsserver;
|
||||
};
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/lib/tsserverlibrary.js
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10));
|
||||
// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well.
|
||||
// Ref https://github.com/microsoft/TypeScript/pull/55326
|
||||
if (major > 5 || (major === 5 && minor >= 5)) {
|
||||
moduleWrapper(absRequire(`typescript`));
|
||||
}
|
||||
|
||||
// Defer to the real typescript/lib/tsserverlibrary.js your application uses
|
||||
|
|
|
@ -1,20 +1,32 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real typescript your application uses
|
||||
module.exports = absRequire(`typescript`);
|
||||
module.exports = wrapWithUserWrapper(absRequire(`typescript`));
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "typescript",
|
||||
"version": "5.3.3-sdk",
|
||||
"version": "5.7.3-sdk",
|
||||
"main": "./lib/typescript.js",
|
||||
"type": "commonjs",
|
||||
"bin": {
|
||||
|
|
31
CHANGELOG.md
31
CHANGELOG.md
|
@ -1,5 +1,36 @@
|
|||
# Changelog
|
||||
|
||||
## [0.33.0](https://github.com/nodejs/corepack/compare/v0.32.0...v0.33.0) (2025-06-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Adds guard to avoid stepping on Yarn's feet ([#714](https://github.com/nodejs/corepack/issues/714)) ([5fc3691](https://github.com/nodejs/corepack/commit/5fc3691354eb5bdeca17a9495b234584353f0151))
|
||||
* update package manager versions ([#671](https://github.com/nodejs/corepack/issues/671)) ([b45b3a3](https://github.com/nodejs/corepack/commit/b45b3a3244bacfbaf65188ae8c04209a1e98307d))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* debug text typo ([#698](https://github.com/nodejs/corepack/issues/698)) ([0b94797](https://github.com/nodejs/corepack/commit/0b94797f96e30e46e466873fe7d437d0471cd92c))
|
||||
|
||||
## [0.32.0](https://github.com/nodejs/corepack/compare/v0.31.0...v0.32.0) (2025-02-28)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add limited support for `devEngines` ([#643](https://github.com/nodejs/corepack/issues/643)) ([b456268](https://github.com/nodejs/corepack/commit/b4562688513f23e37e37b0d69a0daff33ca84c8d))
|
||||
* add more informative error when fetching latest stable fails ([#644](https://github.com/nodejs/corepack/issues/644)) ([53b1fe7](https://github.com/nodejs/corepack/commit/53b1fe75c47c06bd72a8b8f8bb699a47c9ca32fb))
|
||||
* add support for `.corepack.env` ([#642](https://github.com/nodejs/corepack/issues/642)) ([9b95b46](https://github.com/nodejs/corepack/commit/9b95b46f05e50fe1c60f05309c210ba8fe4e23c5))
|
||||
* update package manager versions ([#617](https://github.com/nodejs/corepack/issues/617)) ([b83bb5e](https://github.com/nodejs/corepack/commit/b83bb5ec150980c998b9c7053dff307d912cb508))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* do not resolve fallback descriptor when `packageManager` is defined ([#632](https://github.com/nodejs/corepack/issues/632)) ([12e77e5](https://github.com/nodejs/corepack/commit/12e77e506946d42a0de9ce8e68d75af8454d6776))
|
||||
* **doc:** fix link to proxy library ([#636](https://github.com/nodejs/corepack/issues/636)) ([bae0839](https://github.com/nodejs/corepack/commit/bae08397943d4b99437389b4286546361091f4b3))
|
||||
* replace explicit with specify as verb ([#665](https://github.com/nodejs/corepack/issues/665)) ([351d86c](https://github.com/nodejs/corepack/commit/351d86c20226a8c18bfe212be27401f2908b1595))
|
||||
* **use:** do not throw on invalid `packageManager` ([#663](https://github.com/nodejs/corepack/issues/663)) ([4be72f6](https://github.com/nodejs/corepack/commit/4be72f6941afa0c9b2b7d26635016bb7b560df8a))
|
||||
|
||||
## [0.31.0](https://github.com/nodejs/corepack/compare/v0.30.0...v0.31.0) (2025-01-27)
|
||||
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
If you want to build Corepack yourself, you can build the project like this:
|
||||
|
||||
1. Clone this repository.
|
||||
1. Clone this repository. Some restrictions on the cloning location apply to Windows users, read [Contributing on Windows](#contributing-on-windows) for more information.
|
||||
2. Run `yarn install` (or `corepack yarn install` if the global version of
|
||||
`yarn` is not provided by Corepack).
|
||||
3. Run `yarn build` (or `corepack yarn build`).
|
||||
|
@ -11,6 +11,12 @@ The `dist/` directory now contains the corepack build and the shims.
|
|||
Call `node ./dist/corepack --help` and behold.
|
||||
You can also run the tests with `yarn test`.
|
||||
|
||||
## Contributing on Windows
|
||||
|
||||
If you are cloning the repo to a directory on a Microsoft Windows operating system, it is recommended to use the same drive as your Windows `HOMEDRIVE` to avoid build and other issues related to a current restriction with Yarn Plug'n'Play caching.
|
||||
|
||||
If you are unable to use your `HOMEDRIVE`, you may be able to work around the issue by setting the environment variable `YARN_ENABLE_GLOBAL_CACHE` to `false` before running `yarn install` (or `corepack yarn install`).
|
||||
|
||||
# Adding a new package manager
|
||||
|
||||
New package managers can be added by editing the following files:
|
||||
|
|
66
README.md
66
README.md
|
@ -11,7 +11,7 @@ and pnpm without having to install them**.
|
|||
|
||||
### Default Installs
|
||||
|
||||
Corepack is [distributed by default with all recent Node.js versions](https://nodejs.org/api/corepack.html).
|
||||
Corepack is distributed with Node.js from version 14.19.0 up to (but not including) 25.0.0.
|
||||
Run `corepack enable` to install the required Yarn and pnpm binaries on your path.
|
||||
|
||||
### Manual Installs
|
||||
|
@ -41,6 +41,25 @@ is distributed along with Node.js itself.
|
|||
|
||||
</details>
|
||||
|
||||
<details><summary>Update Corepack using npm</summary>
|
||||
|
||||
To install the latest version of Corepack, use:
|
||||
|
||||
```shell
|
||||
npm install -g corepack@latest
|
||||
```
|
||||
|
||||
If Corepack was installed on your system using a Node.js Windows Installer
|
||||
`.msi` package then you might need to remove it before attempting to install a
|
||||
different version of Corepack using npm. You can select the Modify option of the
|
||||
Node.js app settings to access the Windows Installer feature selection, and on
|
||||
the "corepack manager" feature of the Node.js `.msi` package by selecting
|
||||
"Entire feature will be unavailable". See
|
||||
[Repair apps and programs in Windows](https://support.microsoft.com/en-us/windows/repair-apps-and-programs-in-windows-e90eefe4-d0a2-7c1b-dd59-949a9030f317)
|
||||
for instructions on accessing the Windows apps page to modify settings.
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>Install Corepack from source</summary>
|
||||
|
||||
See [`CONTRIBUTING.md`](./CONTRIBUTING.md).
|
||||
|
@ -94,6 +113,35 @@ use in the archive).
|
|||
}
|
||||
```
|
||||
|
||||
#### `devEngines.packageManager`
|
||||
|
||||
When a `devEngines.packageManager` field is defined, and is an object containing
|
||||
a `"name"` field (can also optionally contain `version` and `onFail` fields),
|
||||
Corepack will use it to validate you're using a compatible package manager.
|
||||
|
||||
Depending on the value of `devEngines.packageManager.onFail`:
|
||||
|
||||
- if set to `ignore`, Corepack won't print any warning or error.
|
||||
- if unset or set to `error`, Corepack will throw an error in case of a mismatch.
|
||||
- if set to `warn` or some other value, Corepack will print a warning in case
|
||||
of mismatch.
|
||||
|
||||
If the top-level `packageManager` field is missing, Corepack will use the
|
||||
package manager defined in `devEngines.packageManager` – in which case you must
|
||||
provide a specific version in `devEngines.packageManager.version`, ideally with
|
||||
a hash, as explained in the previous section:
|
||||
|
||||
```json
|
||||
{
|
||||
"devEngines":{
|
||||
"packageManager": {
|
||||
"name": "yarn",
|
||||
"version": "3.2.3+sha224.953c8233f7a92884eee2de69a1b92d1f2ec1655e66d08071ba9a02fa"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Known Good Releases
|
||||
|
||||
When running Corepack within projects that don't list a supported package
|
||||
|
@ -227,6 +275,7 @@ it.
|
|||
|
||||
Unlike `corepack use` this command doesn't take a package manager name nor a
|
||||
version range, as it will always select the latest available version from the
|
||||
range specified in `devEngines.packageManager.version`, or fallback to the
|
||||
same major line. Should you need to upgrade to a new major, use an explicit
|
||||
`corepack use {name}@latest` call (or simply `corepack use {name}`).
|
||||
|
||||
|
@ -237,8 +286,8 @@ same major line. Should you need to upgrade to a new major, use an explicit
|
|||
package manager, and to not update the Last Known Good version when it
|
||||
downloads a new version of the same major line.
|
||||
|
||||
- `COREPACK_ENABLE_AUTO_PIN` can be set to `0` to prevent Corepack from
|
||||
updating the `packageManager` field when it detects that the local package
|
||||
- `COREPACK_ENABLE_AUTO_PIN` can be set to `1` to instruct Corepack to
|
||||
update the `packageManager` field when it detects that the local package
|
||||
doesn't list it. In general we recommend to always list a `packageManager`
|
||||
field (which you can easily set through `corepack use [name]@[version]`), as
|
||||
it ensures that your project installs are always deterministic.
|
||||
|
@ -248,6 +297,7 @@ same major line. Should you need to upgrade to a new major, use an explicit
|
|||
set to `1` to have the URL shown. By default, when Corepack is called
|
||||
explicitly (e.g. `corepack pnpm …`), it is set to `0`; when Corepack is called
|
||||
implicitly (e.g. `pnpm …`), it is set to `1`.
|
||||
The default value cannot be overridden in a `.corepack.env` file.
|
||||
When standard input is a TTY and no CI environment is detected, Corepack will
|
||||
ask for user input before starting the download.
|
||||
|
||||
|
@ -273,6 +323,14 @@ same major line. Should you need to upgrade to a new major, use an explicit
|
|||
project. This means that it will always use the system-wide package manager
|
||||
regardless of what is being specified in the project's `packageManager` field.
|
||||
|
||||
- `COREPACK_ENV_FILE` can be set to `0` to request Corepack to not attempt to
|
||||
load `.corepack.env`; it can be set to a path to specify a different env file.
|
||||
Only keys that start with `COREPACK_` and are not in the exception list
|
||||
(`COREPACK_ENABLE_DOWNLOAD_PROMPT` and `COREPACK_ENV_FILE` are ignored)
|
||||
will be taken into account.
|
||||
For Node.js 18.x users, this setting has no effect as that version doesn't
|
||||
support parsing of `.env` files.
|
||||
|
||||
- `COREPACK_HOME` can be set in order to define where Corepack should install
|
||||
the package managers. By default it is set to `%LOCALAPPDATA%\node\corepack`
|
||||
on Windows, and to `$HOME/.cache/node/corepack` everywhere else.
|
||||
|
@ -294,7 +352,7 @@ same major line. Should you need to upgrade to a new major, use an explicit
|
|||
empty password, explicitly set `COREPACK_NPM_PASSWORD` to an empty string.
|
||||
|
||||
- `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` are supported through
|
||||
[`node-proxy-agent`](https://github.com/TooTallNate/node-proxy-agent).
|
||||
[`proxy-from-env`](https://github.com/Rob--W/proxy-from-env).
|
||||
|
||||
- `COREPACK_INTEGRITY_KEYS` can be set to an empty string or `0` to
|
||||
instruct Corepack to skip integrity checks, or to a JSON string containing
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"definitions": {
|
||||
"npm": {
|
||||
"default": "11.0.0+sha1.7bba7c80740ef1f5b2c5d4cecc55e94912faa5e6",
|
||||
"default": "11.4.1+sha1.80350af543069991de20657ebcd07d9624cfad06",
|
||||
"fetchLatestFrom": {
|
||||
"type": "npm",
|
||||
"package": "npm"
|
||||
|
@ -38,7 +38,7 @@
|
|||
}
|
||||
},
|
||||
"pnpm": {
|
||||
"default": "9.15.4+sha1.ffa0b5c573381e8035b354028ccff97c8e452047",
|
||||
"default": "10.11.0+sha1.4048eeefd564ff1ab248fac3e2854d38245fe2f1",
|
||||
"fetchLatestFrom": {
|
||||
"type": "npm",
|
||||
"package": "pnpm"
|
||||
|
@ -102,7 +102,7 @@
|
|||
"package": "yarn"
|
||||
},
|
||||
"transparent": {
|
||||
"default": "4.6.0+sha224.acd0786f07ffc6c933940eb65fc1d627131ddf5455bddcc295dc90fd",
|
||||
"default": "4.9.1+sha224.4285002185abb91fe2b781f27fd1e078086c37a7b095f6ea4ee25971",
|
||||
"commands": [
|
||||
[
|
||||
"yarn",
|
||||
|
|
|
@ -13,11 +13,13 @@ export default [
|
|||
...yarnpkg,
|
||||
{
|
||||
rules: {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
'no-restricted-globals': [`error`, {
|
||||
name: `fetch`,
|
||||
message: `Use fetch from sources/httpUtils.ts`,
|
||||
}],
|
||||
'@typescript-eslint/no-unused-vars': [`error`, {
|
||||
caughtErrors: `none`,
|
||||
}],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
20
package.json
20
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "corepack",
|
||||
"version": "0.31.0",
|
||||
"version": "0.33.0",
|
||||
"homepage": "https://github.com/nodejs/corepack#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/nodejs/corepack/issues"
|
||||
|
@ -10,37 +10,37 @@
|
|||
"url": "https://github.com/nodejs/corepack.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.1 || ^20.10.0 || >=22.11.0"
|
||||
"node": "^20.10.0 || ^22.11.0 || >=24.0.0"
|
||||
},
|
||||
"exports": {
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"license": "MIT",
|
||||
"packageManager": "yarn@4.3.1+sha224.934d21773e22af4b69a7032a2d3b4cb38c1f7c019624777cc9916b23",
|
||||
"packageManager": "yarn@4.9.0+sha224.dce6c5df199861784bd9b0eecb2a228df97e3f18a02b1bb75ff98383",
|
||||
"devDependencies": {
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/node": "^20.4.6",
|
||||
"@types/proxy-from-env": "^1",
|
||||
"@types/semver": "^7.1.0",
|
||||
"@types/which": "^3.0.0",
|
||||
"@yarnpkg/eslint-config": "^2.0.0",
|
||||
"@yarnpkg/eslint-config": "^3.0.0",
|
||||
"@yarnpkg/fslib": "^3.0.0-rc.48",
|
||||
"@zkochan/cmd-shim": "^6.0.0",
|
||||
"better-sqlite3": "^11.7.2",
|
||||
"clipanion": "patch:clipanion@npm%3A3.2.1#~/.yarn/patches/clipanion-npm-3.2.1-fc9187f56c.patch",
|
||||
"debug": "^4.1.1",
|
||||
"esbuild": "^0.21.0",
|
||||
"eslint": "^8.57.0",
|
||||
"esbuild": "^0.25.0",
|
||||
"eslint": "^9.22.0",
|
||||
"proxy-from-env": "^1.1.0",
|
||||
"semver": "^7.6.3",
|
||||
"supports-color": "^9.0.0",
|
||||
"supports-color": "^10.0.0",
|
||||
"tar": "^7.4.0",
|
||||
"tsx": "^4.16.2",
|
||||
"typescript": "^5.3.3",
|
||||
"typescript": "^5.7.3",
|
||||
"undici": "^6.19.2",
|
||||
"v8-compile-cache": "^2.3.0",
|
||||
"vitest": "^2.0.3",
|
||||
"which": "^4.0.0"
|
||||
"vitest": "^3.0.5",
|
||||
"which": "^5.0.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"undici-types": "6.x"
|
||||
|
|
|
@ -14,9 +14,9 @@ import * as folderUtils from './folderUtil
|
|||
import type {NodeError} from './nodeUtils';
|
||||
import * as semverUtils from './semverUtils';
|
||||
import * as specUtils from './specUtils';
|
||||
import {Config, Descriptor, Locator, PackageManagerSpec} from './types';
|
||||
import {Config, Descriptor, LazyLocator, Locator} from './types';
|
||||
import {SupportedPackageManagers, SupportedPackageManagerSet} from './types';
|
||||
import {isSupportedPackageManager} from './types';
|
||||
import {isSupportedPackageManager, PackageManagerSpec} from './types';
|
||||
|
||||
export type PreparedPackageManagerInfo = Awaited<ReturnType<Engine[`ensurePackageManager`]>>;
|
||||
|
||||
|
@ -237,19 +237,23 @@ export class Engine {
|
|||
*
|
||||
* If the project doesn't include a specification file, we just assume that
|
||||
* whatever the user uses is exactly what they want to use. Since the version
|
||||
* isn't explicited, we fallback on known good versions.
|
||||
* isn't specified, we fallback on known good versions.
|
||||
*
|
||||
* Finally, if the project doesn't exist at all, we ask the user whether they
|
||||
* want to create one in the current project. If they do, we initialize a new
|
||||
* project using the default package managers, and configure it so that we
|
||||
* don't need to ask again in the future.
|
||||
*/
|
||||
async findProjectSpec(initialCwd: string, locator: Locator, {transparent = false}: {transparent?: boolean} = {}): Promise<Descriptor> {
|
||||
async findProjectSpec(initialCwd: string, locator: Locator | LazyLocator, {transparent = false}: {transparent?: boolean} = {}): Promise<Descriptor> {
|
||||
// A locator is a valid descriptor (but not the other way around)
|
||||
const fallbackDescriptor = {name: locator.name, range: `${locator.reference}`};
|
||||
|
||||
if (process.env.COREPACK_ENABLE_PROJECT_SPEC === `0`)
|
||||
if (process.env.COREPACK_ENABLE_PROJECT_SPEC === `0`) {
|
||||
if (typeof locator.reference === `function`)
|
||||
fallbackDescriptor.range = await locator.reference();
|
||||
|
||||
return fallbackDescriptor;
|
||||
}
|
||||
|
||||
if (process.env.COREPACK_ENABLE_STRICT === `0`)
|
||||
transparent = true;
|
||||
|
@ -258,12 +262,19 @@ export class Engine {
|
|||
const result = await specUtils.loadSpec(initialCwd);
|
||||
|
||||
switch (result.type) {
|
||||
case `NoProject`:
|
||||
case `NoProject`: {
|
||||
if (typeof locator.reference === `function`)
|
||||
fallbackDescriptor.range = await locator.reference();
|
||||
|
||||
debugUtils.log(`Falling back to ${fallbackDescriptor.name}@${fallbackDescriptor.range} as no project manifest were found`);
|
||||
return fallbackDescriptor;
|
||||
}
|
||||
|
||||
case `NoSpec`: {
|
||||
if (process.env.COREPACK_ENABLE_AUTO_PIN !== `0`) {
|
||||
if (typeof locator.reference === `function`)
|
||||
fallbackDescriptor.range = await locator.reference();
|
||||
|
||||
if (process.env.COREPACK_ENABLE_AUTO_PIN === `1`) {
|
||||
const resolved = await this.resolveDescriptor(fallbackDescriptor, {allowTags: true});
|
||||
if (resolved === null)
|
||||
throw new UsageError(`Failed to successfully resolve '${fallbackDescriptor.range}' to a valid ${fallbackDescriptor.name} release`);
|
||||
|
@ -277,21 +288,25 @@ export class Engine {
|
|||
await specUtils.setLocalPackageManager(path.dirname(result.target), installSpec);
|
||||
}
|
||||
|
||||
debugUtils.log(`Falling back to ${fallbackDescriptor.name}@${fallbackDescriptor.range} in the absence of "packageManage" field in ${result.target}`);
|
||||
debugUtils.log(`Falling back to ${fallbackDescriptor.name}@${fallbackDescriptor.range} in the absence of "packageManager" field in ${result.target}`);
|
||||
return fallbackDescriptor;
|
||||
}
|
||||
|
||||
case `Found`: {
|
||||
if (result.spec.name !== locator.name) {
|
||||
const spec = result.getSpec();
|
||||
if (spec.name !== locator.name) {
|
||||
if (transparent) {
|
||||
debugUtils.log(`Falling back to ${fallbackDescriptor.name}@${fallbackDescriptor.range} in a ${result.spec.name}@${result.spec.range} project`);
|
||||
if (typeof locator.reference === `function`)
|
||||
fallbackDescriptor.range = await locator.reference();
|
||||
|
||||
debugUtils.log(`Falling back to ${fallbackDescriptor.name}@${fallbackDescriptor.range} in a ${spec.name}@${spec.range} project`);
|
||||
return fallbackDescriptor;
|
||||
} else {
|
||||
throw new UsageError(`This project is configured to use ${result.spec.name} because ${result.target} has a "packageManager" field`);
|
||||
throw new UsageError(`This project is configured to use ${spec.name} because ${result.target} has a "packageManager" field`);
|
||||
}
|
||||
} else {
|
||||
debugUtils.log(`Using ${result.spec.name}@${result.spec.range} as defined in project manifest ${result.target}`);
|
||||
return result.spec;
|
||||
debugUtils.log(`Using ${spec.name}@${spec.range} as defined in project manifest ${result.target}`);
|
||||
return spec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -299,14 +314,14 @@ export class Engine {
|
|||
}
|
||||
|
||||
async executePackageManagerRequest({packageManager, binaryName, binaryVersion}: PackageManagerRequest, {cwd, args}: {cwd: string, args: Array<string>}): Promise<void> {
|
||||
let fallbackLocator: Locator = {
|
||||
let fallbackLocator: Locator | LazyLocator = {
|
||||
name: binaryName as SupportedPackageManagers,
|
||||
reference: undefined as any,
|
||||
};
|
||||
|
||||
let isTransparentCommand = false;
|
||||
if (packageManager != null) {
|
||||
const defaultVersion = binaryVersion || await this.getDefaultVersion(packageManager);
|
||||
const defaultVersion = binaryVersion || (() => this.getDefaultVersion(packageManager));
|
||||
const definition = this.config.definitions[packageManager]!;
|
||||
|
||||
// If all leading segments match one of the patterns defined in the `transparent`
|
||||
|
@ -325,7 +340,7 @@ export class Engine {
|
|||
|
||||
fallbackLocator = {
|
||||
name: packageManager,
|
||||
reference: fallbackReference,
|
||||
reference: fallbackReference as string,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -13,13 +13,13 @@ export abstract class BaseCommand extends Command<Context> {
|
|||
const lookup = await specUtils.loadSpec(this.context.cwd);
|
||||
switch (lookup.type) {
|
||||
case `NoProject`:
|
||||
throw new UsageError(`Couldn't find a project in the local directory - please explicit the package manager to pack, or run this command from a valid project`);
|
||||
throw new UsageError(`Couldn't find a project in the local directory - please specify the package manager to pack, or run this command from a valid project`);
|
||||
|
||||
case `NoSpec`:
|
||||
throw new UsageError(`The local project doesn't feature a 'packageManager' field - please explicit the package manager to pack, or update the manifest to reference it`);
|
||||
throw new UsageError(`The local project doesn't feature a 'packageManager' field nor a 'devEngines.packageManager' field - please specify the package manager to pack, or update the manifest to reference it`);
|
||||
|
||||
default: {
|
||||
return [lookup.spec];
|
||||
return [lookup.range ?? lookup.getSpec()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import fs from 'f
|
|||
import path from 'path';
|
||||
import which from 'which';
|
||||
|
||||
import * as corepackUtils from '../corepackUtils';
|
||||
import {Context} from '../main';
|
||||
import type {NodeError} from '../nodeUtils';
|
||||
import {isSupportedPackageManager, SupportedPackageManagerSetWithoutNpm} from '../types';
|
||||
|
@ -70,6 +71,11 @@ export class DisableCommand extends Command<Context> {
|
|||
async removePosixLink(installDirectory: string, binName: string) {
|
||||
const file = path.join(installDirectory, binName);
|
||||
try {
|
||||
if (binName.includes(`yarn`) && corepackUtils.isYarnSwitchPath(await fs.promises.realpath(file))) {
|
||||
console.warn(`${binName} is already installed in ${file} and points to a Yarn Switch install - skipping`);
|
||||
return;
|
||||
}
|
||||
|
||||
await fs.promises.unlink(file);
|
||||
} catch (err) {
|
||||
if ((err as NodeError).code !== `ENOENT`) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import fs from 'f
|
|||
import path from 'path';
|
||||
import which from 'which';
|
||||
|
||||
import * as corepackUtils from '../corepackUtils';
|
||||
import {Context} from '../main';
|
||||
import {isSupportedPackageManager, SupportedPackageManagerSetWithoutNpm} from '../types';
|
||||
|
||||
|
@ -83,6 +84,12 @@ export class EnableCommand extends Command<Context> {
|
|||
|
||||
if (fs.existsSync(file)) {
|
||||
const currentSymlink = await fs.promises.readlink(file);
|
||||
|
||||
if (binName.includes(`yarn`) && corepackUtils.isYarnSwitchPath(await fs.promises.realpath(file))) {
|
||||
console.warn(`${binName} is already installed in ${file} and points to a Yarn Switch install - skipping`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentSymlink !== symlink) {
|
||||
await fs.promises.unlink(file);
|
||||
} else {
|
||||
|
|
|
@ -36,13 +36,13 @@ export class PrepareCommand extends Command<Context> {
|
|||
const lookup = await specUtils.loadSpec(this.context.cwd);
|
||||
switch (lookup.type) {
|
||||
case `NoProject`:
|
||||
throw new UsageError(`Couldn't find a project in the local directory - please explicit the package manager to pack, or run this command from a valid project`);
|
||||
throw new UsageError(`Couldn't find a project in the local directory - please specify the package manager to pack, or run this command from a valid project`);
|
||||
|
||||
case `NoSpec`:
|
||||
throw new UsageError(`The local project doesn't feature a 'packageManager' field - please explicit the package manager to pack, or update the manifest to reference it`);
|
||||
throw new UsageError(`The local project doesn't feature a 'packageManager' field - please specify the package manager to pack, or update the manifest to reference it`);
|
||||
|
||||
default: {
|
||||
specs.push(lookup.spec);
|
||||
specs.push(lookup.getSpec());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,12 @@ import * as npmRegistryUtils from './npmRegist
|
|||
import {RegistrySpec, Descriptor, Locator, PackageManagerSpec} from './types';
|
||||
import {BinList, BinSpec, InstallSpec, DownloadSpec} from './types';
|
||||
|
||||
const YARN_SWITCH_REGEX = /[/\\]switch[/\\]bin[/\\]/;
|
||||
|
||||
export function isYarnSwitchPath(p: string) {
|
||||
return YARN_SWITCH_REGEX.test(p);
|
||||
}
|
||||
|
||||
export function getRegistryFromPackageManagerSpec(spec: PackageManagerSpec) {
|
||||
return process.env.COREPACK_NPM_REGISTRY
|
||||
? spec.npmRegistry ?? spec.registry
|
||||
|
|
|
@ -38,6 +38,10 @@ function getPackageManagerRequestFromCli(parameter: string | undefined, engine:
|
|||
};
|
||||
}
|
||||
|
||||
function isUsageError(error: any): error is UsageError {
|
||||
return error?.name === `UsageError`;
|
||||
}
|
||||
|
||||
export async function runMain(argv: Array<string>) {
|
||||
const engine = new Engine();
|
||||
|
||||
|
@ -85,8 +89,8 @@ export async function runMain(argv: Array<string>) {
|
|||
cwd: process.cwd(),
|
||||
args: restArgs,
|
||||
});
|
||||
} catch (error: UsageError | any) {
|
||||
if (error?.name === `UsageError`) {
|
||||
} catch (error) {
|
||||
if (isUsageError(error)) {
|
||||
console.error(error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
|
|
@ -38,19 +38,27 @@ export function verifySignature({signatures, integrity, packageName, version}: {
|
|||
packageName: string;
|
||||
version: string;
|
||||
}) {
|
||||
const {npm: keys} = process.env.COREPACK_INTEGRITY_KEYS ?
|
||||
if (!Array.isArray(signatures) || !signatures.length) throw new Error(`No compatible signature found in package metadata`);
|
||||
|
||||
const {npm: trustedKeys} = process.env.COREPACK_INTEGRITY_KEYS ?
|
||||
JSON.parse(process.env.COREPACK_INTEGRITY_KEYS) as typeof defaultConfig.keys :
|
||||
defaultConfig.keys;
|
||||
|
||||
const key = keys.find(({keyid}) => signatures.some(s => s.keyid === keyid));
|
||||
const signature = signatures.find(({keyid}) => keyid === key?.keyid);
|
||||
|
||||
if (key == null || signature == null) throw new Error(`Cannot find matching keyid: ${JSON.stringify({signatures, keys})}`);
|
||||
let signature: typeof signatures[0] | undefined;
|
||||
let key!: string;
|
||||
for (const k of trustedKeys) {
|
||||
signature = signatures.find(({keyid}) => keyid === k.keyid);
|
||||
if (signature != null) {
|
||||
key = k.key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (signature?.sig == null) throw new UsageError(`The package was not signed by any trusted keys: ${JSON.stringify({signatures, trustedKeys}, undefined, 2)}`);
|
||||
|
||||
const verifier = createVerify(`SHA256`);
|
||||
verifier.end(`${packageName}@${version}:${integrity}`);
|
||||
const valid = verifier.verify(
|
||||
`-----BEGIN PUBLIC KEY-----\n${key.key}\n-----END PUBLIC KEY-----`,
|
||||
`-----BEGIN PUBLIC KEY-----\n${key}\n-----END PUBLIC KEY-----`,
|
||||
signature.sig,
|
||||
`base64`,
|
||||
);
|
||||
|
@ -65,10 +73,15 @@ export async function fetchLatestStableVersion(packageName: string) {
|
|||
const {version, dist: {integrity, signatures, shasum}} = metadata;
|
||||
|
||||
if (!shouldSkipIntegrityCheck()) {
|
||||
verifySignature({
|
||||
packageName, version,
|
||||
integrity, signatures,
|
||||
});
|
||||
try {
|
||||
verifySignature({
|
||||
packageName, version,
|
||||
integrity, signatures,
|
||||
});
|
||||
} catch (cause) {
|
||||
// TODO: consider switching to `UsageError` when https://github.com/arcanis/clipanion/issues/157 is fixed
|
||||
throw new Error(`Corepack cannot download the latest stable version of ${packageName}; you can disable signature verification by setting COREPACK_INTEGRITY_CHECK to 0 in your env, or instruct Corepack to use the latest stable release known by this version of Corepack by setting COREPACK_USE_LATEST to 0`, {cause});
|
||||
}
|
||||
}
|
||||
|
||||
return `${version}+${
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
import {UsageError} from 'clipanion';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import semverSatisfies from 'semver/functions/satisfies';
|
||||
import semverValid from 'semver/functions/valid';
|
||||
import semverValidRange from 'semver/ranges/valid';
|
||||
import {parseEnv} from 'util';
|
||||
|
||||
import {PreparedPackageManagerInfo} from './Engine';
|
||||
import * as debugUtils from './debugUtils';
|
||||
import {NodeError} from './nodeUtils';
|
||||
import * as nodeUtils from './nodeUtils';
|
||||
import {Descriptor, isSupportedPackageManager} from './types';
|
||||
import type {LocalEnvFile} from './types';
|
||||
|
||||
const nodeModulesRegExp = /[\\/]node_modules[\\/](@[^\\/]*[\\/])?([^@\\/][^\\/]*)$/;
|
||||
|
||||
|
@ -52,16 +56,87 @@ export function parseSpec(raw: unknown, source: string, {enforceExactVersion = t
|
|||
};
|
||||
}
|
||||
|
||||
type CorepackPackageJSON = {
|
||||
packageManager?: string;
|
||||
devEngines?: {packageManager?: DevEngineDependency};
|
||||
};
|
||||
|
||||
interface DevEngineDependency {
|
||||
name: string;
|
||||
version: string;
|
||||
onFail?: `ignore` | `warn` | `error`;
|
||||
}
|
||||
function warnOrThrow(errorMessage: string, onFail?: DevEngineDependency[`onFail`]) {
|
||||
switch (onFail) {
|
||||
case `ignore`:
|
||||
break;
|
||||
case `error`:
|
||||
case undefined:
|
||||
throw new UsageError(errorMessage);
|
||||
default:
|
||||
console.warn(`! Corepack validation warning: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
function parsePackageJSON(packageJSONContent: CorepackPackageJSON) {
|
||||
const {packageManager: pm} = packageJSONContent;
|
||||
if (packageJSONContent.devEngines?.packageManager != null) {
|
||||
const {packageManager} = packageJSONContent.devEngines;
|
||||
|
||||
if (typeof packageManager !== `object`) {
|
||||
console.warn(`! Corepack only supports objects as valid value for devEngines.packageManager. The current value (${JSON.stringify(packageManager)}) will be ignored.`);
|
||||
return pm;
|
||||
}
|
||||
if (Array.isArray(packageManager)) {
|
||||
console.warn(`! Corepack does not currently support array values for devEngines.packageManager`);
|
||||
return pm;
|
||||
}
|
||||
|
||||
const {name, version, onFail} = packageManager;
|
||||
if (typeof name !== `string` || name.includes(`@`)) {
|
||||
warnOrThrow(`The value of devEngines.packageManager.name ${JSON.stringify(name)} is not a supported string value`, onFail);
|
||||
return pm;
|
||||
}
|
||||
if (version != null && (typeof version !== `string` || !semverValidRange(version))) {
|
||||
warnOrThrow(`The value of devEngines.packageManager.version ${JSON.stringify(version)} is not a valid semver range`, onFail);
|
||||
return pm;
|
||||
}
|
||||
|
||||
debugUtils.log(`devEngines.packageManager defines that ${name}@${version} is the local package manager`);
|
||||
|
||||
if (pm) {
|
||||
if (!pm.startsWith?.(`${name}@`))
|
||||
warnOrThrow(`"packageManager" field is set to ${JSON.stringify(pm)} which does not match the "devEngines.packageManager" field set to ${JSON.stringify(name)}`, onFail);
|
||||
|
||||
else if (version != null && !semverSatisfies(pm.slice(packageManager.name.length + 1), version))
|
||||
warnOrThrow(`"packageManager" field is set to ${JSON.stringify(pm)} which does not match the value defined in "devEngines.packageManager" for ${JSON.stringify(name)} of ${JSON.stringify(version)}`, onFail);
|
||||
|
||||
return pm;
|
||||
}
|
||||
|
||||
|
||||
return `${name}@${version ?? `*`}`;
|
||||
}
|
||||
|
||||
return pm;
|
||||
}
|
||||
|
||||
export async function setLocalPackageManager(cwd: string, info: PreparedPackageManagerInfo) {
|
||||
const lookup = await loadSpec(cwd);
|
||||
|
||||
const range = `range` in lookup && lookup.range;
|
||||
if (range) {
|
||||
if (info.locator.name !== range.name || !semverSatisfies(info.locator.reference, range.range)) {
|
||||
warnOrThrow(`The requested version of ${info.locator.name}@${info.locator.reference} does not match the devEngines specification (${range.name}@${range.range})`, range.onFail);
|
||||
}
|
||||
}
|
||||
|
||||
const content = lookup.type !== `NoProject`
|
||||
? await fs.promises.readFile(lookup.target, `utf8`)
|
||||
: ``;
|
||||
|
||||
const {data, indent} = nodeUtils.readPackageJson(content);
|
||||
|
||||
const previousPackageManager = data.packageManager ?? `unknown`;
|
||||
const previousPackageManager = data.packageManager ?? (range ? `${range.name}@${range.range}` : `unknown`);
|
||||
data.packageManager = `${info.locator.name}@${info.locator.reference}`;
|
||||
|
||||
const newContent = nodeUtils.normalizeLineEndings(content, `${JSON.stringify(data, null, indent)}\n`);
|
||||
|
@ -72,10 +147,17 @@ export async function setLocalPackageManager(cwd: string, info: PreparedPackageM
|
|||
};
|
||||
}
|
||||
|
||||
interface FoundSpecResult {
|
||||
type: `Found`;
|
||||
target: string;
|
||||
getSpec: () => Descriptor;
|
||||
range?: Descriptor & {onFail?: DevEngineDependency[`onFail`]};
|
||||
envFilePath?: string;
|
||||
}
|
||||
export type LoadSpecResult =
|
||||
| {type: `NoProject`, target: string}
|
||||
| {type: `NoSpec`, target: string}
|
||||
| {type: `Found`, target: string, spec: Descriptor};
|
||||
| FoundSpecResult;
|
||||
|
||||
export async function loadSpec(initialCwd: string): Promise<LoadSpecResult> {
|
||||
let nextCwd = initialCwd;
|
||||
|
@ -84,6 +166,8 @@ export async function loadSpec(initialCwd: string): Promise<LoadSpecResult> {
|
|||
let selection: {
|
||||
data: any;
|
||||
manifestPath: string;
|
||||
envFilePath?: string;
|
||||
localEnv: LocalEnvFile;
|
||||
} | null = null;
|
||||
|
||||
while (nextCwd !== currCwd && (!selection || !selection.data.packageManager)) {
|
||||
|
@ -111,19 +195,60 @@ export async function loadSpec(initialCwd: string): Promise<LoadSpecResult> {
|
|||
if (typeof data !== `object` || data === null)
|
||||
throw new UsageError(`Invalid package.json in ${path.relative(initialCwd, manifestPath)}`);
|
||||
|
||||
selection = {data, manifestPath};
|
||||
let localEnv: LocalEnvFile;
|
||||
const envFilePath = path.resolve(currCwd, process.env.COREPACK_ENV_FILE ?? `.corepack.env`);
|
||||
if (process.env.COREPACK_ENV_FILE == `0`) {
|
||||
debugUtils.log(`Skipping env file as configured with COREPACK_ENV_FILE`);
|
||||
localEnv = process.env;
|
||||
} else if (typeof parseEnv !== `function`) {
|
||||
// TODO: remove this block when support for Node.js 18.x is dropped.
|
||||
debugUtils.log(`Skipping env file as it is not supported by the current version of Node.js`);
|
||||
localEnv = process.env;
|
||||
} else {
|
||||
debugUtils.log(`Checking ${envFilePath}`);
|
||||
try {
|
||||
localEnv = {
|
||||
...Object.fromEntries(Object.entries(parseEnv(await fs.promises.readFile(envFilePath, `utf8`))).filter(e => e[0].startsWith(`COREPACK_`))),
|
||||
...process.env,
|
||||
};
|
||||
debugUtils.log(`Successfully loaded env file found at ${envFilePath}`);
|
||||
} catch (err) {
|
||||
if ((err as NodeError)?.code !== `ENOENT`)
|
||||
throw err;
|
||||
|
||||
debugUtils.log(`No env file found at ${envFilePath}`);
|
||||
localEnv = process.env;
|
||||
}
|
||||
}
|
||||
|
||||
selection = {data, manifestPath, localEnv, envFilePath};
|
||||
}
|
||||
|
||||
if (selection === null)
|
||||
return {type: `NoProject`, target: path.join(initialCwd, `package.json`)};
|
||||
|
||||
const rawPmSpec = selection.data.packageManager;
|
||||
let envFilePath: string | undefined;
|
||||
if (selection.localEnv !== process.env) {
|
||||
envFilePath = selection.envFilePath;
|
||||
process.env = selection.localEnv;
|
||||
}
|
||||
|
||||
const rawPmSpec = parsePackageJSON(selection.data);
|
||||
if (typeof rawPmSpec === `undefined`)
|
||||
return {type: `NoSpec`, target: selection.manifestPath};
|
||||
|
||||
debugUtils.log(`${selection.manifestPath} defines ${rawPmSpec} as local package manager`);
|
||||
|
||||
return {
|
||||
type: `Found`,
|
||||
target: selection.manifestPath,
|
||||
spec: parseSpec(rawPmSpec, path.relative(initialCwd, selection.manifestPath)),
|
||||
envFilePath,
|
||||
range: selection.data.devEngines?.packageManager?.version && {
|
||||
name: selection.data.devEngines.packageManager.name,
|
||||
range: selection.data.devEngines.packageManager.version,
|
||||
onFail: selection.data.devEngines.packageManager.onFail,
|
||||
},
|
||||
// Lazy-loading it so we do not throw errors on commands that do not need valid spec.
|
||||
getSpec: () => parseSpec(rawPmSpec, path.relative(initialCwd, selection.manifestPath)),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ export interface DownloadSpec {
|
|||
*/
|
||||
export interface Config {
|
||||
definitions: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
||||
[name in SupportedPackageManagers]?: {
|
||||
/**
|
||||
* Defines the version that needs to be used when running commands within
|
||||
|
@ -145,3 +145,20 @@ export interface Locator {
|
|||
*/
|
||||
reference: string;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export interface LazyLocator {
|
||||
/**
|
||||
* The name of the package manager required.
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* The exact version required.
|
||||
*/
|
||||
reference: () => Promise<string>;
|
||||
}
|
||||
|
||||
export type LocalEnvFile = Record<string, string | undefined>;
|
||||
|
|
|
@ -97,4 +97,23 @@ describe(`DisableCommand`, () => {
|
|||
await expect(sortedEntries).resolves.toEqual([...binNames].sort());
|
||||
});
|
||||
});
|
||||
|
||||
it(`shouldn't remove Yarn binaries if they are in a /switch/ folder`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.mkdirPromise(ppath.join(cwd, `switch/bin`), {recursive: true});
|
||||
await xfs.writeFilePromise(ppath.join(cwd, `switch/bin/yarn`), `hello`);
|
||||
|
||||
await xfs.linkPromise(
|
||||
ppath.join(cwd, `switch/bin/yarn`),
|
||||
ppath.join(cwd, `yarn`),
|
||||
);
|
||||
|
||||
await expect(runCli(cwd, [`disable`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
});
|
||||
|
||||
const file = await xfs.readFilePromise(ppath.join(cwd, `yarn`), `utf8`);
|
||||
expect(file).toBe(`hello`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {Filename, ppath, xfs, npath} from '@yarnpkg/fslib';
|
||||
import {delimiter} from 'node:path';
|
||||
import process from 'node:process';
|
||||
import {describe, beforeEach, it, expect} from 'vitest';
|
||||
import {describe, beforeEach, it, expect, test} from 'vitest';
|
||||
|
||||
import {Engine} from '../sources/Engine';
|
||||
import {SupportedPackageManagers, SupportedPackageManagerSetWithoutNpm} from '../sources/types';
|
||||
|
@ -87,4 +87,42 @@ describe(`EnableCommand`, () => {
|
|||
await expect(sortedEntries).resolves.toEqual(expectedEntries.sort());
|
||||
});
|
||||
});
|
||||
|
||||
test.skipIf(process.platform === `win32`)(`should overwrite existing files`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeFilePromise(ppath.join(cwd, `yarn`), `hello`);
|
||||
|
||||
process.env.PATH = `${npath.fromPortablePath(cwd)}${delimiter}${process.env.PATH}`;
|
||||
await expect(runCli(cwd, [`enable`])).resolves.toMatchObject({
|
||||
stdout: ``,
|
||||
stderr: ``,
|
||||
exitCode: 0,
|
||||
});
|
||||
|
||||
const file = await xfs.readFilePromise(ppath.join(cwd, `yarn`), `utf8`);
|
||||
expect(file).toBe(`hello`);
|
||||
});
|
||||
});
|
||||
|
||||
test.skipIf(process.platform === `win32`)(`shouldn't overwrite Yarn files if they are in a /switch/ folder`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.mkdirPromise(ppath.join(cwd, `switch/bin`), {recursive: true});
|
||||
await xfs.writeFilePromise(ppath.join(cwd, `switch/bin/yarn`), `hello`);
|
||||
|
||||
await xfs.linkPromise(
|
||||
ppath.join(cwd, `switch/bin/yarn`),
|
||||
ppath.join(cwd, `yarn`),
|
||||
);
|
||||
|
||||
process.env.PATH = `${npath.fromPortablePath(cwd)}${delimiter}${process.env.PATH}`;
|
||||
await expect(runCli(cwd, [`enable`])).resolves.toMatchObject({
|
||||
stdout: ``,
|
||||
stderr: ``,
|
||||
exitCode: 0,
|
||||
});
|
||||
|
||||
const file = await xfs.readFilePromise(ppath.join(cwd, `yarn`), `utf8`);
|
||||
expect(file).toBe(`hello`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
133
tests/Up.test.ts
133
tests/Up.test.ts
|
@ -5,30 +5,131 @@ import {describe, beforeEach, it, expect} from 'vitest';
|
|||
import {runCli} from './_runCli';
|
||||
|
||||
beforeEach(async () => {
|
||||
const home = await xfs.mktempPromise();
|
||||
|
||||
// `process.env` is reset after each tests in setupTests.js.
|
||||
process.env.COREPACK_HOME = npath.fromPortablePath(await xfs.mktempPromise());
|
||||
process.env.COREPACK_HOME = npath.fromPortablePath(home);
|
||||
process.env.COREPACK_DEFAULT_TO_LATEST = `0`;
|
||||
|
||||
return async () => {
|
||||
await xfs.removePromise(home, {recursive: true});
|
||||
};
|
||||
});
|
||||
|
||||
describe(`UpCommand`, () => {
|
||||
it(`should upgrade the package manager from the current project`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
|
||||
packageManager: `yarn@2.1.0`,
|
||||
});
|
||||
describe(`should update the "packageManager" field from the current project`, () => {
|
||||
it(`to the same major if no devEngines range`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
|
||||
packageManager: `yarn@2.1.0`,
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`up`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: ``,
|
||||
});
|
||||
await expect(runCli(cwd, [`up`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: ``,
|
||||
stdout: expect.stringMatching(/^Installing yarn@2\.4\.3 in the project\.\.\.\n\n/),
|
||||
});
|
||||
|
||||
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
|
||||
packageManager: `yarn@2.4.3+sha512.8dd9fedc5451829619e526c56f42609ad88ae4776d9d3f9456d578ac085115c0c2f0fb02bb7d57fd2e1b6e1ac96efba35e80a20a056668f61c96934f67694fd0`,
|
||||
});
|
||||
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
|
||||
packageManager: `yarn@2.4.3+sha512.8dd9fedc5451829619e526c56f42609ad88ae4776d9d3f9456d578ac085115c0c2f0fb02bb7d57fd2e1b6e1ac96efba35e80a20a056668f61c96934f67694fd0`,
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `2.4.3\n`,
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `2.4.3\n`,
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`to whichever range devEngines defines`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
|
||||
packageManager: `yarn@1.1.0`,
|
||||
devEngines: {
|
||||
packageManager: {
|
||||
name: `yarn`,
|
||||
version: `1.x || 2.x`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`up`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: ``,
|
||||
stdout: expect.stringMatching(/^Installing yarn@2\.4\.3 in the project\.\.\.\n\n/),
|
||||
});
|
||||
|
||||
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
|
||||
packageManager: `yarn@2.4.3+sha512.8dd9fedc5451829619e526c56f42609ad88ae4776d9d3f9456d578ac085115c0c2f0fb02bb7d57fd2e1b6e1ac96efba35e80a20a056668f61c96934f67694fd0`,
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `2.4.3\n`,
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`to whichever range devEngines defines even if onFail is set to ignore`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
|
||||
packageManager: `pnpm@10.1.0`,
|
||||
devEngines: {
|
||||
packageManager: {
|
||||
name: `yarn`,
|
||||
version: `1.x || 2.x`,
|
||||
onFail: `ignore`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`up`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: ``,
|
||||
stdout: expect.stringMatching(/^Installing yarn@2\.4\.3 in the project\.\.\.\n\n/),
|
||||
});
|
||||
|
||||
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
|
||||
packageManager: `yarn@2.4.3+sha512.8dd9fedc5451829619e526c56f42609ad88ae4776d9d3f9456d578ac085115c0c2f0fb02bb7d57fd2e1b6e1ac96efba35e80a20a056668f61c96934f67694fd0`,
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `2.4.3\n`,
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`should succeed even if no 'packageManager' field`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
process.env.NO_COLOR = `1`;
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
|
||||
devEngines: {
|
||||
packageManager: {
|
||||
name: `yarn`,
|
||||
version: `1.x || 2.x`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`up`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: ``,
|
||||
stdout: expect.stringMatching(/^Installing yarn@2\.4\.3 in the project\.\.\.\n\n/),
|
||||
});
|
||||
|
||||
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
|
||||
packageManager: `yarn@2.4.3+sha512.8dd9fedc5451829619e526c56f42609ad88ae4776d9d3f9456d578ac085115c0c2f0fb02bb7d57fd2e1b6e1ac96efba35e80a20a056668f61c96934f67694fd0`,
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `2.4.3\n`,
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,29 +5,112 @@ import {describe, beforeEach, it, expect} from 'vitest';
|
|||
import {runCli} from './_runCli';
|
||||
|
||||
beforeEach(async () => {
|
||||
const home = await xfs.mktempPromise();
|
||||
|
||||
// `process.env` is reset after each tests in setupTests.js.
|
||||
process.env.COREPACK_HOME = npath.fromPortablePath(await xfs.mktempPromise());
|
||||
process.env.COREPACK_HOME = npath.fromPortablePath(home);
|
||||
process.env.COREPACK_DEFAULT_TO_LATEST = `0`;
|
||||
|
||||
return async () => {
|
||||
await xfs.removePromise(home, {recursive: true});
|
||||
};
|
||||
});
|
||||
|
||||
describe(`UseCommand`, () => {
|
||||
it(`should set the package manager in the current project`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
|
||||
packageManager: `yarn@1.0.0`,
|
||||
});
|
||||
describe(`should set the package manager in the current project`, () => {
|
||||
it(`With an existing 'packageManager' field`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
|
||||
packageManager: `yarn@1.0.0`,
|
||||
license: `MIT`,
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`use`, `yarn@1.22.4`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
});
|
||||
await expect(runCli(cwd, [`use`, `yarn@1.22.4`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: expect.stringMatching(/^Installing yarn@1\.22\.4 in the project\.\.\.\n\n/),
|
||||
stderr: ``,
|
||||
});
|
||||
|
||||
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
|
||||
packageManager: `yarn@1.22.4+sha512.a1833b862fe52169bd6c2a033045a07df5bc6a23595c259e675fed1b2d035ab37abe6ce309720abb6636d68f03615054b6292dc0a70da31c8697fda228b50d18`,
|
||||
});
|
||||
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
|
||||
packageManager: `yarn@1.22.4+sha512.a1833b862fe52169bd6c2a033045a07df5bc6a23595c259e675fed1b2d035ab37abe6ce309720abb6636d68f03615054b6292dc0a70da31c8697fda228b50d18`,
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `1.22.4\n`,
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `1.22.4\n`,
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
it(`with 'devEngines.packageManager' field`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
process.env.NO_COLOR = `1`;
|
||||
const devEngines = {packageManager: {name: `yarn`, version: `2.x`}};
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
|
||||
devEngines,
|
||||
});
|
||||
|
||||
// Should refuse to install an incompatible version:
|
||||
await expect(runCli(cwd, [`use`, `yarn@1.22.4`])).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: ``,
|
||||
stdout: `Installing yarn@1.22.4 in the project...\nUsage Error: The requested version of yarn@1.22.4+sha512.a1833b862fe52169bd6c2a033045a07df5bc6a23595c259e675fed1b2d035ab37abe6ce309720abb6636d68f03615054b6292dc0a70da31c8697fda228b50d18 does not match the devEngines specification (yarn@2.x)\n\n$ corepack use <pattern>\n`,
|
||||
});
|
||||
|
||||
// Should accept setting to a compatible version:
|
||||
await expect(runCli(cwd, [`use`, `yarn@2.4.3`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: ``,
|
||||
stdout: expect.stringMatching(/^Installing yarn@2\.4\.3 in the project\.\.\.\n\n/),
|
||||
});
|
||||
|
||||
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
|
||||
devEngines,
|
||||
packageManager: `yarn@2.4.3+sha512.8dd9fedc5451829619e526c56f42609ad88ae4776d9d3f9456d578ac085115c0c2f0fb02bb7d57fd2e1b6e1ac96efba35e80a20a056668f61c96934f67694fd0`,
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `2.4.3\n`,
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`with 'devEngines.packageManager' and 'packageManager' fields`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
process.env.NO_COLOR = `1`;
|
||||
const devEngines = {packageManager: {name: `yarn`, version: `1.x || 2.x`}};
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
|
||||
devEngines,
|
||||
packageManager: `yarn@1.1.0`,
|
||||
license: `MIT`,
|
||||
});
|
||||
|
||||
// Should refuse to install an incompatible version:
|
||||
await expect(runCli(cwd, [`use`, `yarn@1.22.4`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: ``,
|
||||
stdout: expect.stringMatching(/^Installing yarn@1\.22\.4 in the project\.\.\.\n\n/),
|
||||
});
|
||||
|
||||
// Should accept setting to a compatible version:
|
||||
await expect(runCli(cwd, [`use`, `yarn@2.4.3`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: ``,
|
||||
stdout: expect.stringMatching(/^Installing yarn@2\.4\.3 in the project\.\.\.\n\n/),
|
||||
});
|
||||
|
||||
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
|
||||
devEngines,
|
||||
packageManager: `yarn@2.4.3+sha512.8dd9fedc5451829619e526c56f42609ad88ae4776d9d3f9456d578ac085115c0c2f0fb02bb7d57fd2e1b6e1ac96efba35e80a20a056668f61c96934f67694fd0`,
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `2.4.3\n`,
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -64,4 +147,50 @@ describe(`UseCommand`, () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(`should not care if packageManager is set to an invalid value`, () => {
|
||||
for (const {description, packageManager} of [
|
||||
{
|
||||
description: `when a version range is given`,
|
||||
packageManager: `yarn@1.x`,
|
||||
},
|
||||
{
|
||||
description: `when only the pm name is given`,
|
||||
packageManager: `yarn`,
|
||||
},
|
||||
{
|
||||
description: `when the version is missing`,
|
||||
packageManager: `yarn@`,
|
||||
},
|
||||
{
|
||||
description: `when the field is not a string`,
|
||||
packageManager: [],
|
||||
},
|
||||
]) {
|
||||
it(description, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json`), {
|
||||
packageManager,
|
||||
license: `MIT`, // To avoid warning
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`use`, `yarn@1.22.4`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: ``,
|
||||
stdout: expect.stringMatching(/^Installing yarn@1\.22\.4 in the project\.\.\.\n\n/),
|
||||
});
|
||||
|
||||
await expect(xfs.readJsonPromise(ppath.join(cwd, `package.json`))).resolves.toMatchObject({
|
||||
packageManager: `yarn@1.22.4+sha512.a1833b862fe52169bd6c2a033045a07df5bc6a23595c259e675fed1b2d035ab37abe6ce309720abb6636d68f03615054b6292dc0a70da31c8697fda228b50d18`,
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `1.22.4\n`,
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -79,7 +79,7 @@ const registry = {
|
|||
__proto__: null,
|
||||
yarn: [`1.9998.9999`],
|
||||
pnpm: [`1.9998.9999`],
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
|
||||
'@yarnpkg/cli-dist': [`5.9999.9999`],
|
||||
customPkgManager: [`1.0.0`],
|
||||
};
|
||||
|
@ -111,6 +111,8 @@ function generateVersionMetadata(packageName, version) {
|
|||
const TOKEN_MOCK = `SOME_DUMMY_VALUE`;
|
||||
|
||||
const server = createServer((req, res) => {
|
||||
res.setHeader(`connection`, `close`);
|
||||
|
||||
const auth = req.headers.authorization;
|
||||
|
||||
if (
|
||||
|
@ -129,7 +131,6 @@ const server = createServer((req, res) => {
|
|||
const packageName = req.url.slice(1, slashPosition === -1 ? undefined : slashPosition);
|
||||
if (packageName in registry) {
|
||||
if (req.url === `/${packageName}`) {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
res.end(JSON.stringify({"dist-tags": {
|
||||
latest: registry[packageName].at(-1),
|
||||
}, versions: Object.fromEntries(registry[packageName].map(version =>
|
||||
|
|
|
@ -9,6 +9,6 @@ describe(`key store should be up-to-date`, () => {
|
|||
it(`should contain up-to-date npm keys`, async () => {
|
||||
const r = await globalThis.fetch(new URL(`/-/npm/v1/keys`, DEFAULT_NPM_REGISTRY_URL));
|
||||
expect(r.ok).toBe(true);
|
||||
expect(r.json()).resolves.toMatchObject({keys: defaultConfig.keys.npm});
|
||||
await expect(r.json()).resolves.toMatchObject({keys: defaultConfig.keys.npm});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,31 +1,54 @@
|
|||
import {Filename, ppath, xfs, npath, PortablePath} from '@yarnpkg/fslib';
|
||||
import os from 'node:os';
|
||||
import process from 'node:process';
|
||||
import {beforeEach, describe, expect, it} from 'vitest';
|
||||
import {Filename, ppath, xfs, npath, PortablePath} from '@yarnpkg/fslib';
|
||||
import os from 'node:os';
|
||||
import process from 'node:process';
|
||||
import {afterEach, beforeEach, describe, expect, it} from 'vitest';
|
||||
|
||||
import config from '../config.json';
|
||||
import * as folderUtils from '../sources/folderUtils';
|
||||
import {SupportedPackageManagerSet} from '../sources/types';
|
||||
import config from '../config.json';
|
||||
import * as folderUtils from '../sources/folderUtils';
|
||||
import {SupportedPackageManagerSet} from '../sources/types';
|
||||
|
||||
import {runCli} from './_runCli';
|
||||
import {runCli} from './_runCli';
|
||||
|
||||
|
||||
beforeEach(async () => {
|
||||
const home = await xfs.mktempPromise();
|
||||
|
||||
// `process.env` is reset after each tests in setupTests.js.
|
||||
process.env.COREPACK_HOME = npath.fromPortablePath(await xfs.mktempPromise());
|
||||
process.env.COREPACK_HOME = npath.fromPortablePath(home);
|
||||
process.env.COREPACK_DEFAULT_TO_LATEST = `0`;
|
||||
|
||||
return async () => {
|
||||
await xfs.removePromise(home, {recursive: true});
|
||||
};
|
||||
});
|
||||
|
||||
it(`should refuse to download a package manager if the hash doesn't match`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
packageManager: `yarn@1.22.4+sha1.deadbeef`,
|
||||
});
|
||||
describe(`should refuse to download a package manager if the hash doesn't match`, () => {
|
||||
it(`the one defined in "devEngines.packageManager" field`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
devEngines: {
|
||||
packageManager: {name: `yarn`, version: `1.22.4+sha1.deadbeef`},
|
||||
},
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: /Mismatch hashes/,
|
||||
stdout: ``,
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: expect.stringContaining(`Mismatch hashes`),
|
||||
stdout: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
it(`the one defined in "packageManager" field`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
packageManager: `yarn@1.22.4+sha1.deadbeef`,
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: expect.stringContaining(`Mismatch hashes`),
|
||||
stdout: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -35,7 +58,7 @@ it(`should refuse to download a known package manager from a URL`, async () => {
|
|||
// Package managers known by Corepack cannot be loaded from a URL.
|
||||
await expect(runCli(cwd, [`yarn@https://registry.npmjs.com/yarn/-/yarn-1.22.21.tgz`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: /Illegal use of URL for known package manager/,
|
||||
stderr: expect.stringContaining(`Illegal use of URL for known package manager`),
|
||||
stdout: ``,
|
||||
});
|
||||
|
||||
|
@ -57,7 +80,7 @@ it.fails(`should refuse to download a known package manager from a URL in packag
|
|||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: /Illegal use of URL for known package manager/,
|
||||
stderr: expect.stringContaining(`Illegal use of URL for known package manager`),
|
||||
stdout: ``,
|
||||
});
|
||||
|
||||
|
@ -82,7 +105,7 @@ it(`should require a version to be specified`, async () => {
|
|||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: /expected a semver version/,
|
||||
stderr: `No version specified for yarn in "packageManager" of package.json\n`,
|
||||
stdout: ``,
|
||||
});
|
||||
|
||||
|
@ -92,7 +115,7 @@ it(`should require a version to be specified`, async () => {
|
|||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: /expected a semver version/,
|
||||
stderr: expect.stringContaining(`expected a semver version`),
|
||||
stdout: ``,
|
||||
});
|
||||
|
||||
|
@ -102,7 +125,7 @@ it(`should require a version to be specified`, async () => {
|
|||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: /expected a semver version/,
|
||||
stderr: expect.stringContaining(`expected a semver version`),
|
||||
stdout: ``,
|
||||
});
|
||||
});
|
||||
|
@ -150,6 +173,20 @@ for (const [name, version, expectedVersion = version.split(`+`, 1)[0]] of tested
|
|||
stderr: ``,
|
||||
stdout: `${expectedVersion}\n`,
|
||||
});
|
||||
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
devEngines: {packageManager: {name, version}},
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [name, `--version`])).resolves.toMatchObject(URL.canParse(version) ? {
|
||||
exitCode: 1,
|
||||
stderr: expect.stringMatching(/^The value of devEngines\.packageManager\.version ".+" is not a valid semver range\n$/),
|
||||
stdout: ``,
|
||||
} : {
|
||||
exitCode: 0,
|
||||
stderr: ``,
|
||||
stdout: `${expectedVersion}\n`,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -231,6 +268,298 @@ it(`should ignore the packageManager field when found within a node_modules vend
|
|||
});
|
||||
});
|
||||
|
||||
describe(`should handle invalid devEngines values`, () => {
|
||||
it(`throw on missing version`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as PortablePath), {
|
||||
devEngines: {
|
||||
packageManager: {
|
||||
name: `yarn`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: `Invalid package manager specification in package.json (yarn@*); expected a semver version\n`,
|
||||
stdout: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
it(`throw on invalid version`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as PortablePath), {
|
||||
devEngines: {
|
||||
packageManager: {
|
||||
name: `yarn`,
|
||||
version: `yarn@1.x`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: `The value of devEngines.packageManager.version "yarn@1.x" is not a valid semver range\n`,
|
||||
stdout: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
it(`warn on array values`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as PortablePath), {
|
||||
packageManager: `yarn@1.22.4+sha1.01c1197ca5b27f21edc8bc472cd4c8ce0e5a470e`,
|
||||
devEngines: {
|
||||
packageManager: [{
|
||||
name: `pnpm`,
|
||||
version: `10.x`,
|
||||
},
|
||||
]},
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: `! Corepack does not currently support array values for devEngines.packageManager\n`,
|
||||
stdout: `1.22.4\n`,
|
||||
});
|
||||
});
|
||||
});
|
||||
it(`warn on string values`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as PortablePath), {
|
||||
packageManager: `yarn@1.22.4+sha1.01c1197ca5b27f21edc8bc472cd4c8ce0e5a470e`,
|
||||
devEngines: {
|
||||
packageManager: `pnpm@10.x`,
|
||||
},
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: `! Corepack only supports objects as valid value for devEngines.packageManager. The current value ("pnpm@10.x") will be ignored.\n`,
|
||||
stdout: `1.22.4\n`,
|
||||
});
|
||||
});
|
||||
});
|
||||
it(`warn on number values`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as PortablePath), {
|
||||
packageManager: `yarn@1.22.4+sha1.01c1197ca5b27f21edc8bc472cd4c8ce0e5a470e`,
|
||||
devEngines: {
|
||||
packageManager: 10,
|
||||
},
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: `! Corepack only supports objects as valid value for devEngines.packageManager. The current value (10) will be ignored.\n`,
|
||||
stdout: `1.22.4\n`,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`should use hash from "packageManager" even when "devEngines" defines a different one`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as PortablePath), {
|
||||
packageManager: `yarn@3.0.0-rc.2+sha1.11111`,
|
||||
devEngines: {
|
||||
packageManager: {
|
||||
name: `yarn`,
|
||||
version: `3.0.0-rc.2+sha1.22222`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: expect.stringContaining(`Mismatch hashes. Expected 11111, got`),
|
||||
stdout: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(`should accept range in devEngines only if a specific version is provided`, () => {
|
||||
it(`either in package.json#packageManager field`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as PortablePath), {
|
||||
devEngines: {
|
||||
packageManager: {
|
||||
name: `pnpm`,
|
||||
version: `6.x`,
|
||||
},
|
||||
},
|
||||
});
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: `Invalid package manager specification in package.json (pnpm@6.x); expected a semver version\n`,
|
||||
stdout: ``,
|
||||
});
|
||||
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as PortablePath), {
|
||||
devEngines: {
|
||||
packageManager: {
|
||||
name: `pnpm`,
|
||||
version: `6.x`,
|
||||
},
|
||||
},
|
||||
packageManager: `pnpm@6.6.2+sha224.eb5c0acad3b0f40ecdaa2db9aa5a73134ad256e17e22d1419a2ab073`,
|
||||
});
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: ``,
|
||||
stdout: `6.6.2\n`,
|
||||
});
|
||||
|
||||
// No version should also work
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as PortablePath), {
|
||||
devEngines: {
|
||||
packageManager: {
|
||||
name: `pnpm`,
|
||||
},
|
||||
},
|
||||
packageManager: `pnpm@6.6.2+sha224.eb5c0acad3b0f40ecdaa2db9aa5a73134ad256e17e22d1419a2ab073`,
|
||||
});
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: ``,
|
||||
stdout: `6.6.2\n`,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(`when devEngines.packageManager.name does not match packageManager`, () => {
|
||||
it(`should ignore if devEngines.packageManager.onFail is set to "ignore"`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as PortablePath), {
|
||||
devEngines: {
|
||||
packageManager: {
|
||||
name: `yarn`,
|
||||
onFail: `ignore`,
|
||||
},
|
||||
},
|
||||
packageManager: `pnpm@6.6.2+sha1.7b4d6b176c1b93b5670ed94c24babb7d80c13854`,
|
||||
});
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: ``,
|
||||
stdout: `6.6.2\n`,
|
||||
});
|
||||
});
|
||||
});
|
||||
it(`should warn if devEngines.packageManager.onFail is set to "warn"`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as PortablePath), {
|
||||
devEngines: {
|
||||
packageManager: {
|
||||
name: `yarn`,
|
||||
onFail: `warn`,
|
||||
},
|
||||
},
|
||||
packageManager: `pnpm@6.6.2+sha1.7b4d6b176c1b93b5670ed94c24babb7d80c13854`,
|
||||
});
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: `! Corepack validation warning: "packageManager" field is set to "pnpm@6.6.2+sha1.7b4d6b176c1b93b5670ed94c24babb7d80c13854" which does not match the "devEngines.packageManager" field set to "yarn"\n`,
|
||||
stdout: `6.6.2\n`,
|
||||
});
|
||||
});
|
||||
});
|
||||
it(`should throw if devEngines.packageManager.onFail is set to "error"`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as PortablePath), {
|
||||
devEngines: {
|
||||
packageManager: {
|
||||
name: `yarn`,
|
||||
onFail: `error`,
|
||||
},
|
||||
},
|
||||
packageManager: `pnpm@6.6.2+sha1.7b4d6b176c1b93b5670ed94c24babb7d80c13854`,
|
||||
});
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: `"packageManager" field is set to "pnpm@6.6.2+sha1.7b4d6b176c1b93b5670ed94c24babb7d80c13854" which does not match the "devEngines.packageManager" field set to "yarn"\n`,
|
||||
stdout: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
it(`should throw by default`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as PortablePath), {
|
||||
devEngines: {
|
||||
packageManager: {
|
||||
name: `yarn`,
|
||||
},
|
||||
},
|
||||
packageManager: `pnpm@6.6.2+sha1.7b4d6b176c1b93b5670ed94c24babb7d80c13854`,
|
||||
});
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: `"packageManager" field is set to "pnpm@6.6.2+sha1.7b4d6b176c1b93b5670ed94c24babb7d80c13854" which does not match the "devEngines.packageManager" field set to "yarn"\n`,
|
||||
stdout: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(`should reject if range in devEngines does not match version provided`, () => {
|
||||
it(`unless onFail is set to "ignore"`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as PortablePath), {
|
||||
devEngines: {
|
||||
packageManager: {
|
||||
name: `pnpm`,
|
||||
version: `10.x`,
|
||||
onFail: `ignore`,
|
||||
},
|
||||
},
|
||||
packageManager: `pnpm@6.6.2+sha1.7b4d6b176c1b93b5670ed94c24babb7d80c13854`,
|
||||
});
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: ``,
|
||||
stdout: `6.6.2\n`,
|
||||
});
|
||||
});
|
||||
});
|
||||
it(`unless onFail is set to "warn"`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as PortablePath), {
|
||||
devEngines: {
|
||||
packageManager: {
|
||||
name: `pnpm`,
|
||||
version: `10.x`,
|
||||
onFail: `warn`,
|
||||
},
|
||||
},
|
||||
packageManager: `pnpm@6.6.2+sha1.7b4d6b176c1b93b5670ed94c24babb7d80c13854`,
|
||||
});
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: `! Corepack validation warning: "packageManager" field is set to "pnpm@6.6.2+sha1.7b4d6b176c1b93b5670ed94c24babb7d80c13854" which does not match the value defined in "devEngines.packageManager" for "pnpm" of "10.x"\n`,
|
||||
stdout: `6.6.2\n`,
|
||||
});
|
||||
});
|
||||
});
|
||||
it(`in package.json#packageManager field`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as PortablePath), {
|
||||
devEngines: {
|
||||
packageManager: {
|
||||
name: `pnpm`,
|
||||
version: `10.x`,
|
||||
},
|
||||
},
|
||||
packageManager: `pnpm@6.6.2+sha1.7b4d6b176c1b93b5670ed94c24babb7d80c13854`,
|
||||
});
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: `"packageManager" field is set to "pnpm@6.6.2+sha1.7b4d6b176c1b93b5670ed94c24babb7d80c13854" which does not match the value defined in "devEngines.packageManager" for "pnpm" of "10.x"\n`,
|
||||
stdout: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`should use the closest matching packageManager field`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.mkdirPromise(ppath.join(cwd, `foo` as PortablePath), {recursive: true});
|
||||
|
@ -272,7 +601,7 @@ it(`shouldn't allow using regular Yarn commands on npm-configured projects`, asy
|
|||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: /This project is configured to use npm/,
|
||||
stderr: expect.stringContaining(`This project is configured to use npm`),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -318,18 +647,73 @@ for (const name of SupportedPackageManagerSet) {
|
|||
});
|
||||
}
|
||||
|
||||
it(`should configure the project when calling a package manager on it for the first time`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
describe(`when called on a project without any defined packageManager`, () => {
|
||||
it(`should not modify package.json by default`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
// empty package.json file
|
||||
});
|
||||
|
||||
await runCli(cwd, [`yarn`]);
|
||||
|
||||
const data = await xfs.readJsonPromise(ppath.join(cwd, `package.json` as Filename));
|
||||
|
||||
expect(Object.hasOwn(data, `packageManager`)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
await runCli(cwd, [`yarn`]);
|
||||
it(`should modify package.json if enabled by env`, async () => {
|
||||
process.env.COREPACK_ENABLE_AUTO_PIN = `1`;
|
||||
|
||||
const data = await xfs.readJsonPromise(ppath.join(cwd, `package.json` as Filename));
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
// empty package.json file
|
||||
});
|
||||
|
||||
expect(data).toMatchObject({
|
||||
packageManager: `yarn@${config.definitions.yarn.default}`,
|
||||
await runCli(cwd, [`yarn`]);
|
||||
|
||||
const data = await xfs.readJsonPromise(ppath.join(cwd, `package.json` as Filename));
|
||||
|
||||
expect(data).toMatchObject({
|
||||
packageManager: expect.stringMatching(/^yarn@/),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`should modify package.json if enabled by .corepack.env`, async t => {
|
||||
// Skip that test on Node.js 18.x as it lacks support for .env files.
|
||||
if (process.version.startsWith(`v18.`)) t.skip();
|
||||
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
// empty package.json file
|
||||
});
|
||||
await xfs.writeFilePromise(ppath.join(cwd, `.corepack.env` as Filename), `COREPACK_ENABLE_AUTO_PIN=1\n`);
|
||||
|
||||
await runCli(cwd, [`yarn`]);
|
||||
|
||||
const data = await xfs.readJsonPromise(ppath.join(cwd, `package.json` as Filename));
|
||||
|
||||
expect(data).toMatchObject({
|
||||
packageManager: expect.stringMatching(/^yarn@/),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`should not modify package.json if .corepack.env is disabled`, async () => {
|
||||
process.env.COREPACK_ENV_FILE = `0`;
|
||||
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
// empty package.json file
|
||||
});
|
||||
await xfs.writeFilePromise(ppath.join(cwd, `.corepack.env` as Filename), `COREPACK_ENABLE_AUTO_PIN=1\n`);
|
||||
|
||||
await runCli(cwd, [`yarn`]);
|
||||
|
||||
const data = await xfs.readJsonPromise(ppath.join(cwd, `package.json` as Filename));
|
||||
|
||||
expect(Object.hasOwn(data, `packageManager`)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -473,13 +857,19 @@ it(`should support disabling the network accesses from the environment`, async (
|
|||
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
stdout: ``,
|
||||
stderr: /Network access disabled by the environment/,
|
||||
stderr: expect.stringContaining(`Network access disabled by the environment`),
|
||||
exitCode: 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(`read-only and offline environment`, () => {
|
||||
afterEach(async () => {
|
||||
const home = npath.toPortablePath(process.env.COREPACK_HOME!);
|
||||
await xfs.chmodPromise(ppath.join(home, `lastKnownGood.json`), 0o644);
|
||||
await xfs.chmodPromise(home, 0o755);
|
||||
});
|
||||
|
||||
it(`should support running in project scope`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
// Reset to default
|
||||
|
@ -497,15 +887,10 @@ describe(`read-only and offline environment`, () => {
|
|||
exitCode: 0,
|
||||
});
|
||||
|
||||
// Let corepack discover the latest yarn version.
|
||||
// BUG: This should not be necessary with a fully specified version in package.json plus populated corepack cache.
|
||||
// Engine.executePackageManagerRequest needs to defer the fallback work. This requires a big refactoring.
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
});
|
||||
|
||||
// Make COREPACK_HOME ro
|
||||
const home = npath.toPortablePath(folderUtils.getCorepackHomeFolder());
|
||||
// Make a lastKnownGood.json file with not JSON-parsable content:
|
||||
await xfs.writeFilePromise(ppath.join(home, `lastKnownGood.json`), `{`);
|
||||
await xfs.chmodPromise(ppath.join(home, `lastKnownGood.json`), 0o444);
|
||||
await xfs.chmodPromise(home, 0o555);
|
||||
|
||||
|
@ -811,16 +1196,35 @@ it(`should support package managers in ESM format`, async () => {
|
|||
});
|
||||
});
|
||||
|
||||
it(`should show a warning on stderr before downloading when enable`, async() => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
process.env.COREPACK_ENABLE_DOWNLOAD_PROMPT = `1`;
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
packageManager: `yarn@3.0.0`,
|
||||
describe(`should show a warning on stderr before downloading when enable`, () => {
|
||||
it(`when enabled by the environment`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
process.env.COREPACK_ENABLE_DOWNLOAD_PROMPT = `1`;
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
packageManager: `yarn@3.0.0`,
|
||||
});
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `3.0.0\n`,
|
||||
stderr: `! Corepack is about to download https://repo.yarnpkg.com/3.0.0/packages/yarnpkg-cli/bin/yarn.js\n`,
|
||||
});
|
||||
});
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `3.0.0\n`,
|
||||
stderr: `! Corepack is about to download https://repo.yarnpkg.com/3.0.0/packages/yarnpkg-cli/bin/yarn.js\n`,
|
||||
});
|
||||
|
||||
it(`should ignore setting in .corepack.env`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeFilePromise(
|
||||
ppath.join(cwd, `.corepack.env` as Filename),
|
||||
`COREPACK_ENABLE_DOWNLOAD_PROMPT=1\n`,
|
||||
);
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
packageManager: `yarn@3.0.0`,
|
||||
});
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `3.0.0\n`,
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -850,8 +1254,8 @@ it(`should download yarn classic from custom registry`, async () => {
|
|||
process.env.COREPACK_ENABLE_DOWNLOAD_PROMPT = `1`;
|
||||
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: /^1\.\d+\.\d+\r?\n$/,
|
||||
stderr: /^! Corepack is about to download https:\/\/registry\.npmmirror\.com\/yarn\/-\/yarn-1\.\d+\.\d+\.tgz\r?\n$/,
|
||||
stdout: expect.stringMatching(/^1\.\d+\.\d+\r?\n$/),
|
||||
stderr: expect.stringMatching(/^! Corepack is about to download https:\/\/registry\.npmmirror\.com\/yarn\/-\/yarn-1\.\d+\.\d+\.tgz\r?\n$/),
|
||||
});
|
||||
|
||||
// Should keep working with cache
|
||||
|
@ -899,7 +1303,7 @@ it(`should download latest pnpm from custom registry`, async () => {
|
|||
await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `pnpm: Hello from custom registry\n`,
|
||||
stderr: /^! The local project doesn't define a 'packageManager' field\. Corepack will now add one referencing pnpm@1\.9998\.9999@sha1\./,
|
||||
stderr: ``,
|
||||
});
|
||||
|
||||
// Should keep working with cache
|
||||
|
@ -911,6 +1315,156 @@ it(`should download latest pnpm from custom registry`, async () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe(`should pick up COREPACK_INTEGRITY_KEYS from env`, () => {
|
||||
beforeEach(() => {
|
||||
process.env.AUTH_TYPE = `COREPACK_NPM_TOKEN`; // See `_registryServer.mjs`
|
||||
process.env.COREPACK_DEFAULT_TO_LATEST = `1`;
|
||||
});
|
||||
|
||||
it(`from env variable`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stdout: ``,
|
||||
stderr: expect.stringContaining(`No compatible signature found in package metadata`),
|
||||
});
|
||||
|
||||
process.env.COREPACK_INTEGRITY_KEYS = `0`;
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `pnpm: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`from .corepack.env file`, async t => {
|
||||
// Skip that test on Node.js 18.x as it lacks support for .env files.
|
||||
if (process.version.startsWith(`v18.`)) t.skip();
|
||||
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stdout: ``,
|
||||
stderr: expect.stringContaining(`No compatible signature found in package metadata`),
|
||||
});
|
||||
|
||||
await xfs.writeFilePromise(ppath.join(cwd, `.corepack.env` as Filename), `COREPACK_INTEGRITY_KEYS=0\n`);
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `pnpm: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`from env file defined by COREPACK_ENV_FILE`, async t => {
|
||||
// Skip that test on Node.js 18.x as it lacks support for .env files.
|
||||
if (process.version.startsWith(`v18.`)) t.skip();
|
||||
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
});
|
||||
|
||||
await xfs.writeFilePromise(ppath.join(cwd, `.corepack.env` as Filename), `COREPACK_INTEGRITY_KEYS={}\n`);
|
||||
await xfs.writeFilePromise(ppath.join(cwd, `.other.env` as Filename), `COREPACK_INTEGRITY_KEYS=0\n`);
|
||||
|
||||
// By default, Corepack should be using .corepack.env and fail.
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stdout: ``,
|
||||
stderr: expect.stringContaining(`No compatible signature found in package metadata`),
|
||||
});
|
||||
|
||||
process.env.COREPACK_ENV_FILE = `.other.env`;
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `pnpm: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`from env even if there's a .corepack.env file`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
});
|
||||
|
||||
await xfs.writeFilePromise(ppath.join(cwd, `.corepack.env` as Filename), `COREPACK_INTEGRITY_KEYS={}\n`);
|
||||
|
||||
// By default, Corepack should be using .corepack.env (or the built-in ones on Node.js 18.x) and fail.
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stdout: ``,
|
||||
stderr: expect.stringContaining(`No compatible signature found in package metadata`),
|
||||
});
|
||||
|
||||
process.env.COREPACK_INTEGRITY_KEYS = ``;
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `pnpm: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`should ignore .corepack.env file if COREPACK_ENV_FILE is set to 0`, async t => {
|
||||
// Skip that test on Node.js 18.x as it lacks support for .env files.
|
||||
if (process.version.startsWith(`v18.`)) t.skip();
|
||||
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
});
|
||||
|
||||
await xfs.writeFilePromise(ppath.join(cwd, `.corepack.env` as Filename), `COREPACK_INTEGRITY_KEYS=0\n`);
|
||||
|
||||
process.env.COREPACK_ENV_FILE = `0`;
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stdout: ``,
|
||||
stderr: expect.stringContaining(`No compatible signature found in package metadata`),
|
||||
});
|
||||
|
||||
delete process.env.COREPACK_ENV_FILE;
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `pnpm: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`from env file defined by COREPACK_ENV_FILE`, async t => {
|
||||
// Skip that test on Node.js 18.x as it lacks support for .env files.
|
||||
if (process.version.startsWith(`v18.`)) t.skip();
|
||||
|
||||
process.env.COREPACK_ENV_FILE = `.other.env`;
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||
});
|
||||
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stdout: ``,
|
||||
stderr: expect.stringContaining(`No compatible signature found in package metadata`),
|
||||
});
|
||||
|
||||
await xfs.writeFilePromise(ppath.join(cwd, `.other.env` as Filename), `COREPACK_INTEGRITY_KEYS=0\n`);
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `pnpm: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
for (const authType of [`COREPACK_NPM_REGISTRY`, `COREPACK_NPM_TOKEN`, `COREPACK_NPM_PASSWORD`, `PROXY`]) {
|
||||
describe(`custom registry with auth ${authType}`, () => {
|
||||
beforeEach(() => {
|
||||
|
@ -967,122 +1521,139 @@ for (const authType of [`COREPACK_NPM_REGISTRY`, `COREPACK_NPM_TOKEN`, `COREPACK
|
|||
describe(`handle integrity checks`, () => {
|
||||
beforeEach(() => {
|
||||
process.env.AUTH_TYPE = `COREPACK_NPM_TOKEN`; // See `_registryServer.mjs`
|
||||
process.env.COREPACK_DEFAULT_TO_LATEST = `1`;
|
||||
});
|
||||
|
||||
it(`should return no error when signature matches`, async () => {
|
||||
process.env.TEST_INTEGRITY = `valid`; // See `_registryServer.mjs`
|
||||
describe(`when signature matches`, () => {
|
||||
beforeEach(() => {
|
||||
process.env.TEST_INTEGRITY = `valid`; // See `_registryServer.mjs`
|
||||
});
|
||||
|
||||
it(`should return no error when calling 'corepack use'`, async () => {
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
// Skip rest of the test on Windows & Node.js 18.x as it inevitably times out otherwise.
|
||||
if (process.version.startsWith(`v18.`) && os.platform() === `win32`) return;
|
||||
|
||||
// Removing home directory to force the "re-download"
|
||||
await xfs.rmPromise(process.env.COREPACK_HOME as any, {recursive: true});
|
||||
|
||||
await Promise.all([
|
||||
expect(runCli(cwd, [`use`, `pnpm`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `Installing pnpm@1.9998.9999 in the project...\n\npnpm: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
}),
|
||||
expect(runCli(cwd, [`use`, `yarn@1.x`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `Installing yarn@1.9998.9999 in the project...\n\nyarn: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
}),
|
||||
expect(runCli(cwd, [`use`, `yarn@latest`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `Installing yarn@5.9999.9999 in the project...\n\nyarn: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it(`should return no error when fetching latest version`, async () => {
|
||||
process.env.COREPACK_DEFAULT_TO_LATEST = `1`;
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await Promise.all([
|
||||
expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `pnpm: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
}),
|
||||
expect(runCli(cwd, [`yarn@1.x`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `yarn: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
}),
|
||||
expect(runCli(cwd, [`yarn@5.x`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `yarn: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
it(`should return an error when signature does not match with a range`, async () => {
|
||||
process.env.TEST_INTEGRITY = `invalid_signature`; // See `_registryServer.mjs`
|
||||
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await Promise.all([
|
||||
expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `pnpm: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
}),
|
||||
expect(runCli(cwd, [`yarn@1.x`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `yarn: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
}),
|
||||
expect(runCli(cwd, [`yarn@5.x`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `yarn: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
}),
|
||||
]);
|
||||
|
||||
// Skip rest of the test on Windows & Node.js 18.x as it inevitably times out otherwise.
|
||||
if (process.version.startsWith(`v18.`) && os.platform() === `win32`) return;
|
||||
|
||||
// Removing home directory to force the "re-download"
|
||||
await xfs.rmPromise(process.env.COREPACK_HOME as any, {recursive: true});
|
||||
|
||||
await Promise.all([
|
||||
expect(runCli(cwd, [`use`, `pnpm`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `Installing pnpm@1.9998.9999 in the project...\n\npnpm: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
}),
|
||||
expect(runCli(cwd, [`use`, `yarn@1.x`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `Installing yarn@1.9998.9999 in the project...\n\nyarn: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
}),
|
||||
expect(runCli(cwd, [`use`, `yarn@latest`], true)).resolves.toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: `Installing yarn@5.9999.9999 in the project...\n\nyarn: Hello from custom registry\n`,
|
||||
stderr: ``,
|
||||
}),
|
||||
]);
|
||||
await expect(runCli(cwd, [`pnpm@1.x`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: expect.stringContaining(`Signature does not match`),
|
||||
stdout: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
it(`should return an error when signature does not match with a tag`, async () => {
|
||||
process.env.TEST_INTEGRITY = `invalid_signature`; // See `_registryServer.mjs`
|
||||
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await expect(runCli(cwd, [`pnpm@1.x`, `--version`], true)).resolves.toMatchObject({
|
||||
await expect(runCli(cwd, [`pnpm@latest`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: /Signature does not match/,
|
||||
stdout: ``,
|
||||
});
|
||||
await expect(runCli(cwd, [`yarn@stable`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: /Signature does not match/,
|
||||
stderr: expect.stringContaining(`Signature does not match`),
|
||||
stdout: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
it(`should return an error when hash does not match without a tag`, async () => {
|
||||
process.env.TEST_INTEGRITY = `invalid_integrity`; // See `_registryServer.mjs`
|
||||
process.env.COREPACK_DEFAULT_TO_LATEST = `1`; // Necessary as the version defined in `config.json` does not exist on the custom registry.
|
||||
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: /Mismatch hashes. Expected [a-f0-9]{128}, got [a-f0-9]{128}/,
|
||||
stderr: expect.stringMatching(/Mismatch hashes. Expected [a-f0-9]{128}, got [a-f0-9]{128}/),
|
||||
stdout: ``,
|
||||
});
|
||||
// A second time to validate the invalid version was not cached.
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: /Mismatch hashes. Expected [a-f0-9]{128}, got [a-f0-9]{128}/,
|
||||
stderr: expect.stringMatching(/Mismatch hashes. Expected [a-f0-9]{128}, got [a-f0-9]{128}/),
|
||||
stdout: ``,
|
||||
});
|
||||
await expect(runCli(cwd, [`yarn`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: /Mismatch hashes. Expected [a-f0-9]{128}, got [a-f0-9]{128}/,
|
||||
stderr: expect.stringMatching(/Mismatch hashes. Expected [a-f0-9]{128}, got [a-f0-9]{128}/),
|
||||
stdout: ``,
|
||||
});
|
||||
await expect(runCli(cwd, [`use`, `pnpm`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stdout: /Mismatch hashes. Expected [a-f0-9]{128}, got [a-f0-9]{128}/,
|
||||
stdout: expect.stringMatching(/Mismatch hashes. Expected [a-f0-9]{128}, got [a-f0-9]{128}/),
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
});
|
||||
it(`should return an error when signature does not match without a tag`, async () => {
|
||||
process.env.TEST_INTEGRITY = `invalid_signature`; // See `_registryServer.mjs`
|
||||
process.env.COREPACK_DEFAULT_TO_LATEST = `1`; // Necessary as the version defined in `config.json` does not exist on the custom registry.
|
||||
|
||||
await xfs.mktempPromise(async cwd => {
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: /Signature does not match/,
|
||||
stderr: expect.stringContaining(`Signature does not match`),
|
||||
stdout: ``,
|
||||
});
|
||||
// A second time to validate the invalid version was not cached.
|
||||
await expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: /Signature does not match/,
|
||||
stderr: expect.stringContaining(`Signature does not match`),
|
||||
stdout: ``,
|
||||
});
|
||||
await expect(runCli(cwd, [`yarn`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: /Signature does not match/,
|
||||
stderr: expect.stringContaining(`Signature does not match`),
|
||||
stdout: ``,
|
||||
});
|
||||
await expect(runCli(cwd, [`use`, `pnpm`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stdout: /Signature does not match/,
|
||||
stdout: expect.stringContaining(`Signature does not match`),
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
|
@ -1093,12 +1664,12 @@ describe(`handle integrity checks`, () => {
|
|||
await xfs.mktempPromise(async cwd => {
|
||||
await expect(runCli(cwd, [`yarn@1.9998.9999`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: /Signature does not match/,
|
||||
stderr: expect.stringContaining(`Signature does not match`),
|
||||
stdout: ``,
|
||||
});
|
||||
await expect(runCli(cwd, [`use`, `yarn@1.9998.9999`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stdout: /Signature does not match/,
|
||||
stdout: expect.stringContaining(`Signature does not match`),
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
|
@ -1109,12 +1680,12 @@ describe(`handle integrity checks`, () => {
|
|||
await xfs.mktempPromise(async cwd => {
|
||||
await expect(runCli(cwd, [`yarn@1.9998.9999`, `--version`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stderr: /Mismatch hashes. Expected [a-f0-9]{128}, got [a-f0-9]{128}/,
|
||||
stderr: expect.stringMatching(/Mismatch hashes. Expected [a-f0-9]{128}, got [a-f0-9]{128}/),
|
||||
stdout: ``,
|
||||
});
|
||||
await expect(runCli(cwd, [`use`, `yarn@1.9998.9999`], true)).resolves.toMatchObject({
|
||||
exitCode: 1,
|
||||
stdout: /Mismatch hashes. Expected [a-f0-9]{128}, got [a-f0-9]{128}/,
|
||||
stdout: expect.stringMatching(/Mismatch hashes. Expected [a-f0-9]{128}, got [a-f0-9]{128}/),
|
||||
stderr: ``,
|
||||
});
|
||||
});
|
||||
|
|
BIN
tests/nocks.db
BIN
tests/nocks.db
Binary file not shown.
Loading…
Reference in New Issue