feat(self-update): point users at the migration guide across major bumps (#11354)

When `pnpm self-update <version>` 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.
This commit is contained in:
Zoltan Kochan
2026-04-28 00:45:23 +02:00
committed by GitHub
parent 38a5d700ae
commit f3e33d8ecb

View File

@@ -30,6 +30,15 @@ export function cliOptionsTypes (): Record<string, unknown> {
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<number, string> = {
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)