fix: do not resolve fallback descriptor when `packageManager` is defined (#632)

This commit is contained in:
Antoine du Hamel 2025-02-08 18:55:54 +01:00 committed by GitHub
parent b0c46078f1
commit 12e77e5069
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 90 additions and 57 deletions

View File

@ -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`]>>;
@ -244,12 +244,16 @@ export class Engine {
* 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,11 +262,18 @@ 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 (typeof locator.reference === `function`)
fallbackDescriptor.range = await locator.reference();
if (process.env.COREPACK_ENABLE_AUTO_PIN !== `0`) {
const resolved = await this.resolveDescriptor(fallbackDescriptor, {allowTags: true});
if (resolved === null)
@ -284,6 +295,9 @@ export class Engine {
case `Found`: {
if (result.spec.name !== locator.name) {
if (transparent) {
if (typeof locator.reference === `function`)
fallbackDescriptor.range = await locator.reference();
debugUtils.log(`Falling back to ${fallbackDescriptor.name}@${fallbackDescriptor.range} in a ${result.spec.name}@${result.spec.range} project`);
return fallbackDescriptor;
} else {
@ -299,14 +313,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 +339,7 @@ export class Engine {
fallbackLocator = {
name: packageManager,
reference: fallbackReference,
reference: fallbackReference as string,
};
}

View File

@ -145,3 +145,18 @@ export interface Locator {
*/
reference: string;
}
/**
*
*/
export interface LazyLocator {
/**
* The name of the package manager required.
*/
name: string;
/**
* The exact version required.
*/
reference: () => Promise<string>;
}

View File

@ -497,15 +497,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);
@ -967,54 +962,63 @@ 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`
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: ``,
}),
]);
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;
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});
// 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 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 tag`, async () => {