Skip to content

Commit 64c0705

Browse files
committed
fix(essentials): set yarnPath in project .yarnrc.yml regardless if 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
1 parent b2846fa commit 64c0705

File tree

4 files changed

+188
-24
lines changed

4 files changed

+188
-24
lines changed

.yarn/versions/187a6871.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
releases:
2+
"@yarnpkg/cli": patch
3+
"@yarnpkg/plugin-essentials": patch
4+
5+
declined:
6+
- "@yarnpkg/plugin-compat"
7+
- "@yarnpkg/plugin-constraints"
8+
- "@yarnpkg/plugin-dlx"
9+
- "@yarnpkg/plugin-init"
10+
- "@yarnpkg/plugin-interactive-tools"
11+
- "@yarnpkg/plugin-nm"
12+
- "@yarnpkg/plugin-npm-cli"
13+
- "@yarnpkg/plugin-pack"
14+
- "@yarnpkg/plugin-patch"
15+
- "@yarnpkg/plugin-pnp"
16+
- "@yarnpkg/plugin-pnpm"
17+
- "@yarnpkg/plugin-stage"
18+
- "@yarnpkg/plugin-typescript"
19+
- "@yarnpkg/plugin-version"
20+
- "@yarnpkg/plugin-workspace-tools"
21+
- "@yarnpkg/builder"
22+
- "@yarnpkg/core"
23+
- "@yarnpkg/doctor"

packages/acceptance-tests/pkg-tests-core/sources/utils/tests.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ interface RunDriverOptions extends Record<string, any> {
2727
cwd?: PortablePath;
2828
projectFolder?: PortablePath;
2929
registryUrl: string;
30-
env?: Record<string, string>;
30+
env?: Record<string, string | undefined>;
3131
}
3232

