From 8d357b66e03e3971d38b8a3fc3cc1fd1f8c5cf8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Tue, 22 Mar 2022 23:36:48 +0100 Subject: [PATCH 01/12] Uses exclusively Corepack by default --- .yarn/versions/2847fa24.yml | 23 ++++ .../sources/commands/set/version.ts | 102 ++++++++++-------- 2 files changed, 81 insertions(+), 44 deletions(-) create mode 100644 .yarn/versions/2847fa24.yml diff --git a/.yarn/versions/2847fa24.yml b/.yarn/versions/2847fa24.yml new file mode 100644 index 000000000000..0076a5a53a10 --- /dev/null +++ b/.yarn/versions/2847fa24.yml @@ -0,0 +1,23 @@ +releases: + "@yarnpkg/cli": major + "@yarnpkg/plugin-essentials": major + +declined: + - "@yarnpkg/plugin-compat" + - "@yarnpkg/plugin-constraints" + - "@yarnpkg/plugin-dlx" + - "@yarnpkg/plugin-init" + - "@yarnpkg/plugin-interactive-tools" + - "@yarnpkg/plugin-nm" + - "@yarnpkg/plugin-npm-cli" + - "@yarnpkg/plugin-pack" + - "@yarnpkg/plugin-patch" + - "@yarnpkg/plugin-pnp" + - "@yarnpkg/plugin-pnpm" + - "@yarnpkg/plugin-stage" + - "@yarnpkg/plugin-typescript" + - "@yarnpkg/plugin-version" + - "@yarnpkg/plugin-workspace-tools" + - "@yarnpkg/builder" + - "@yarnpkg/core" + - "@yarnpkg/doctor" diff --git a/packages/plugin-essentials/sources/commands/set/version.ts b/packages/plugin-essentials/sources/commands/set/version.ts index 311d04c412a0..66be24059c06 100644 --- a/packages/plugin-essentials/sources/commands/set/version.ts +++ b/packages/plugin-essentials/sources/commands/set/version.ts @@ -86,25 +86,33 @@ export default class SetVersionCommand extends BaseCommand { return `file://${process.argv[1]}`; }; - let bundleUrl: string; + let bundleRef: { + version: string | null; + url: string; + }; + + const getRef = (url: string, version: string | null = null) => { + return version ? {version, url: url.replace(/\{\}/g, version)} : {version, url}; + }; + if (this.version === `self`) - bundleUrl = getBundlePath(); + bundleRef = getRef(getBundlePath()); else if (this.version === `latest` || this.version === `berry` || this.version === `stable`) - bundleUrl = `https://repo.yarnpkg.com/${await resolveTag(configuration, `stable`)}/packages/yarnpkg-cli/bin/yarn.js`; + bundleRef = getRef(`https://repo.yarnpkg.com/{}/packages/yarnpkg-cli/bin/yarn.js`, await resolveTag(configuration, `stable`)); else if (this.version === `canary`) - bundleUrl = `https://repo.yarnpkg.com/${await resolveTag(configuration, `canary`)}/packages/yarnpkg-cli/bin/yarn.js`; + bundleRef = getRef(`https://repo.yarnpkg.com/{}/packages/yarnpkg-cli/bin/yarn.js`, await resolveTag(configuration, `canary`)); else if (this.version === `classic`) - bundleUrl = `https://nightly.yarnpkg.com/latest.js`; + bundleRef = getRef(`https://nightly.yarnpkg.com/latest.js`); else if (this.version.match(/^https?:/)) - bundleUrl = this.version; + bundleRef = getRef(this.version); else if (this.version.match(/^\.{0,2}[\\/]/) || npath.isAbsolute(this.version)) - bundleUrl = `file://${npath.resolve(this.version)}`; + bundleRef = getRef(`file://${npath.resolve(this.version)}`); else if (semverUtils.satisfiesWithPrereleases(this.version, `>=2.0.0`)) - bundleUrl = `https://repo.yarnpkg.com/${this.version}/packages/yarnpkg-cli/bin/yarn.js`; + bundleRef = getRef(`https://repo.yarnpkg.com/{}/packages/yarnpkg-cli/bin/yarn.js`, this.version); else if (semverUtils.satisfiesWithPrereleases(this.version, `^0.x || ^1.x`)) - bundleUrl = `https://github.com/yarnpkg/yarn/releases/download/v${this.version}/yarn-${this.version}.js`; + bundleRef = getRef(`https://github.com/yarnpkg/yarn/releases/download/v{}/yarn-{}.js`, this.version); else if (semverUtils.validRange(this.version)) - bundleUrl = `https://repo.yarnpkg.com/${await resolveRange(configuration, this.version)}/packages/yarnpkg-cli/bin/yarn.js`; + bundleRef = getRef(`https://repo.yarnpkg.com/{}/packages/yarnpkg-cli/bin/yarn.js`, await resolveRange(configuration, this.version)); else throw new UsageError(`Invalid version descriptor "${this.version}"`); @@ -113,18 +121,19 @@ export default class SetVersionCommand extends BaseCommand { stdout: this.context.stdout, includeLogs: !this.context.quiet, }, async (report: StreamReport) => { - const filePrefix = `file://`; - - let bundleBuffer: Buffer; - if (bundleUrl.startsWith(filePrefix)) { - report.reportInfo(MessageName.UNNAMED, `Downloading ${formatUtils.pretty(configuration, bundleUrl, FormatType.URL)}`); - bundleBuffer = await xfs.readFilePromise(npath.toPortablePath(bundleUrl.slice(filePrefix.length))); - } else { - report.reportInfo(MessageName.UNNAMED, `Retrieving ${formatUtils.pretty(configuration, bundleUrl, FormatType.PATH)}`); - bundleBuffer = await httpUtils.get(bundleUrl, {configuration}); - } - - await setVersion(configuration, null, bundleBuffer, {report}); + const fetchBuffer = async () => { + const filePrefix = `file://`; + + if (bundleRef.url.startsWith(filePrefix)) { + report.reportInfo(MessageName.UNNAMED, `Retrieving ${formatUtils.pretty(configuration, bundleRef.url, FormatType.PATH)}`); + return await xfs.readFilePromise(npath.toPortablePath(bundleRef.url.slice(filePrefix.length))); + } else { + report.reportInfo(MessageName.UNNAMED, `Downloading ${formatUtils.pretty(configuration, bundleRef.url, FormatType.URL)}`); + return await httpUtils.get(bundleRef.url, {configuration}); + } + }; + + await setVersion(configuration, bundleRef.version, fetchBuffer, {report}); }); return report.exitCode(); @@ -150,24 +159,7 @@ export async function resolveTag(configuration: Configuration, request: `stable` return data.latest[request]; } -export async function setVersion(configuration: Configuration, bundleVersion: string | null, bundleBuffer: Buffer, {report}: {report: Report}) { - if (bundleVersion === null) { - await xfs.mktempPromise(async tmpDir => { - const temporaryPath = ppath.join(tmpDir, `yarn.cjs` as Filename); - await xfs.writeFilePromise(temporaryPath, bundleBuffer); - - const {stdout} = await execUtils.execvp(process.execPath, [npath.fromPortablePath(temporaryPath), `--version`], { - cwd: tmpDir, - env: {...process.env, YARN_IGNORE_PATH: `1`}, - }); - - bundleVersion = stdout.trim(); - if (!semver.valid(bundleVersion)) { - throw new Error(`Invalid semver version. ${formatUtils.pretty(configuration, `yarn --version`, formatUtils.Type.CODE)} returned:\n${bundleVersion}`); - } - }); - } - +export async function setVersion(configuration: Configuration, bundleVersion: string | null, fetchBuffer: () => Promise, {report}: {report: Report}) { const projectCwd = configuration.projectCwd ?? configuration.startingCwd; const releaseFolder = ppath.resolve(projectCwd, `.yarn/releases` as PortablePath); @@ -178,13 +170,35 @@ export async function setVersion(configuration: Configuration, bundleVersion: st const yarnPath = configuration.get(`yarnPath`); const updateConfig = yarnPath === null || yarnPath.startsWith(`${releaseFolder}/`); + const updateFile = yarnPath && yarnPath.startsWith(`${releaseFolder}/`); + + if (updateFile) { + const bundleBuffer = await fetchBuffer(); + + if (bundleVersion === null) { + await xfs.mktempPromise(async tmpDir => { + const temporaryPath = ppath.join(tmpDir, `yarn.cjs` as Filename); + await xfs.writeFilePromise(temporaryPath, bundleBuffer); - report.reportInfo(MessageName.UNNAMED, `Saving the new release in ${formatUtils.pretty(configuration, displayPath, `magenta`)}`); + const {stdout} = await execUtils.execvp(process.execPath, [npath.fromPortablePath(temporaryPath), `--version`], { + cwd: tmpDir, + env: {...process.env, YARN_IGNORE_PATH: `1`}, + }); - await xfs.removePromise(ppath.dirname(absolutePath)); - await xfs.mkdirPromise(ppath.dirname(absolutePath), {recursive: true}); + bundleVersion = stdout.trim(); + if (!semver.valid(bundleVersion)) { + throw new Error(`Invalid semver version. ${formatUtils.pretty(configuration, `yarn --version`, formatUtils.Type.CODE)} returned:\n${bundleVersion}`); + } + }); + } + + report.reportInfo(MessageName.UNNAMED, `Saving the new release in ${formatUtils.pretty(configuration, displayPath, `magenta`)}`); - await xfs.writeFilePromise(absolutePath, bundleBuffer, {mode: 0o755}); + await xfs.removePromise(ppath.dirname(absolutePath)); + await xfs.mkdirPromise(ppath.dirname(absolutePath), {recursive: true}); + + await xfs.writeFilePromise(absolutePath, bundleBuffer, {mode: 0o755}); + } if (updateConfig) { await Configuration.updateConfiguration(projectCwd, { From d3618e40cc3b899b10a5245a328be96510bcde65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Wed, 23 Mar 2022 13:43:04 +0100 Subject: [PATCH 02/12] Fixes tests --- .../sources/commands/set/version.ts | 65 ++++++++++--------- .../sources/commands/set/version/sources.ts | 2 +- .../yarnpkg-core/sources/Configuration.ts | 10 ++- 3 files changed, 44 insertions(+), 33 deletions(-) diff --git a/packages/plugin-essentials/sources/commands/set/version.ts b/packages/plugin-essentials/sources/commands/set/version.ts index 66be24059c06..a3da8adbea48 100644 --- a/packages/plugin-essentials/sources/commands/set/version.ts +++ b/packages/plugin-essentials/sources/commands/set/version.ts @@ -87,26 +87,26 @@ export default class SetVersionCommand extends BaseCommand { }; let bundleRef: { - version: string | null; + version: string; url: string; }; - const getRef = (url: string, version: string | null = null) => { - return version ? {version, url: url.replace(/\{\}/g, version)} : {version, url}; + const getRef = (url: string, version: string) => { + return {version, url: url.replace(/\{\}/g, version)}; }; if (this.version === `self`) - bundleRef = getRef(getBundlePath()); + bundleRef = {url: getBundlePath(), version: YarnVersion ?? `self`}; else if (this.version === `latest` || this.version === `berry` || this.version === `stable`) bundleRef = getRef(`https://repo.yarnpkg.com/{}/packages/yarnpkg-cli/bin/yarn.js`, await resolveTag(configuration, `stable`)); else if (this.version === `canary`) bundleRef = getRef(`https://repo.yarnpkg.com/{}/packages/yarnpkg-cli/bin/yarn.js`, await resolveTag(configuration, `canary`)); else if (this.version === `classic`) - bundleRef = getRef(`https://nightly.yarnpkg.com/latest.js`); + bundleRef = {url: `https://nightly.yarnpkg.com/latest.js`, version: `classic`}; else if (this.version.match(/^https?:/)) - bundleRef = getRef(this.version); + bundleRef = {url: this.version, version: `remote`}; else if (this.version.match(/^\.{0,2}[\\/]/) || npath.isAbsolute(this.version)) - bundleRef = getRef(`file://${npath.resolve(this.version)}`); + bundleRef = {url: `file://${npath.resolve(this.version)}`, version: `file`}; else if (semverUtils.satisfiesWithPrereleases(this.version, `>=2.0.0`)) bundleRef = getRef(`https://repo.yarnpkg.com/{}/packages/yarnpkg-cli/bin/yarn.js`, this.version); else if (semverUtils.satisfiesWithPrereleases(this.version, `^0.x || ^1.x`)) @@ -159,18 +159,17 @@ export async function resolveTag(configuration: Configuration, request: `stable` return data.latest[request]; } -export async function setVersion(configuration: Configuration, bundleVersion: string | null, fetchBuffer: () => Promise, {report}: {report: Report}) { +export async function setVersion(configuration: Configuration, bundleVersion: string, fetchBuffer: () => Promise, {report}: {report: Report}) { const projectCwd = configuration.projectCwd ?? configuration.startingCwd; const releaseFolder = ppath.resolve(projectCwd, `.yarn/releases` as PortablePath); const absolutePath = ppath.resolve(releaseFolder, `yarn-${bundleVersion}.cjs` as Filename); - const displayPath = ppath.relative(configuration.startingCwd, absolutePath); - const projectPath = ppath.relative(projectCwd, absolutePath); + + const isTaggedYarnVersion = miscUtils.isTaggedYarnVersion(bundleVersion); const yarnPath = configuration.get(`yarnPath`); - const updateConfig = yarnPath === null || yarnPath.startsWith(`${releaseFolder}/`); - const updateFile = yarnPath && yarnPath.startsWith(`${releaseFolder}/`); + const updateFile = yarnPath || !isTaggedYarnVersion; if (updateFile) { const bundleBuffer = await fetchBuffer(); @@ -198,30 +197,36 @@ export async function setVersion(configuration: Configuration, bundleVersion: st await xfs.mkdirPromise(ppath.dirname(absolutePath), {recursive: true}); await xfs.writeFilePromise(absolutePath, bundleBuffer, {mode: 0o755}); - } - if (updateConfig) { + if (!yarnPath || ppath.contains(releaseFolder, yarnPath)) { + await Configuration.updateConfiguration(projectCwd, { + yarnPath: ppath.relative(projectCwd, absolutePath), + }); + } + } else { + await xfs.removePromise(ppath.dirname(absolutePath)); + await Configuration.updateConfiguration(projectCwd, { - yarnPath: projectPath, + yarnPath: Configuration.deleteProperty, }); + } - const manifest = (await Manifest.tryFind(projectCwd)) || new Manifest(); + const manifest = (await Manifest.tryFind(projectCwd)) || new Manifest(); - manifest.packageManager = `yarn@${ - bundleVersion && miscUtils.isTaggedYarnVersion(bundleVersion) - ? bundleVersion - // If the version isn't tagged, we use the latest stable version as the wrapper - : await resolveTag(configuration, `stable`) - }`; + manifest.packageManager = `yarn@${ + bundleVersion && isTaggedYarnVersion + ? bundleVersion + // If the version isn't tagged, we use the latest stable version as the wrapper + : await resolveTag(configuration, `stable`) + }`; - const data = {}; - manifest.exportTo(data); + const data = {}; + manifest.exportTo(data); - const path = ppath.join(projectCwd, Manifest.fileName); - const content = `${JSON.stringify(data, null, manifest.indent)}\n`; + const path = ppath.join(projectCwd, Manifest.fileName); + const content = `${JSON.stringify(data, null, manifest.indent)}\n`; - await xfs.changeFilePromise(path, content, { - automaticNewlines: true, - }); - } + await xfs.changeFilePromise(path, content, { + automaticNewlines: true, + }); } diff --git a/packages/plugin-essentials/sources/commands/set/version/sources.ts b/packages/plugin-essentials/sources/commands/set/version/sources.ts index 5f6de1459e2b..39c205689374 100644 --- a/packages/plugin-essentials/sources/commands/set/version/sources.ts +++ b/packages/plugin-essentials/sources/commands/set/version/sources.ts @@ -107,7 +107,7 @@ export default class SetVersionSourcesCommand extends BaseCommand { const bundlePath = ppath.resolve(target, `packages/yarnpkg-cli/bundles/yarn.js` as PortablePath); const bundleBuffer = await xfs.readFilePromise(bundlePath); - await setVersion(configuration, `sources`, bundleBuffer, { + await setVersion(configuration, `sources`, async () => bundleBuffer, { report, }); diff --git a/packages/yarnpkg-core/sources/Configuration.ts b/packages/yarnpkg-core/sources/Configuration.ts index ab93467428b6..ff8591e0e95c 100644 --- a/packages/yarnpkg-core/sources/Configuration.ts +++ b/packages/yarnpkg-core/sources/Configuration.ts @@ -543,7 +543,7 @@ export type MapConfigurationValue = miscUtils.ToMapValue; export interface ConfigurationValueMap { lastUpdateCheck: string | null; - yarnPath: PortablePath; + yarnPath: PortablePath | null; ignorePath: boolean; ignoreCwd: boolean; @@ -897,6 +897,8 @@ export type FindProjectOptions = { }; export class Configuration { + public static deleteProperty = Symbol(); + public static telemetry: TelemetryManager | null = null; public startingCwd: PortablePath; @@ -1265,7 +1267,11 @@ export class Configuration { if (currentValue === nextValue) continue; - replacement[key] = nextValue; + if (nextValue === Configuration.deleteProperty) + delete replacement[key]; + else + replacement[key] = nextValue; + patched = true; } From 2a0a0c4b3b6f5a4f9027fd94a333494c137ea854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Thu, 24 Mar 2022 13:37:22 +0100 Subject: [PATCH 03/12] Implements --yarn-path --- .../plugin-essentials/sources/commands/set/version.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/plugin-essentials/sources/commands/set/version.ts b/packages/plugin-essentials/sources/commands/set/version.ts index a3da8adbea48..33a73abb33a4 100644 --- a/packages/plugin-essentials/sources/commands/set/version.ts +++ b/packages/plugin-essentials/sources/commands/set/version.ts @@ -68,6 +68,10 @@ export default class SetVersionCommand extends BaseCommand { ]], }); + useYarnPath = Option.Boolean(`--yarn-path`, false, { + description: `Set the yarnPath setting even if the version can be accessed by Corepack`, + }); + onlyIfNeeded = Option.Boolean(`--only-if-needed`, false, { description: `Only lock the Yarn version if it isn't already locked`, }); @@ -133,7 +137,7 @@ export default class SetVersionCommand extends BaseCommand { } }; - await setVersion(configuration, bundleRef.version, fetchBuffer, {report}); + await setVersion(configuration, bundleRef.version, fetchBuffer, {report, useYarnPath: this.useYarnPath}); }); return report.exitCode(); @@ -159,7 +163,7 @@ export async function resolveTag(configuration: Configuration, request: `stable` return data.latest[request]; } -export async function setVersion(configuration: Configuration, bundleVersion: string, fetchBuffer: () => Promise, {report}: {report: Report}) { +export async function setVersion(configuration: Configuration, bundleVersion: string, fetchBuffer: () => Promise, {report, useYarnPath}: {report: Report, useYarnPath?: boolean}) { const projectCwd = configuration.projectCwd ?? configuration.startingCwd; const releaseFolder = ppath.resolve(projectCwd, `.yarn/releases` as PortablePath); @@ -169,7 +173,7 @@ export async function setVersion(configuration: Configuration, bundleVersion: st const isTaggedYarnVersion = miscUtils.isTaggedYarnVersion(bundleVersion); const yarnPath = configuration.get(`yarnPath`); - const updateFile = yarnPath || !isTaggedYarnVersion; + const updateFile = yarnPath || !isTaggedYarnVersion || useYarnPath; if (updateFile) { const bundleBuffer = await fetchBuffer(); From 5ca1203a6cc35aac46bf58a911352210a6445e93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Thu, 24 Mar 2022 13:40:30 +0100 Subject: [PATCH 04/12] Updates documentation --- packages/plugin-essentials/sources/commands/set/version.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/plugin-essentials/sources/commands/set/version.ts b/packages/plugin-essentials/sources/commands/set/version.ts index 33a73abb33a4..eea6c9501c63 100644 --- a/packages/plugin-essentials/sources/commands/set/version.ts +++ b/packages/plugin-essentials/sources/commands/set/version.ts @@ -19,7 +19,9 @@ export default class SetVersionCommand extends BaseCommand { static usage: Usage = Command.Usage({ description: `lock the Yarn version used by the project`, details: ` - This command will download a specific release of Yarn directly from the Yarn GitHub repository, will store it inside your project, and will change the \`yarnPath\` settings from your project \`.yarnrc.yml\` file to point to the new file. + This command will set a specific release of Yarn to be used by Corepack: https://nodejs.org/api/corepack.html. + + By default it only will set the \`packageManager\` field at the root of your project, but if the referenced release cannot be represented this way, if you already have \`yarnPath\` configured, or if you set the \`--yarn-path\` command line flag, then the release will also be downloaded from the Yarn GitHub repository, stored inside your project, and referenced via the \`yarnPath\` settings from your project \`.yarnrc.yml\` file. A very good use case for this command is to enforce the version of Yarn used by the any single member of your team inside a same project - by doing this you ensure that you have control on Yarn upgrades and downgrades (including on your deployment servers), and get rid of most of the headaches related to someone using a slightly different version and getting a different behavior than you. From 1567ad50d90fa5ddff29ddba25ec9c1e88652e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Thu, 24 Mar 2022 13:41:27 +0100 Subject: [PATCH 05/12] Fixes types --- packages/yarnpkg-cli/sources/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/yarnpkg-cli/sources/main.ts b/packages/yarnpkg-cli/sources/main.ts index 7cf3a724ad11..4c3d3df58b75 100644 --- a/packages/yarnpkg-cli/sources/main.ts +++ b/packages/yarnpkg-cli/sources/main.ts @@ -73,7 +73,7 @@ export async function main({binaryVersion, pluginConfiguration}: {binaryVersion: strict: false, }); - const yarnPath: PortablePath = configuration.get(`yarnPath`); + const yarnPath = configuration.get(`yarnPath`); const ignorePath = configuration.get(`ignorePath`); const ignoreCwd = configuration.get(`ignoreCwd`); From f69657bd9bf9b13c9ae812b6619dd4876308a4e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Thu, 24 Mar 2022 16:40:58 +0100 Subject: [PATCH 06/12] Fixes types --- packages/yarnpkg-cli/sources/main.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/yarnpkg-cli/sources/main.ts b/packages/yarnpkg-cli/sources/main.ts index 4c3d3df58b75..5c21d0100bb7 100644 --- a/packages/yarnpkg-cli/sources/main.ts +++ b/packages/yarnpkg-cli/sources/main.ts @@ -83,15 +83,16 @@ export async function main({binaryVersion, pluginConfiguration}: {binaryVersion: return Buffer.of(); }); - const isSameBinary = async () => - yarnPath === selfPath || + const isDifferentBinary = async () => + yarnPath && + yarnPath !== selfPath && Buffer.compare(...await Promise.all([ tryRead(yarnPath), tryRead(selfPath), - ])) === 0; + ])) !== 0; // Avoid unnecessary spawn when run directly - if (!ignorePath && !ignoreCwd && await isSameBinary()) { + if (!ignorePath && !ignoreCwd && !await isDifferentBinary()) { process.env.YARN_IGNORE_PATH = `1`; process.env.YARN_IGNORE_CWD = `1`; From 7c61c58b91b98e5c97766677a47243fb002afd1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Fri, 25 Mar 2022 16:20:15 +0100 Subject: [PATCH 07/12] Uses yarnPath by default if Corepack isnt enabled --- .../plugin-essentials/sources/commands/set/version.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/plugin-essentials/sources/commands/set/version.ts b/packages/plugin-essentials/sources/commands/set/version.ts index eea6c9501c63..df9dadd2a17c 100644 --- a/packages/plugin-essentials/sources/commands/set/version.ts +++ b/packages/plugin-essentials/sources/commands/set/version.ts @@ -70,7 +70,7 @@ export default class SetVersionCommand extends BaseCommand { ]], }); - useYarnPath = Option.Boolean(`--yarn-path`, false, { + useYarnPath = Option.Boolean(`--yarn-path`, { description: `Set the yarnPath setting even if the version can be accessed by Corepack`, }); @@ -175,7 +175,13 @@ export async function setVersion(configuration: Configuration, bundleVersion: st const isTaggedYarnVersion = miscUtils.isTaggedYarnVersion(bundleVersion); const yarnPath = configuration.get(`yarnPath`); - const updateFile = yarnPath || !isTaggedYarnVersion || useYarnPath; + let updateFile = yarnPath || !isTaggedYarnVersion || yarnPath; + + if (typeof yarnPath === `undefined`) { + report.reportWarning(MessageName.UNNAMED, `You don't seem to have ${formatUtils.applyHyperlink(configuration, `Corepack`, `https://nodejs.org/api/corepack.html`)} enabled; we'll have to rely on ${formatUtils.applyHyperlink(configuration, `yarnPath`, `https://yarnpkg.com/configuration/yarnrc#yarnPath`)} instead`); + updateFile = true; + } + if (updateFile) { const bundleBuffer = await fetchBuffer(); From a933e576d7bba3aae686b0fdd0acbeeb6fc3ecb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Tue, 29 Mar 2022 11:11:18 +0200 Subject: [PATCH 08/12] Adds tests --- .../pkg-tests-core/sources/utils/tests.ts | 2 +- .../sources/commands/set/version.test.ts | 130 ++++++++++++++++++ .../sources/commands/set/version.ts | 29 ++-- 3 files changed, 148 insertions(+), 13 deletions(-) create mode 100644 packages/acceptance-tests/pkg-tests-specs/sources/commands/set/version.test.ts diff --git a/packages/acceptance-tests/pkg-tests-core/sources/utils/tests.ts b/packages/acceptance-tests/pkg-tests-core/sources/utils/tests.ts index edbd62cc53c9..820fd31672ad 100644 --- a/packages/acceptance-tests/pkg-tests-core/sources/utils/tests.ts +++ b/packages/acceptance-tests/pkg-tests-core/sources/utils/tests.ts @@ -27,7 +27,7 @@ interface RunDriverOptions extends Record { cwd?: PortablePath; projectFolder?: PortablePath; registryUrl: string; - env?: Record; + env?: Record; } export type PackageRunDriver = ( diff --git a/packages/acceptance-tests/pkg-tests-specs/sources/commands/set/version.test.ts b/packages/acceptance-tests/pkg-tests-specs/sources/commands/set/version.test.ts new file mode 100644 index 000000000000..133b509d98c0 --- /dev/null +++ b/packages/acceptance-tests/pkg-tests-specs/sources/commands/set/version.test.ts @@ -0,0 +1,130 @@ +import {xfs, ppath, PortablePath, Filename} from '@yarnpkg/fslib'; + +const yarnrcRegexp = /^yarnPath:/; + +describe(`Commands`, () => { + describe(`set version`, () => { + test( + `it shouldn't set yarnPath if corepack is enabled and the version is semver`, + makeTemporaryEnv({}, { + env: {COREPACK_ROOT: `/path/to/corepack`}, + }, async ({path, run, source}) => { + await run(`set`, `version`, `3.0.0`); + await check(path, {corepackVersion: `3.0.0`, usePath: false}); + }), + ); + + test( + `it should set yarnPath if corepack is disabled, even when the version is semver`, + makeTemporaryEnv({}, { + env: {COREPACK_ROOT: undefined}, + }, async ({path, run, source}) => { + await run(`set`, `version`, `3.0.0`); + await check(path, {corepackVersion: `3.0.0`, usePath: true}); + }), + ); + + test( + `it should always set yarnPath if one already exists`, + makeTemporaryEnv({}, { + env: {COREPACK_ROOT: `/path/to/corepack`}, + }, async ({path, run, source}) => { + // To force yarnPath to be set; followed by a sanity check + await run(`set`, `version`, `3.0.0`, {env: {COREPACK_ROOT: undefined}}); + await check(path, {corepackVersion: `3.0.0`, usePath: true}); + + await run(`set`, `version`, `3.0.0`); + await check(path, {corepackVersion: `3.0.0`, usePath: true}); + }), + ); + + test( + `it should always set yarnPath if --yarn-path is set`, + makeTemporaryEnv({}, { + env: {COREPACK_ROOT: `/path/to/corepack`}, + }, async ({path, run, source}) => { + await run(`set`, `version`, `3.0.0`, `--yarn-path`); + await check(path, {corepackVersion: `3.0.0`, usePath: true}); + }), + ); + + test( + `it should never set yarnPath if --no-yarn-path is set`, + makeTemporaryEnv({}, { + env: {COREPACK_ROOT: undefined}, + }, async ({path, run, source}) => { + await run(`set`, `version`, `3.0.0`, `--no-yarn-path`); + await check(path, {corepackVersion: `3.0.0`, usePath: false}); + }), + ); + + test( + `it should prevent using --no-yarn-path with arbitrary files`, + makeTemporaryEnv({}, { + env: {COREPACK_ROOT: undefined}, + }, async ({path, run, source}) => { + const yarnIndirection = ppath.join(path, `custom-yarn.cjs` as Filename); + await xfs.writeFilePromise(yarnIndirection, ``); + + await expect(run(`set`, `version`, yarnIndirection, `--no-yarn-path`)).rejects.toThrow(); + }), + ); + + test( + `it should set yarnPath if the version is an arbitrary file`, + makeTemporaryEnv({}, { + env: {COREPACK_ROOT: undefined}, + }, async ({path, run, source}) => { + const yarnIndirection = ppath.join(path, `custom-yarn.cjs` as Filename); + await xfs.writeFilePromise(yarnIndirection, ``); + + await run(`set`, `version`, yarnIndirection); + await check(path, {corepackVersion: /[0-9]+\./, usePath: true}); + }), + ); + }); +}); + +async function check(path: PortablePath, checks: {corepackVersion: string | RegExp, usePath: boolean}) { + const releasesPath = ppath.join(path, `.yarn/releases` as PortablePath); + const yarnrcPath = ppath.join(path, Filename.rc); + const manifestPath = ppath.join(path, Filename.manifest); + + let releases: Array | null; + try { + releases = await xfs.readdirPromise(releasesPath); + } catch (err) { + if (err.code === `ENOENT`) { + releases = null; + } else { + throw err; + } + } + + let yarnrcFile; + try { + yarnrcFile = await xfs.readFilePromise(yarnrcPath, `utf8`); + } catch (err) { + if (err.code === `ENOENT`) { + yarnrcFile = ``; + } else { + throw err; + } + } + + if (checks.usePath) + expect(releases).toHaveLength(1); + else + expect(releases).toEqual(null); + + if (checks.usePath) + expect(yarnrcFile).toMatch(yarnrcRegexp); + else + expect(yarnrcFile).not.toMatch(yarnrcRegexp); + + await expect(xfs.readJsonPromise(manifestPath)).resolves.toMatchObject({ + packageManager: checks.corepackVersion instanceof RegExp + ? expect.stringMatching(`yarn@${checks.corepackVersion.source}`) + : `yarn@${checks.corepackVersion}`, + }); +} diff --git a/packages/plugin-essentials/sources/commands/set/version.ts b/packages/plugin-essentials/sources/commands/set/version.ts index df9dadd2a17c..e40a4e3c894f 100644 --- a/packages/plugin-essentials/sources/commands/set/version.ts +++ b/packages/plugin-essentials/sources/commands/set/version.ts @@ -1,9 +1,9 @@ -import {BaseCommand} from '@yarnpkg/cli'; -import {Configuration, StreamReport, MessageName, Report, Manifest, FormatType, YarnVersion} from '@yarnpkg/core'; -import {execUtils, formatUtils, httpUtils, miscUtils, semverUtils} from '@yarnpkg/core'; -import {Filename, PortablePath, ppath, xfs, npath} from '@yarnpkg/fslib'; -import {Command, Option, Usage, UsageError} from 'clipanion'; -import semver from 'semver'; +import {BaseCommand} from '@yarnpkg/cli'; +import {Configuration, StreamReport, MessageName, Report, Manifest, FormatType, YarnVersion, ReportError} from '@yarnpkg/core'; +import {execUtils, formatUtils, httpUtils, miscUtils, semverUtils} from '@yarnpkg/core'; +import {Filename, PortablePath, ppath, xfs, npath} from '@yarnpkg/fslib'; +import {Command, Option, Usage, UsageError} from 'clipanion'; +import semver from 'semver'; export type Tags = { latest: Record; @@ -173,17 +173,22 @@ export async function setVersion(configuration: Configuration, bundleVersion: st const displayPath = ppath.relative(configuration.startingCwd, absolutePath); const isTaggedYarnVersion = miscUtils.isTaggedYarnVersion(bundleVersion); - const yarnPath = configuration.get(`yarnPath`); - let updateFile = yarnPath || !isTaggedYarnVersion || yarnPath; - if (typeof yarnPath === `undefined`) { + const absolutelyMustUseYarnPath = !isTaggedYarnVersion; + let probablyShouldUseYarnPath = absolutelyMustUseYarnPath || !!yarnPath || !!useYarnPath; + + if (useYarnPath === false) { + if (absolutelyMustUseYarnPath) + throw new ReportError(MessageName.UNNAMED, `You explicitly opted out of yarnPath usage in your command line, but the version you specified cannot be represented by Corepack`); + + probablyShouldUseYarnPath = false; + } else if (!probablyShouldUseYarnPath && !process.env.COREPACK_ROOT) { report.reportWarning(MessageName.UNNAMED, `You don't seem to have ${formatUtils.applyHyperlink(configuration, `Corepack`, `https://nodejs.org/api/corepack.html`)} enabled; we'll have to rely on ${formatUtils.applyHyperlink(configuration, `yarnPath`, `https://yarnpkg.com/configuration/yarnrc#yarnPath`)} instead`); - updateFile = true; + probablyShouldUseYarnPath = true; } - - if (updateFile) { + if (probablyShouldUseYarnPath) { const bundleBuffer = await fetchBuffer(); if (bundleVersion === null) { From f7d9aea701e63a33f06dfbdb5e44f9c6011a9f34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Tue, 29 Mar 2022 11:20:29 +0200 Subject: [PATCH 09/12] Reverts parts of the changes --- packages/yarnpkg-cli/sources/main.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/yarnpkg-cli/sources/main.ts b/packages/yarnpkg-cli/sources/main.ts index 5c21d0100bb7..4c3d3df58b75 100644 --- a/packages/yarnpkg-cli/sources/main.ts +++ b/packages/yarnpkg-cli/sources/main.ts @@ -83,16 +83,15 @@ export async function main({binaryVersion, pluginConfiguration}: {binaryVersion: return Buffer.of(); }); - const isDifferentBinary = async () => - yarnPath && - yarnPath !== selfPath && + const isSameBinary = async () => + yarnPath === selfPath || Buffer.compare(...await Promise.all([ tryRead(yarnPath), tryRead(selfPath), - ])) !== 0; + ])) === 0; // Avoid unnecessary spawn when run directly - if (!ignorePath && !ignoreCwd && !await isDifferentBinary()) { + if (!ignorePath && !ignoreCwd && await isSameBinary()) { process.env.YARN_IGNORE_PATH = `1`; process.env.YARN_IGNORE_CWD = `1`; From 6f1b3d3e67433daddaf1d237a7edd6bcb855da5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Tue, 29 Mar 2022 11:22:25 +0200 Subject: [PATCH 10/12] Fixes broken plugin tests --- packages/yarnpkg-cli/sources/main.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/yarnpkg-cli/sources/main.ts b/packages/yarnpkg-cli/sources/main.ts index 4c3d3df58b75..613d70b0e6b0 100644 --- a/packages/yarnpkg-cli/sources/main.ts +++ b/packages/yarnpkg-cli/sources/main.ts @@ -84,11 +84,13 @@ export async function main({binaryVersion, pluginConfiguration}: {binaryVersion: }); const isSameBinary = async () => - yarnPath === selfPath || - Buffer.compare(...await Promise.all([ - tryRead(yarnPath), - tryRead(selfPath), - ])) === 0; + yarnPath && ( + yarnPath === selfPath || + Buffer.compare(...await Promise.all([ + tryRead(yarnPath), + tryRead(selfPath), + ])) === 0 + ); // Avoid unnecessary spawn when run directly if (!ignorePath && !ignoreCwd && await isSameBinary()) { From 0aba7ff482539196cbd41ff8dd7e4e0ff624c165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Tue, 29 Mar 2022 12:21:16 +0200 Subject: [PATCH 11/12] Fixes windows set version from file --- .../sources/commands/set/version.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/plugin-essentials/sources/commands/set/version.ts b/packages/plugin-essentials/sources/commands/set/version.ts index e40a4e3c894f..d0e0e5f5d463 100644 --- a/packages/plugin-essentials/sources/commands/set/version.ts +++ b/packages/plugin-essentials/sources/commands/set/version.ts @@ -112,7 +112,7 @@ export default class SetVersionCommand extends BaseCommand { else if (this.version.match(/^https?:/)) bundleRef = {url: this.version, version: `remote`}; else if (this.version.match(/^\.{0,2}[\\/]/) || npath.isAbsolute(this.version)) - bundleRef = {url: `file://${npath.resolve(this.version)}`, version: `file`}; + bundleRef = {url: `file://${ppath.resolve(npath.toPortablePath(this.version))}`, version: `file`}; else if (semverUtils.satisfiesWithPrereleases(this.version, `>=2.0.0`)) bundleRef = getRef(`https://repo.yarnpkg.com/{}/packages/yarnpkg-cli/bin/yarn.js`, this.version); else if (semverUtils.satisfiesWithPrereleases(this.version, `^0.x || ^1.x`)) @@ -127,6 +127,7 @@ export default class SetVersionCommand extends BaseCommand { stdout: this.context.stdout, includeLogs: !this.context.quiet, }, async (report: StreamReport) => { +<<<<<<< Updated upstream const fetchBuffer = async () => { const filePrefix = `file://`; @@ -140,6 +141,20 @@ export default class SetVersionCommand extends BaseCommand { }; await setVersion(configuration, bundleRef.version, fetchBuffer, {report, useYarnPath: this.useYarnPath}); +======= + const filePrefix = `file://`; + + let bundleBuffer: Buffer; + if (bundleUrl.startsWith(filePrefix)) { + report.reportInfo(MessageName.UNNAMED, `Downloading ${formatUtils.pretty(configuration, bundleUrl, FormatType.URL)}`); + bundleBuffer = await xfs.readFilePromise(bundleUrl.slice(filePrefix.length) as PortablePath); + } else { + report.reportInfo(MessageName.UNNAMED, `Retrieving ${formatUtils.pretty(configuration, bundleUrl, FormatType.PATH)}`); + bundleBuffer = await httpUtils.get(bundleUrl, {configuration}); + } + + await setVersion(configuration, null, bundleBuffer, {report}); +>>>>>>> Stashed changes }); return report.exitCode(); From 669d3c95dc6322d159e77de9fc9b8b8f295611a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Tue, 29 Mar 2022 12:25:44 +0200 Subject: [PATCH 12/12] Fixes merge conflicts --- .../sources/commands/set/version.ts | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/packages/plugin-essentials/sources/commands/set/version.ts b/packages/plugin-essentials/sources/commands/set/version.ts index d0e0e5f5d463..e7488e95c8a3 100644 --- a/packages/plugin-essentials/sources/commands/set/version.ts +++ b/packages/plugin-essentials/sources/commands/set/version.ts @@ -127,13 +127,12 @@ export default class SetVersionCommand extends BaseCommand { stdout: this.context.stdout, includeLogs: !this.context.quiet, }, async (report: StreamReport) => { -<<<<<<< Updated upstream const fetchBuffer = async () => { const filePrefix = `file://`; if (bundleRef.url.startsWith(filePrefix)) { report.reportInfo(MessageName.UNNAMED, `Retrieving ${formatUtils.pretty(configuration, bundleRef.url, FormatType.PATH)}`); - return await xfs.readFilePromise(npath.toPortablePath(bundleRef.url.slice(filePrefix.length))); + return await xfs.readFilePromise(bundleRef.url.slice(filePrefix.length) as PortablePath); } else { report.reportInfo(MessageName.UNNAMED, `Downloading ${formatUtils.pretty(configuration, bundleRef.url, FormatType.URL)}`); return await httpUtils.get(bundleRef.url, {configuration}); @@ -141,20 +140,6 @@ export default class SetVersionCommand extends BaseCommand { }; await setVersion(configuration, bundleRef.version, fetchBuffer, {report, useYarnPath: this.useYarnPath}); -======= - const filePrefix = `file://`; - - let bundleBuffer: Buffer; - if (bundleUrl.startsWith(filePrefix)) { - report.reportInfo(MessageName.UNNAMED, `Downloading ${formatUtils.pretty(configuration, bundleUrl, FormatType.URL)}`); - bundleBuffer = await xfs.readFilePromise(bundleUrl.slice(filePrefix.length) as PortablePath); - } else { - report.reportInfo(MessageName.UNNAMED, `Retrieving ${formatUtils.pretty(configuration, bundleUrl, FormatType.PATH)}`); - bundleBuffer = await httpUtils.get(bundleUrl, {configuration}); - } - - await setVersion(configuration, null, bundleBuffer, {report}); ->>>>>>> Stashed changes }); return report.exitCode();