Skip to content

Commit

Permalink
fix(essentials): set yarnPath in project .yarnrc.yml regardless i…
Browse files Browse the repository at this point in the history
…f it's set in a parent directory (#4928)

* fix(essentials): set yarnPath when one is set outside of the project

* fix(essentials): set yarnPath when using `--only-if-needed` and one is set outside of the project

* chore: versions
  • Loading branch information
merceyz committed Oct 5, 2022
1 parent b2846fa commit 64c0705
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 24 deletions.
23 changes: 23 additions & 0 deletions .yarn/versions/187a6871.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
releases:
"@yarnpkg/cli": patch
"@yarnpkg/plugin-essentials": patch

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"
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ interface RunDriverOptions extends Record<string, any> {
cwd?: PortablePath;
projectFolder?: PortablePath;
registryUrl: string;
env?: Record<string, string>;
env?: Record<string, string | undefined>;
}

export type PackageRunDriver = (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import {xfs, ppath, PortablePath, Filename} from '@yarnpkg/fslib';

const yarnrcRegexp = /^yarnPath:/;

describe(`Commands`, () => {
describe(`set version`, () => {
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 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 even if yarnPath is set outside of the project`,
makeTemporaryEnv({}, {
env: {COREPACK_ROOT: undefined},
}, async ({path, run, source}) => {
await run(`set`, `version`, `self`);
await check(path, {corepackVersion: /[0-9]+\./, usePath: true});

const projectDir = ppath.join(path, `project` as Filename);
await xfs.mkdirPromise(projectDir);
await xfs.writeJsonPromise(ppath.join(projectDir, Filename.manifest), {});
await xfs.writeFilePromise(ppath.join(projectDir, Filename.lockfile), ``);

await run(`set`, `version`, `self`, {cwd: projectDir});
await check(projectDir, {corepackVersion: /[0-9]+\./, usePath: true});
}),
);

test(
`it shouldn't set the version when using '--only-if-needed' and a yarnPath is already set`,
makeTemporaryEnv({}, {
env: {COREPACK_ROOT: undefined},
}, async ({path, run, source}) => {
await run(`set`, `version`, `self`);

const before = await xfs.readFilePromise(ppath.join(path, Filename.rc), `utf8`);
await run(`set`, `version`, `3.0.0`, `--only-if-needed`);
const after = await xfs.readFilePromise(ppath.join(path, Filename.rc), `utf8`);

expect(before).toEqual(after);
}),
);

test(
`it should set yarnPath when using '--only-if-needed' even if yarnPath is set outside of the project`,
makeTemporaryEnv({}, {
env: {COREPACK_ROOT: undefined},
}, async ({path, run, source}) => {
await run(`set`, `version`, `self`);
await check(path, {corepackVersion: /[0-9]+\./, usePath: true});

const projectDir = ppath.join(path, `project` as Filename);
await xfs.mkdirPromise(projectDir);
await xfs.writeJsonPromise(ppath.join(projectDir, Filename.manifest), {});
await xfs.writeFilePromise(ppath.join(projectDir, Filename.lockfile), ``);

await run(`set`, `version`, `self`, `--only-if-needed`, {cwd: projectDir});
await check(projectDir, {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<string> | 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}`,
});
}
50 changes: 27 additions & 23 deletions packages/plugin-essentials/sources/commands/set/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,16 @@ export default class SetVersionCommand extends BaseCommand {

async execute() {
const configuration = await Configuration.find(this.context.cwd, this.context.plugins);
if (configuration.get(`yarnPath`) && this.onlyIfNeeded)
return 0;
if (this.onlyIfNeeded && configuration.get(`yarnPath`)) {
const yarnPathSource = configuration.sources.get(`yarnPath`) as PortablePath | undefined;
if (!yarnPathSource)
throw new Error(`Assertion failed: Expected 'yarnPath' to have a source`);

const projectCwd = configuration.projectCwd ?? configuration.startingCwd;
if (ppath.contains(projectCwd, yarnPathSource)) {
return 0;
}
}

const getBundlePath = () => {
if (typeof YarnVersion === `undefined`)
Expand Down Expand Up @@ -176,8 +184,6 @@ export async function setVersion(configuration: Configuration, bundleVersion: st
const displayPath = ppath.relative(configuration.startingCwd, absolutePath);
const projectPath = ppath.relative(projectCwd, absolutePath);

const yarnPath = configuration.get(`yarnPath`);
const updateConfig = yarnPath === null || yarnPath.startsWith(`${releaseFolder}/`);

report.reportInfo(MessageName.UNNAMED, `Saving the new release in ${formatUtils.pretty(configuration, displayPath, `magenta`)}`);

Expand All @@ -186,28 +192,26 @@ export async function setVersion(configuration: Configuration, bundleVersion: st

await xfs.writeFilePromise(absolutePath, bundleBuffer, {mode: 0o755});

if (updateConfig) {
await Configuration.updateConfiguration(projectCwd, {
yarnPath: projectPath,
});
await Configuration.updateConfiguration(projectCwd, {
yarnPath: projectPath,
});

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 && miscUtils.isTaggedYarnVersion(bundleVersion)
? 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,
});
}

0 comments on commit 64c0705

Please sign in to comment.