3333
export type PackageRunDriver = (
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import {xfs, ppath, PortablePath, Filename} from '@yarnpkg/fslib';
2+
3+
const yarnrcRegexp = /^yarnPath:/;
4+
5+
describe(`Commands`, () => {
6+
describe(`set version`, () => {
7+
test(
8+
`it should set yarnPath if corepack is disabled, even when the version is semver`,
9+
makeTemporaryEnv({}, {
10+
env: {COREPACK_ROOT: undefined},
11+
}, async ({path, run, source}) => {
12+
await run(`set`, `version`, `3.0.0`);
13+
await check(path, {corepackVersion: `3.0.0`, usePath: true});
14+
}),
15+
);
16+
17+
test(
18+
`it should always set yarnPath if one already exists`,
19+
makeTemporaryEnv({}, {
20+
env: {COREPACK_ROOT: `/path/to/corepack`},
21+
}, async ({path, run, source}) => {
22+
// To force yarnPath to be set; followed by a sanity check
23+
await run(`set`, `version`, `3.0.0`, {env: {COREPACK_ROOT: undefined}});
24+
await check(path, {corepackVersion: `3.0.0`, usePath: true});
25+
26+
await run(`set`, `version`, `3.0.0`);
27+
await check(path, {corepackVersion: `3.0.0`, usePath: true});
28+
}),
29+
);
30+
test(
31+
`it should prevent using --no-yarn-path with arbitrary files`,
32+
makeTemporaryEnv({}, {
33+
env: {COREPACK_ROOT: undefined},
34+
}, async ({path, run, source}) => {
35+
const yarnIndirection = ppath.join(path, `custom-yarn.cjs` as Filename);
36+
await xfs.writeFilePromise(yarnIndirection, ``);
37+
38+
await expect(run(`set`, `version`, yarnIndirection, `--no-yarn-path`)).rejects.toThrow();
39+
}),
40+
);
41+
42+
test(
43+
`it should set yarnPath even if yarnPath is set outside of the project`,
44+
makeTemporaryEnv({}, {
45+
env: {COREPACK_ROOT: undefined},
46+
}, async ({path, run, source}) => {
47+
await run(`set`, `version`, `self`);
48+
await check(path, {corepackVersion: /[0-9]+\./, usePath: true});
49+
50+
const projectDir = ppath.join(path, `project` as Filename);
51+
await xfs.mkdirPromise(projectDir);
52+
await xfs.writeJsonPromise(ppath.join(projectDir, Filename.manifest), {});
53+
await xfs.writeFilePromise(ppath.join(projectDir, Filename.lockfile), ``);
54+
55+
await run(`set`, `version`, `self`, {cwd: projectDir});
56+
await check(projectDir, {corepackVersion: /[0-9]+\./, usePath: true});
57+
}),
58+
);
59+
60+
test(
61+
`it shouldn't set the version when using '--only-if-needed' and a yarnPath is already set`,
62+
makeTemporaryEnv({}, {
63+
env: {COREPACK_ROOT: undefined},
64+
}, async ({path, run, source}) => {
65+
await run(`set`, `version`, `self`);
66+
67+
const before = await xfs.readFilePromise(ppath.join(path, Filename.rc), `utf8`);
68+
await run(`set`, `version`, `3.0.0`, `--only-if-needed`);
69+
const after = await xfs.readFilePromise(ppath.join(path, Filename.rc), `utf8`);
70+
71+
expect(before).toEqual(after);
72+
}),
73+
);
74+
75+
test(
76+
`it should set yarnPath when using '--only-if-needed' even if yarnPath is set outside of the project`,
77+
makeTemporaryEnv({}, {
78+
env: {COREPACK_ROOT: undefined},
79+
}, async ({path, run, source}) => {
80+
await run(`set`, `version`, `self`);
81+
await check(path, {corepackVersion: /[0-9]+\./, usePath: true});
82+
83+
const projectDir = ppath.join(path, `project` as Filename);
84+
await xfs.mkdirPromise(projectDir);
85+
await xfs.writeJsonPromise(ppath.join(projectDir, Filename.manifest), {});
86+
await xfs.writeFilePromise(ppath.join(projectDir, Filename.lockfile), ``);
87+
88+
await run(`set`, `version`, `self`, `--only-if-needed`, {cwd: projectDir});
89+
await check(projectDir, {corepackVersion: /[0-9]+\./, usePath: true});
90+
}),
91+
);
92+
});
93+
});
94+
95+
async function check(path: PortablePath, checks: {corepackVersion: string | RegExp, usePath: boolean}) {
96+
const releasesPath = ppath.join(path, `.yarn/releases` as PortablePath);
97+
const yarnrcPath = ppath.join(path, Filename.rc);
98+
const manifestPath = ppath.join(path, Filename.manifest);
99+
100+
let releases: Array<string> | null;
101+
try {
102+
releases = await xfs.readdirPromise(releasesPath);
103+
} catch (err) {
104+
if (err.code === `ENOENT`) {
105+
releases = null;
106+
} else {
107+
throw err;
108+
}
109+
}
110+
111+
let yarnrcFile;
112+
try {
113+
yarnrcFile = await xfs.readFilePromise(yarnrcPath, `utf8`);
114+
} catch (err) {
115+
if (err.code === `ENOENT`) {
116+
yarnrcFile = ``;
117+
} else {
118+
throw err;
119+
}
120+
}
121+
122+
if (checks.usePath)
123+
expect(releases).toHaveLength(1);
124+
else
125+
expect(releases).toEqual(null);
126+
127+
if (checks.usePath)
128+
expect(yarnrcFile).toMatch(yarnrcRegexp);
129+
else
130+
expect(yarnrcFile).not.toMatch(yarnrcRegexp);
131+
132+
await expect(xfs.readJsonPromise(manifestPath)).resolves.toMatchObject({
133+
packageManager: checks.corepackVersion instanceof RegExp
134+
? expect.stringMatching(`yarn@${checks.corepackVersion.source}`)
135+
: `yarn@${checks.corepackVersion}`,
136+
});
137+
}

packages/plugin-essentials/sources/commands/set/version.ts

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,16 @@ export default class SetVersionCommand extends BaseCommand {
7676

7777
async execute() {
7878
const configuration = await Configuration.find(this.context.cwd, this.context.plugins);
79-
if (configuration.get(`yarnPath`) && this.onlyIfNeeded)
80-
return 0;
79+
if (this.onlyIfNeeded && configuration.get(`yarnPath`)) {
80+
const yarnPathSource = configuration.sources.get(`yarnPath`) as PortablePath | undefined;
81+
if (!yarnPathSource)
82+
throw new Error(`Assertion failed: Expected 'yarnPath' to have a source`);
83+
84+
const projectCwd = configuration.projectCwd ?? configuration.startingCwd;
85+
if (ppath.contains(projectCwd, yarnPathSource)) {
86+
return 0;
87+
}
88+
}
8189

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

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

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

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

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

189-
if (updateConfig) {
190-
await Configuration.updateConfiguration(projectCwd, {
191-
yarnPath: projectPath,
192-
});
195+
await Configuration.updateConfiguration(projectCwd, {
196+
yarnPath: projectPath,
197+
});
193198

194-
const manifest = (await Manifest.tryFind(projectCwd)) || new Manifest();
199+
const manifest = (await Manifest.tryFind(projectCwd)) || new Manifest();
195200

196-
manifest.packageManager = `yarn@${
197-
bundleVersion && miscUtils.isTaggedYarnVersion(bundleVersion)
198-
? bundleVersion
199-
// If the version isn't tagged, we use the latest stable version as the wrapper
200-
: await resolveTag(configuration, `stable`)
201-
}`;
201+
manifest.packageManager = `yarn@${
202+
bundleVersion && miscUtils.isTaggedYarnVersion(bundleVersion)
203+
? bundleVersion
204+
// If the version isn't tagged, we use the latest stable version as the wrapper
205+
: await resolveTag(configuration, `stable`)
206+
}`;
202207

203-
const data = {};
204-
manifest.exportTo(data);
208+
const data = {};
209+
manifest.exportTo(data);
205210

206-
const path = ppath.join(projectCwd, Manifest.fileName);
207-
const content = `${JSON.stringify(data, null, manifest.indent)}\n`;
211+
const path = ppath.join(projectCwd, Manifest.fileName);
212+
const content = `${JSON.stringify(data, null, manifest.indent)}\n`;
208213

209-
await xfs.changeFilePromise(path, content, {
210-
automaticNewlines: true,
211-
});
212-
}
214+
await xfs.changeFilePromise(path, content, {
215+
automaticNewlines: true,
216+
});
213217
}

0 commit comments

Comments
 (0)