From f3e33d8ecb30ebe860ee96472340171fa86ee340 Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Tue, 28 Apr 2026 00:45:23 +0200 Subject: [PATCH] feat(self-update): point users at the migration guide across major bumps (#11354) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When `pnpm self-update ` crosses a pnpm major (upward) from the version being upgraded from, print a one-line pointer to the versioned migration guide on pnpm.io. The "from" version is the project's `packageManager`/`devEngines.packageManager` pin when present (so the hint still fires if the running pnpm is already the new major — e.g. corepack-managed), falling back to the running binary's version otherwise. No-op updates (target === previous) are silent. v11 points at https://pnpm.io/11.x/migration. Future majors register an entry in the in-file `MAJOR_UPGRADE_HINTS` table. --- .../commands/src/self-updater/selfUpdate.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/engine/pm/commands/src/self-updater/selfUpdate.ts b/engine/pm/commands/src/self-updater/selfUpdate.ts index a4b850019f..22b926be22 100644 --- a/engine/pm/commands/src/self-updater/selfUpdate.ts +++ b/engine/pm/commands/src/self-updater/selfUpdate.ts @@ -30,6 +30,15 @@ export function cliOptionsTypes (): Record { export const commandNames = ['self-update'] +// Migration guidance printed once when `pnpm self-update` crosses a major +// boundary. Add an entry here for each future major that ships breaking +// changes users need to act on. +const MAJOR_UPGRADE_HINTS: Record = { + 11: + 'pnpm v11 removed or renamed several v10 settings. ' + + 'See https://pnpm.io/11.x/migration for migration instructions.', +} + export const skipPackageManagerCheck = true export function help (): string { @@ -76,6 +85,30 @@ export async function handler ( throw new PnpmError('CANNOT_RESOLVE_PNPM', `Cannot find "${bareSpecifier}" version of pnpm`) } + // Determine the "previous" pnpm version being upgraded FROM. If the + // project pins pnpm via `packageManager`/`devEngines.packageManager`, + // the pin is the source of truth — the running pnpm binary may already + // be at a newer major (e.g. a globally-installed v11 operating on a + // project still pinned to v10). Otherwise fall back to the running + // binary. Skip the hint entirely on a no-op (target === previous). + const targetVersion = resolution.manifest.version + let previousVersion: string | undefined + if (opts.wantedPackageManager?.name === packageManager.name) { + if (opts.wantedPackageManager.version !== targetVersion) { + previousVersion = opts.wantedPackageManager.version + } + } else if (packageManager.version !== targetVersion) { + previousVersion = packageManager.version + } + const previousMajor = previousVersion != null + ? semver.coerce(previousVersion)?.major + : undefined + const targetMajor = semver.major(targetVersion) + if (previousMajor != null && targetMajor > previousMajor) { + const hint = MAJOR_UPGRADE_HINTS[targetMajor] + if (hint) globalWarn(hint) + } + if (opts.wantedPackageManager?.name === packageManager.name) { if (opts.wantedPackageManager?.version !== resolution.manifest.version) { const { manifest, writeProjectManifest } = await readProjectManifest(opts.rootProjectManifestDir)