diff --git a/.changeset/salty-beds-sell.md b/.changeset/salty-beds-sell.md new file mode 100644 index 0000000000..d320f5afd5 --- /dev/null +++ b/.changeset/salty-beds-sell.md @@ -0,0 +1,8 @@ +--- +"@pnpm/lockfile.settings-checker": patch +"@pnpm/lockfile.verification": patch +"@pnpm/core": patch +"@pnpm/deps.status": patch +--- + +Properly throw a frozen lockfile error when changing catalogs defined in `pnpm-workspace.yaml` and running `pnpm install --frozen-lockfile`. This previously passed silently as reported in [#9369](https://github.com/pnpm/pnpm/issues/9369). diff --git a/__fixtures__/pnpm-workspace.yaml b/__fixtures__/pnpm-workspace.yaml index f816f49ab5..21b565263d 100644 --- a/__fixtures__/pnpm-workspace.yaml +++ b/__fixtures__/pnpm-workspace.yaml @@ -18,3 +18,7 @@ packages: - '!workspace-has-shared-package-lock-json' - '!workspace-has-shared-npm-shrinkwrap-json' sharedWorkspaceLockfile: false + +catalog: + # Used in has-outdated-deps-using-catalog-protocol fixture. + is-negative: ^1.0.0 diff --git a/deps/status/src/checkDepsStatus.ts b/deps/status/src/checkDepsStatus.ts index 98f6a9ce1a..8894a5f5de 100644 --- a/deps/status/src/checkDepsStatus.ts +++ b/deps/status/src/checkDepsStatus.ts @@ -488,6 +488,7 @@ async function assertWantedLockfileUpToDate ( ]) const outdatedLockfileSettingName = getOutdatedLockfileSetting(wantedLockfile, { + catalogs: config.catalogs, autoInstallPeers: config.autoInstallPeers, injectWorkspacePackages: config.injectWorkspacePackages, excludeLinksFromLockfile: config.excludeLinksFromLockfile, diff --git a/lockfile/settings-checker/package.json b/lockfile/settings-checker/package.json index 1e914ed39b..424c82b184 100644 --- a/lockfile/settings-checker/package.json +++ b/lockfile/settings-checker/package.json @@ -33,8 +33,10 @@ "compile": "tsc --build && pnpm run lint --fix" }, "dependencies": { + "@pnpm/catalogs.types": "workspace:*", "@pnpm/crypto.hash": "workspace:*", "@pnpm/lockfile.types": "workspace:*", + "@pnpm/lockfile.verification": "workspace:*", "@pnpm/parse-overrides": "workspace:*", "p-map-values": "catalog:", "ramda": "catalog:" diff --git a/lockfile/settings-checker/src/getOutdatedLockfileSetting.ts b/lockfile/settings-checker/src/getOutdatedLockfileSetting.ts index e5a8cbe09e..825ac8cd8d 100644 --- a/lockfile/settings-checker/src/getOutdatedLockfileSetting.ts +++ b/lockfile/settings-checker/src/getOutdatedLockfileSetting.ts @@ -1,7 +1,10 @@ +import { type Catalogs } from '@pnpm/catalogs.types' import { type LockfileObject, type PatchFile } from '@pnpm/lockfile.types' +import { allCatalogsAreUpToDate } from '@pnpm/lockfile.verification' import { equals } from 'ramda' export type ChangedField = + | 'catalogs' | 'patchedDependencies' | 'overrides' | 'packageExtensionsChecksum' @@ -15,6 +18,7 @@ export type ChangedField = export function getOutdatedLockfileSetting ( lockfile: LockfileObject, { + catalogs, overrides, packageExtensionsChecksum, ignoredOptionalDependencies, @@ -25,6 +29,7 @@ export function getOutdatedLockfileSetting ( pnpmfileChecksum, injectWorkspacePackages, }: { + catalogs?: Catalogs overrides?: Record packageExtensionsChecksum?: string patchedDependencies?: Record @@ -36,6 +41,9 @@ export function getOutdatedLockfileSetting ( injectWorkspacePackages?: boolean } ): ChangedField | null { + if (!allCatalogsAreUpToDate(catalogs ?? {}, lockfile.catalogs)) { + return 'catalogs' + } if (!equals(lockfile.overrides ?? {}, overrides ?? {})) { return 'overrides' } diff --git a/lockfile/settings-checker/tsconfig.json b/lockfile/settings-checker/tsconfig.json index 4df354e570..71c0277464 100644 --- a/lockfile/settings-checker/tsconfig.json +++ b/lockfile/settings-checker/tsconfig.json @@ -12,6 +12,9 @@ { "path": "../../__utils__/prepare" }, + { + "path": "../../catalogs/types" + }, { "path": "../../config/parse-overrides" }, @@ -20,6 +23,9 @@ }, { "path": "../types" + }, + { + "path": "../verification" } ] } diff --git a/lockfile/verification/src/index.ts b/lockfile/verification/src/index.ts index 31817b065b..ef8428c8b0 100644 --- a/lockfile/verification/src/index.ts +++ b/lockfile/verification/src/index.ts @@ -1,4 +1,5 @@ export { allProjectsAreUpToDate } from './allProjectsAreUpToDate.js' +export { allCatalogsAreUpToDate } from './allCatalogsAreUpToDate.js' export { getWorkspacePackagesByDirectory } from './getWorkspacePackagesByDirectory.js' export { localTarballDepsAreUpToDate } from './localTarballDepsAreUpToDate.js' export { linkedPackagesAreUpToDate } from './linkedPackagesAreUpToDate.js' diff --git a/pkg-manager/core/src/install/index.ts b/pkg-manager/core/src/install/index.ts index c415fb905d..1fd02f53a3 100644 --- a/pkg-manager/core/src/install/index.ts +++ b/pkg-manager/core/src/install/index.ts @@ -410,6 +410,7 @@ export async function mutateModules ( if (!opts.ignorePackageManifest) { const outdatedLockfileSettingName = getOutdatedLockfileSetting(ctx.wantedLockfile, { autoInstallPeers: opts.autoInstallPeers, + catalogs: opts.catalogs, injectWorkspacePackages: opts.injectWorkspacePackages, excludeLinksFromLockfile: opts.excludeLinksFromLockfile, peersSuffixMaxLength: opts.peersSuffixMaxLength, diff --git a/pkg-manager/core/test/catalogs.ts b/pkg-manager/core/test/catalogs.ts index e8833b2547..5296fb4d15 100644 --- a/pkg-manager/core/test/catalogs.ts +++ b/pkg-manager/core/test/catalogs.ts @@ -256,6 +256,49 @@ test('lockfile is updated if catalog config changes', async () => { }) }) +test('frozen lockfile error is thrown if catalog config changes', async () => { + const { options, projects, readLockfile } = preparePackagesAndReturnObjects([ + { + name: 'project1', + dependencies: { + 'is-positive': 'catalog:', + }, + }, + ]) + + await mutateModules(installProjects(projects), { + ...options, + lockfileOnly: true, + catalogs: { + default: { + 'is-positive': '=1.0.0', + }, + }, + }) + + expect(readLockfile().importers['project1' as ProjectId]).toEqual({ + dependencies: { + 'is-positive': { + specifier: 'catalog:', + version: '1.0.0', + }, + }, + }) + + const frozenLockfileMutation = mutateModules(installProjects(projects), { + ...options, + lockfileOnly: true, + frozenLockfile: true, + catalogs: { + default: { + 'is-positive': '=3.1.0', + }, + }, + }) + + await expect(frozenLockfileMutation).rejects.toThrow('Cannot proceed with the frozen installation. The current "catalogs" configuration doesn\'t match the value found in the lockfile') +}) + test('lockfile catalog snapshots retain existing entries on --filter', async () => { const { options, projects, readLockfile } = preparePackagesAndReturnObjects([ { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ccf8ef867a..dbc2ab98d1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4012,12 +4012,18 @@ importers: lockfile/settings-checker: dependencies: + '@pnpm/catalogs.types': + specifier: workspace:* + version: link:../../catalogs/types '@pnpm/crypto.hash': specifier: workspace:* version: link:../../crypto/hash '@pnpm/lockfile.types': specifier: workspace:* version: link:../types + '@pnpm/lockfile.verification': + specifier: workspace:* + version: link:../verification '@pnpm/parse-overrides': specifier: workspace:* version: link:../../config/parse-overrides