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

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

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` pin when present
(via the existing `managePackageManagerVersions` gate), 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 is the v10-line backport of the corresponding main-branch change
in #11354.

* fix: update lockfile
This commit is contained in:
Zoltan Kochan
2026-04-28 01:21:13 +02:00
committed by GitHub
parent 2a1ffe1956
commit bebe10647c
3 changed files with 42 additions and 0 deletions

6
pnpm-lock.yaml generated
View File

@@ -8660,6 +8660,9 @@ importers:
render-help:
specifier: 'catalog:'
version: 1.0.3
semver:
specifier: 'catalog:'
version: 7.7.4
symlink-dir:
specifier: 'catalog:'
version: 6.0.5
@@ -8682,6 +8685,9 @@ importers:
'@types/ramda':
specifier: 'catalog:'
version: 0.29.12
'@types/semver':
specifier: 'catalog:'
version: 7.5.3
cross-spawn:
specifier: 'catalog:'
version: 7.0.6

View File

@@ -44,6 +44,7 @@
"path-temp": "catalog:",
"ramda": "catalog:",
"render-help": "catalog:",
"semver": "catalog:",
"symlink-dir": "catalog:"
},
"peerDependencies": {
@@ -56,6 +57,7 @@
"@pnpm/tools.plugin-commands-self-updater": "workspace:*",
"@types/cross-spawn": "catalog:",
"@types/ramda": "catalog:",
"@types/semver": "catalog:",
"cross-spawn": "catalog:",
"nock": "catalog:"
},

View File

@@ -9,6 +9,7 @@ import { readProjectManifest } from '@pnpm/read-project-manifest'
import { linkBins } from '@pnpm/link-bins'
import pick from 'ramda/src/pick'
import renderHelp from 'render-help'
import semver from 'semver'
import { installPnpmToTools } from './installPnpmToTools.js'
export function rcOptionsTypes (): Record<string, unknown> {
@@ -23,6 +24,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 function help (): string {
return renderHelp({
description: 'Updates pnpm to the latest version (or the one specified)',
@@ -69,6 +79,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`, 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 && opts.managePackageManagerVersions) {
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 && opts.managePackageManagerVersions) {
if (opts.wantedPackageManager?.version !== resolution.manifest.version) {
const { manifest, writeProjectManifest } = await readProjectManifest(opts.rootProjectManifestDir)