From 46f10165ed8fd7f7862a054516d77960ce2823a9 Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Fri, 14 Nov 2025 15:19:36 +0100 Subject: [PATCH] fix: self-update should not install @pnpm/exe >= 11 (#10190) --- .changeset/dirty-foxes-invite.md | 6 +++++ pnpm-lock.yaml | 26 ++++++++++++------- .../plugin-commands-self-updater/package.json | 6 +++-- .../src/installPnpmToTools.ts | 13 +++++++--- 4 files changed, 36 insertions(+), 15 deletions(-) create mode 100644 .changeset/dirty-foxes-invite.md diff --git a/.changeset/dirty-foxes-invite.md b/.changeset/dirty-foxes-invite.md new file mode 100644 index 0000000000..cbca10d45d --- /dev/null +++ b/.changeset/dirty-foxes-invite.md @@ -0,0 +1,6 @@ +--- +"@pnpm/tools.plugin-commands-self-updater": patch +"pnpm": patch +--- + +`pnpm self-update` should always install the non-executable pnpm package (pnpm in the registry) and never the `@pnpm/exe` package, when installing v11 or newer. We currently cannot ship `@pnpm/exe` as `pkg` doesn't work with ESM [#10190](https://github.com/pnpm/pnpm/pull/10190). diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cf0af7b2ff..008dfe1894 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8646,12 +8646,15 @@ importers: ramda: specifier: 'catalog:' version: '@pnpm/ramda@0.28.1' - rename-overwrite: - specifier: 'catalog:' - version: 6.0.2 render-help: specifier: 'catalog:' version: 1.0.3 + semver: + specifier: 'catalog:' + version: 7.7.2 + symlink-dir: + specifier: 'catalog:' + version: 7.0.0 devDependencies: '@jest/globals': specifier: 'catalog:' @@ -8671,6 +8674,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 @@ -18243,7 +18249,7 @@ snapshots: '@pnpm/fs.packlist': 2.0.0 '@pnpm/logger': 1001.0.0 '@pnpm/prepare-package': 1000.0.16(@pnpm/logger@1001.0.0)(typanion@3.14.0) - '@pnpm/worker': 1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.30) + '@pnpm/worker': 1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.30) '@zkochan/rimraf': 3.0.2 execa: safe-execa@0.1.2 transitivePeerDependencies: @@ -18378,7 +18384,7 @@ snapshots: '@pnpm/find-workspace-dir': 1000.1.0 '@pnpm/logger': 1001.0.0 '@pnpm/types': 1000.6.0 - '@pnpm/worker': 1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.30) + '@pnpm/worker': 1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.30) '@pnpm/workspace.find-packages': 1000.0.25(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.30))(typanion@3.14.0) '@pnpm/workspace.read-manifest': 1000.1.5 load-json-file: 7.0.1 @@ -18578,7 +18584,7 @@ snapshots: '@pnpm/store-controller-types': 1003.0.2 '@pnpm/store.cafs': 1000.0.13 '@pnpm/types': 1000.6.0 - '@pnpm/worker': 1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.30) + '@pnpm/worker': 1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.30) p-defer: 3.0.0 p-limit: 3.1.0 p-queue: 6.6.2 @@ -18597,7 +18603,7 @@ snapshots: '@pnpm/store-controller-types': 1003.0.2 '@pnpm/store.cafs': 1000.0.13 '@pnpm/types': 1000.6.0 - '@pnpm/worker': 1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.30) + '@pnpm/worker': 1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.30) '@zkochan/rimraf': 3.0.2 load-json-file: 6.2.0 ramda: '@pnpm/ramda@0.28.1' @@ -18876,7 +18882,7 @@ snapshots: '@pnpm/graceful-fs': 1000.0.0 '@pnpm/logger': 1001.0.0 '@pnpm/prepare-package': 1000.0.16(@pnpm/logger@1001.0.0)(typanion@3.14.0) - '@pnpm/worker': 1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.30) + '@pnpm/worker': 1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.30) '@zkochan/retry': 0.2.0 lodash.throttle: 4.1.1 p-map-values: 1.0.0 @@ -18915,7 +18921,7 @@ snapshots: dependencies: isexe: 2.0.0 - '@pnpm/worker@1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.30)': + '@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.30)': dependencies: '@pnpm/cafs-types': 1000.0.0 '@pnpm/create-cafs-store': 1000.0.14(@pnpm/logger@1001.0.0) @@ -18924,7 +18930,7 @@ snapshots: '@pnpm/exec.pkg-requires-build': 1000.0.8 '@pnpm/fs.hard-link-dir': 1000.0.1(@pnpm/logger@1001.0.0) '@pnpm/graceful-fs': 1000.0.0 - '@pnpm/logger': link:packages/logger + '@pnpm/logger': 1001.0.0 '@pnpm/store.cafs': 1000.0.13 '@pnpm/symlink-dependency': 1000.0.9(@pnpm/logger@1001.0.0) '@rushstack/worker-pool': 0.4.9(@types/node@22.15.30) diff --git a/tools/plugin-commands-self-updater/package.json b/tools/plugin-commands-self-updater/package.json index b88ba13245..fd028b56aa 100644 --- a/tools/plugin-commands-self-updater/package.json +++ b/tools/plugin-commands-self-updater/package.json @@ -43,8 +43,9 @@ "@zkochan/rimraf": "catalog:", "path-temp": "catalog:", "ramda": "catalog:", - "rename-overwrite": "catalog:", - "render-help": "catalog:" + "render-help": "catalog:", + "semver": "catalog:", + "symlink-dir": "catalog:" }, "peerDependencies": { "@pnpm/logger": "catalog:" @@ -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:" }, diff --git a/tools/plugin-commands-self-updater/src/installPnpmToTools.ts b/tools/plugin-commands-self-updater/src/installPnpmToTools.ts index 55e38669a0..80b57451f0 100644 --- a/tools/plugin-commands-self-updater/src/installPnpmToTools.ts +++ b/tools/plugin-commands-self-updater/src/installPnpmToTools.ts @@ -5,7 +5,8 @@ import { runPnpmCli } from '@pnpm/exec.pnpm-cli-runner' import { getToolDirPath } from '@pnpm/tools.path' import { sync as rimraf } from '@zkochan/rimraf' import { fastPathTemp as pathTemp } from 'path-temp' -import renameOverwrite from 'rename-overwrite' +import semver from 'semver' +import symlinkDir from 'symlink-dir' import { type SelfUpdateCommandOptions } from './selfUpdate.js' export interface InstallPnpmToToolsResult { @@ -15,7 +16,9 @@ export interface InstallPnpmToToolsResult { } export async function installPnpmToTools (pnpmVersion: string, opts: SelfUpdateCommandOptions): Promise { - const currentPkgName = getCurrentPackageName() + // We have moved pnpm to esm and that prevents us from using pkg to bundle pnpm to an executable. + // Related issue: https://github.com/yao-pkg/pkg/issues/16 + const currentPkgName = semver.gt(pnpmVersion, '11.0.0-alpha') ? 'pnpm' : getCurrentPackageName() const dir = getToolDirPath({ pnpmHomeDir: opts.pnpmHomeDir, tool: { @@ -50,7 +53,11 @@ export async function installPnpmToTools (pnpmVersion: string, opts: SelfUpdateC '--config.node-linker=hoisted', '--config.bin=bin', ], { cwd: stage }) - renameOverwrite.sync(stage, dir) + // We need the operation of installing pnpm to be atomic. + // However, we cannot use a rename as that breaks the command shim created for pnpm. + // Hence, we use a symlink. + // In future we may switch back to rename if we will move Node.js out of the pnpm subdirectory. + symlinkDir.sync(stage, dir) } catch (err: unknown) { try { rimraf(stage)