Skip to content

Commit 12e77e5

Browse files
authored
fix: do not resolve fallback descriptor when packageManager is defined (#632)
1 parent b0c4607 commit 12e77e5

File tree

3 files changed

+90
-57
lines changed

3 files changed

+90
-57
lines changed

sources/Engine.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ import * as folderUtils from './folderUtil
1414
import type {NodeError} from './nodeUtils';
1515
import * as semverUtils from './semverUtils';
1616
import * as specUtils from './specUtils';
17-
import {Config, Descriptor, Locator, PackageManagerSpec} from './types';
17+
import {Config, Descriptor, LazyLocator, Locator} from './types';
1818
import {SupportedPackageManagers, SupportedPackageManagerSet} from './types';
19-
import {isSupportedPackageManager} from './types';
19+
import {isSupportedPackageManager, PackageManagerSpec} from './types';
2020

2121
export type PreparedPackageManagerInfo = Awaited<ReturnType<Engine[`ensurePackageManager`]>>;
2222

@@ -244,12 +244,16 @@ export class Engine {
244244
* project using the default package managers, and configure it so that we
245245
* don't need to ask again in the future.
246246
*/
247-
async findProjectSpec(initialCwd: string, locator: Locator, {transparent = false}: {transparent?: boolean} = {}): Promise<Descriptor> {
247+
async findProjectSpec(initialCwd: string, locator: Locator | LazyLocator, {transparent = false}: {transparent?: boolean} = {}): Promise<Descriptor> {
248248
// A locator is a valid descriptor (but not the other way around)
249249
const fallbackDescriptor = {name: locator.name, range: `${locator.reference}`};
250250

251-
if (process.env.COREPACK_ENABLE_PROJECT_SPEC === `0`)
251+
if (process.env.COREPACK_ENABLE_PROJECT_SPEC === `0`) {
252+
if (typeof locator.reference === `function`)
253+
fallbackDescriptor.range = await locator.reference();
254+
252255
return fallbackDescriptor;
256+
}
253257

254258
if (process.env.COREPACK_ENABLE_STRICT === `0`)
255259
transparent = true;
@@ -258,11 +262,18 @@ export class Engine {
258262
const result = await specUtils.loadSpec(initialCwd);
259263

260264
switch (result.type) {
261-
case `NoProject`:
265+
case `NoProject`: {
266+
if (typeof locator.reference === `function`)
267+
fallbackDescriptor.range = await locator.reference();
268+
262269
debugUtils.log(`Falling back to ${fallbackDescriptor.name}@${fallbackDescriptor.range} as no project manifest were found`);
263270
return fallbackDescriptor;
271+
}
264272

265273
case `NoSpec`: {
274+
if (typeof locator.reference === `function`)
275+
fallbackDescriptor.range = await locator.reference();
276+
266277
if (process.env.COREPACK_ENABLE_AUTO_PIN !== `0`) {
267278
const resolved = await this.resolveDescriptor(fallbackDescriptor, {allowTags: true});
268279
if (resolved === null)
@@ -284,6 +295,9 @@ export class Engine {
284295
case `Found`: {
285296
if (result.spec.name !== locator.name) {
286297
if (transparent) {
298+
if (typeof locator.reference === `function`)
299+
fallbackDescriptor.range = await locator.reference();
300+
287301
debugUtils.log(`Falling back to ${fallbackDescriptor.name}@${fallbackDescriptor.range} in a ${result.spec.name}@${result.spec.range} project`);
288302
return fallbackDescriptor;
289303
} else {
@@ -299,14 +313,14 @@ export class Engine {
299313
}
300314

301315
async executePackageManagerRequest({packageManager, binaryName, binaryVersion}: PackageManagerRequest, {cwd, args}: {cwd: string, args: Array<string>}): Promise<void> {
302-
let fallbackLocator: Locator = {
316+
let fallbackLocator: Locator | LazyLocator = {
303317
name: binaryName as SupportedPackageManagers,
304318
reference: undefined as any,
305319
};
306320

307321
let isTransparentCommand = false;
308322
if (packageManager != null) {
309-
const defaultVersion = binaryVersion || await this.getDefaultVersion(packageManager);
323+
const defaultVersion = binaryVersion || (() => this.getDefaultVersion(packageManager));
310324
const definition = this.config.definitions[packageManager]!;
311325

312326
// If all leading segments match one of the patterns defined in the `transparent`
@@ -325,7 +339,7 @@ export class Engine {
325339

326340
fallbackLocator = {
327341
name: packageManager,
328-
reference: fallbackReference,
342+
reference: fallbackReference as string,
329343
};
330344
}
331345

sources/types.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,18 @@ export interface Locator {
145145
*/
146146
reference: string;
147147
}
148+
149+
/**
150+
*
151+
*/
152+
export interface LazyLocator {
153+
/**
154+
* The name of the package manager required.
155+
*/
156+
name: string;
157+
158+
/**
159+
* The exact version required.
160+
*/
161+
reference: () => Promise<string>;
162+
}

tests/main.test.ts

Lines changed: 53 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -497,15 +497,10 @@ describe(`read-only and offline environment`, () => {
497497
exitCode: 0,
498498
});
499499

500-
// Let corepack discover the latest yarn version.
501-
// BUG: This should not be necessary with a fully specified version in package.json plus populated corepack cache.
502-
// Engine.executePackageManagerRequest needs to defer the fallback work. This requires a big refactoring.
503-
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
504-
exitCode: 0,
505-
});
506-
507500
// Make COREPACK_HOME ro
508501
const home = npath.toPortablePath(folderUtils.getCorepackHomeFolder());
502+
// Make a lastKnownGood.json file with not JSON-parsable content:
503+
await xfs.writeFilePromise(ppath.join(home, `lastKnownGood.json`), `{`);
509504
await xfs.chmodPromise(ppath.join(home, `lastKnownGood.json`), 0o444);
510505
await xfs.chmodPromise(home, 0o555);
511506

@@ -967,54 +962,63 @@ for (const authType of [`COREPACK_NPM_REGISTRY`, `COREPACK_NPM_TOKEN`, `COREPACK
967962
describe(`handle integrity checks`, () => {
968963
beforeEach(() => {
969964
process.env.AUTH_TYPE = `COREPACK_NPM_TOKEN`; // See `_registryServer.mjs`
970-
process.env.COREPACK_DEFAULT_TO_LATEST = `1`;
971965
});
972966

973-
it(`should return no error when signature matches`, async () => {
974-
process.env.TEST_INTEGRITY = `valid`; // See `_registryServer.mjs`
975-
976-
await xfs.mktempPromise(async cwd => {
977-
await Promise.all([
978-
expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
979-
exitCode: 0,
980-
stdout: `pnpm: Hello from custom registry\n`,
981-
stderr: ``,
982-
}),
983-
expect(runCli(cwd, [`[email protected]`, `--version`], true)).resolves.toMatchObject({
984-
exitCode: 0,
985-
stdout: `yarn: Hello from custom registry\n`,
986-
stderr: ``,
987-
}),
988-
expect(runCli(cwd, [`[email protected]`, `--version`], true)).resolves.toMatchObject({
989-
exitCode: 0,
990-
stdout: `yarn: Hello from custom registry\n`,
991-
stderr: ``,
992-
}),
993-
]);
967+
describe(`when signature matches`, () => {
968+
beforeEach(() => {
969+
process.env.TEST_INTEGRITY = `valid`; // See `_registryServer.mjs`
970+
});
994971

972+
it(`should return no error when calling 'corepack use'`, async () => {
973+
await xfs.mktempPromise(async cwd => {
995974
// Skip rest of the test on Windows & Node.js 18.x as it inevitably times out otherwise.
996-
if (process.version.startsWith(`v18.`) && os.platform() === `win32`) return;
975+
if (process.version.startsWith(`v18.`) && os.platform() === `win32`) return;
976+
977+
// Removing home directory to force the "re-download"
978+
await xfs.rmPromise(process.env.COREPACK_HOME as any, {recursive: true});
979+
980+
await Promise.all([
981+
expect(runCli(cwd, [`use`, `pnpm`], true)).resolves.toMatchObject({
982+
exitCode: 0,
983+
stdout: `Installing [email protected] in the project...\n\npnpm: Hello from custom registry\n`,
984+
stderr: ``,
985+
}),
986+
expect(runCli(cwd, [`use`, `[email protected]`], true)).resolves.toMatchObject({
987+
exitCode: 0,
988+
stdout: `Installing [email protected] in the project...\n\nyarn: Hello from custom registry\n`,
989+
stderr: ``,
990+
}),
991+
expect(runCli(cwd, [`use`, `yarn@latest`], true)).resolves.toMatchObject({
992+
exitCode: 0,
993+
stdout: `Installing [email protected] in the project...\n\nyarn: Hello from custom registry\n`,
994+
stderr: ``,
995+
}),
996+
]);
997+
});
998+
});
997999

998-
// Removing home directory to force the "re-download"
999-
await xfs.rmPromise(process.env.COREPACK_HOME as any, {recursive: true});
10001000

1001-
await Promise.all([
1002-
expect(runCli(cwd, [`use`, `pnpm`], true)).resolves.toMatchObject({
1003-
exitCode: 0,
1004-
stdout: `Installing [email protected] in the project...\n\npnpm: Hello from custom registry\n`,
1005-
stderr: ``,
1006-
}),
1007-
expect(runCli(cwd, [`use`, `[email protected]`], true)).resolves.toMatchObject({
1008-
exitCode: 0,
1009-
stdout: `Installing [email protected] in the project...\n\nyarn: Hello from custom registry\n`,
1010-
stderr: ``,
1011-
}),
1012-
expect(runCli(cwd, [`use`, `yarn@latest`], true)).resolves.toMatchObject({
1013-
exitCode: 0,
1014-
stdout: `Installing [email protected] in the project...\n\nyarn: Hello from custom registry\n`,
1015-
stderr: ``,
1016-
}),
1017-
]);
1001+
it(`should return no error when fetching latest version`, async () => {
1002+
process.env.COREPACK_DEFAULT_TO_LATEST = `1`;
1003+
await xfs.mktempPromise(async cwd => {
1004+
await Promise.all([
1005+
expect(runCli(cwd, [`pnpm`, `--version`], true)).resolves.toMatchObject({
1006+
exitCode: 0,
1007+
stdout: `pnpm: Hello from custom registry\n`,
1008+
stderr: ``,
1009+
}),
1010+
expect(runCli(cwd, [`[email protected]`, `--version`], true)).resolves.toMatchObject({
1011+
exitCode: 0,
1012+
stdout: `yarn: Hello from custom registry\n`,
1013+
stderr: ``,
1014+
}),
1015+
expect(runCli(cwd, [`[email protected]`, `--version`], true)).resolves.toMatchObject({
1016+
exitCode: 0,
1017+
stdout: `yarn: Hello from custom registry\n`,
1018+
stderr: ``,
1019+
}),
1020+
]);
1021+
});
10181022
});
10191023
});
10201024
it(`should return an error when signature does not match with a tag`, async () => {

0 commit comments

Comments
 (0)