From 840e2000b5a85b24de004e1fcec57d5b20cbc72b Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Thu, 22 Aug 2024 11:16:08 +0200 Subject: [PATCH] feat: self-update should update pnpm in the packageManager field (#8449) * feat: self-update should update pnpm in the packageManager field * fix: self-update should update packageManager in package.json * fix: verify package manager name * refactor: test --- .changeset/cuddly-comics-fix.md | 4 +- pnpm-lock.yaml | 3 + pnpm/src/main.ts | 2 +- .../plugin-commands-self-updater/package.json | 1 + .../src/selfUpdate.ts | 12 +- .../test/selfUpdate.test.ts | 110 +++++++++--------- .../tsconfig.json | 3 + 7 files changed, 76 insertions(+), 59 deletions(-) diff --git a/.changeset/cuddly-comics-fix.md b/.changeset/cuddly-comics-fix.md index d786f1523e..a3252d2a4e 100644 --- a/.changeset/cuddly-comics-fix.md +++ b/.changeset/cuddly-comics-fix.md @@ -7,4 +7,6 @@ "pnpm": minor --- -Added a new command for upgrading pnpm itself, when it isn't managed by Corepack: `pnpm self-update`. This command will work, when pnpm was installed via the standalone script from the [pnpm installation page](https://pnpm.io/installation#using-a-standalone-script) [#8424](https://github.com/pnpm/pnpm/pull/8424). +Added a new command for upgrading pnpm itself when it isn't managed by Corepack: `pnpm self-update`. This command will work, when pnpm was installed via the standalone script from the [pnpm installation page](https://pnpm.io/installation#using-a-standalone-script) [#8424](https://github.com/pnpm/pnpm/pull/8424). + +When executed in a project that has a `packageManager` field in its `package.json` file, pnpm will update its version in the `packageManager` field. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7ec25f5512..7e2a0c4dc8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7326,6 +7326,9 @@ importers: '@pnpm/plugin-commands-installation': specifier: workspace:* version: link:../../pkg-manager/plugin-commands-installation + '@pnpm/read-project-manifest': + specifier: workspace:* + version: link:../../pkg-manifest/read-project-manifest '@pnpm/tools.path': specifier: workspace:* version: link:../path diff --git a/pnpm/src/main.ts b/pnpm/src/main.ts index f2bd6a385f..2e5782271c 100644 --- a/pnpm/src/main.ts +++ b/pnpm/src/main.ts @@ -105,7 +105,7 @@ export async function main (inputArgv: string[]): Promise { checkUnknownSetting: false, ignoreNonAuthSettingsFromLocal: isDlxCommand, }) as typeof config - if (!isExecutedByCorepack() && cmd !== 'setup' && cmd !== 'self-update' && config.wantedPackageManager != null) { + if (!isExecutedByCorepack() && cmd !== 'setup' && config.wantedPackageManager != null) { if (config.managePackageManagerVersions) { await switchCliVersion(config) } else { diff --git a/tools/plugin-commands-self-updater/package.json b/tools/plugin-commands-self-updater/package.json index acc386b4af..e734491460 100644 --- a/tools/plugin-commands-self-updater/package.json +++ b/tools/plugin-commands-self-updater/package.json @@ -37,6 +37,7 @@ "@pnpm/link-bins": "workspace:*", "@pnpm/pick-registry-for-package": "workspace:*", "@pnpm/plugin-commands-installation": "workspace:*", + "@pnpm/read-project-manifest": "workspace:*", "@pnpm/tools.path": "workspace:*", "ramda": "catalog:", "render-help": "catalog:" diff --git a/tools/plugin-commands-self-updater/src/selfUpdate.ts b/tools/plugin-commands-self-updater/src/selfUpdate.ts index afb72b22ff..5603f899df 100644 --- a/tools/plugin-commands-self-updater/src/selfUpdate.ts +++ b/tools/plugin-commands-self-updater/src/selfUpdate.ts @@ -4,10 +4,11 @@ import { docsUrl } from '@pnpm/cli-utils' import { getCurrentPackageName, packageManager, isExecutedByCorepack } from '@pnpm/cli-meta' import { createResolver } from '@pnpm/client' import { pickRegistryForPackage } from '@pnpm/pick-registry-for-package' -import { types as allTypes } from '@pnpm/config' +import { type Config, types as allTypes } from '@pnpm/config' import { PnpmError } from '@pnpm/error' import { globalWarn } from '@pnpm/logger' import { add, type InstallCommandOptions } from '@pnpm/plugin-commands-installation' +import { readProjectManifest } from '@pnpm/read-project-manifest' import { getToolDirPath } from '@pnpm/tools.path' import { linkBins } from '@pnpm/link-bins' import pick from 'ramda/src/pick' @@ -34,7 +35,7 @@ export function help (): string { }) } -export type SelfUpdateCommandOptions = InstallCommandOptions +export type SelfUpdateCommandOptions = InstallCommandOptions & Pick export async function handler ( opts: SelfUpdateCommandOptions @@ -57,6 +58,13 @@ export async function handler ( return `The currently active ${packageManager.name} v${packageManager.version} is already "latest" and doesn't need an update` } + if (opts.wantedPackageManager?.name === packageManager.name && opts.managePackageManagerVersions) { + const { manifest, writeProjectManifest } = await readProjectManifest(opts.rootProjectManifestDir) + manifest.packageManager = `pnpm@${resolution.manifest.version}` + await writeProjectManifest(manifest) + return `The current project has been updated to use pnpm v${resolution.manifest.version}` + } + const currentPkgName = getCurrentPackageName() const dir = getToolDirPath({ pnpmHomeDir: opts.pnpmHomeDir, diff --git a/tools/plugin-commands-self-updater/test/selfUpdate.test.ts b/tools/plugin-commands-self-updater/test/selfUpdate.test.ts index 64200c9bd9..7460bdd1ed 100644 --- a/tools/plugin-commands-self-updater/test/selfUpdate.test.ts +++ b/tools/plugin-commands-self-updater/test/selfUpdate.test.ts @@ -1,7 +1,7 @@ import fs from 'fs' import path from 'path' import { prependDirsToPath } from '@pnpm/env.path' -import { tempDir } from '@pnpm/prepare' +import { tempDir, prepare as prepareWithPkg } from '@pnpm/prepare' import { selfUpdate } from '@pnpm/tools.plugin-commands-self-updater' import spawn from 'cross-spawn' import nock from 'nock' @@ -29,6 +29,10 @@ beforeEach(() => { function prepare () { const dir = tempDir(false) + return prepareOptions(dir) +} + +function prepareOptions (dir: string) { return { argv: { original: [], @@ -51,6 +55,26 @@ function prepare () { cacheDir: path.join(dir, '.cache'), virtualStoreDirMaxLength: 120, dir: process.cwd(), + managePackageManagerVersions: false, + } +} + +function createMetadata (latest: string, registry: string) { + return { + name: 'pnpm', + 'dist-tags': { latest }, + versions: { + [latest]: { + name: 'pnpm', + version: latest, + dist: { + shasum: '217063ce3fcbf44f3051666f38b810f1ddefee4a', + tarball: `${registry}pnpm/-/pnpm-${latest}.tgz`, + fileCount: 880, + integrity: 'sha512-Z/WHmRapKT5c8FnCOFPVcb6vT3U8cH9AyyK+1fsVeMaq07bEEHzLO6CzW+AD62IaFkcayDbIe+tT+dVLtGEnJA==', + }, + }, + }, } } @@ -58,24 +82,7 @@ test('self-update', async () => { const opts = prepare() nock(opts.registries.default) .get('/pnpm') - .reply(200, { - name: 'pnpm', - 'dist-tags': { - latest: '9.1.0', - }, - versions: { - '9.1.0': { - name: 'pnpm', - version: '9.1.0', - dist: { - shasum: '217063ce3fcbf44f3051666f38b810f1ddefee4a', - tarball: `${opts.registries.default}pnpm/-/pnpm-9.1.0.tgz`, - fileCount: 880, - integrity: 'sha512-Z/WHmRapKT5c8FnCOFPVcb6vT3U8cH9AyyK+1fsVeMaq07bEEHzLO6CzW+AD62IaFkcayDbIe+tT+dVLtGEnJA==', - }, - }, - }, - }) + .reply(200, createMetadata('9.1.0', opts.registries.default)) nock(opts.registries.default) .get('/pnpm/-/pnpm-9.1.0.tgz') .replyWithFile(200, path.join(__dirname, 'pnpm-9.1.0.tgz')) @@ -100,24 +107,7 @@ test('self-update does nothing when pnpm is up to date', async () => { const opts = prepare() nock(opts.registries.default) .get('/pnpm') - .reply(200, { - name: 'pnpm', - 'dist-tags': { - latest: '9.0.0', - }, - versions: { - '9.0.0': { - name: 'pnpm', - version: '9.0.0', - dist: { - shasum: '217063ce3fcbf44f3051666f38b810f1ddefee4a', - tarball: `${opts.registries.default}pnpm/-/pnpm-9.1.0.tgz`, - fileCount: 880, - integrity: 'sha512-Z/WHmRapKT5c8FnCOFPVcb6vT3U8cH9AyyK+1fsVeMaq07bEEHzLO6CzW+AD62IaFkcayDbIe+tT+dVLtGEnJA==', - }, - }, - }, - }) + .reply(200, createMetadata('9.0.0', opts.registries.default)) const output = await selfUpdate.handler(opts) @@ -128,24 +118,7 @@ test('self-update links pnpm that is already present on the disk', async () => { const opts = prepare() nock(opts.registries.default) .get('/pnpm') - .reply(200, { - name: 'pnpm', - 'dist-tags': { - latest: '9.2.0', - }, - versions: { - '9.2.0': { - name: 'pnpm', - version: '9.2.0', - dist: { - shasum: '217063ce3fcbf44f3051666f38b810f1ddefee4a', - tarball: `${opts.registries.default}pnpm/-/pnpm-9.2.0.tgz`, - fileCount: 880, - integrity: 'sha512-Z/WHmRapKT5c8FnCOFPVcb6vT3U8cH9AyyK+1fsVeMaq07bEEHzLO6CzW+AD62IaFkcayDbIe+tT+dVLtGEnJA==', - }, - }, - }, - }) + .reply(200, createMetadata('9.2.0', opts.registries.default)) const latestPnpmDir = path.join(opts.pnpmHomeDir, '.tools/pnpm/9.2.0/node_modules/pnpm') fs.mkdirSync(latestPnpmDir, { recursive: true }) @@ -166,3 +139,30 @@ console.log('9.2.0')`, 'utf8') expect(status).toBe(0) expect(stdout.toString().trim()).toBe('9.2.0') }) + +test('self-update updates the packageManager field in package.json', async () => { + prepareWithPkg({ + packageManager: 'pnpm@9.0.0', + }) + const opts = { + ...prepareOptions(process.cwd()), + managePackageManagerVersions: true, + wantedPackageManager: { + name: 'pnpm', + version: '9.0.0', + }, + } + nock(opts.registries.default) + .get('/pnpm') + .reply(200, createMetadata('9.1.0', opts.registries.default)) + nock(opts.registries.default) + .get('/pnpm/-/pnpm-9.1.0.tgz') + .replyWithFile(200, path.join(__dirname, 'pnpm-9.1.0.tgz')) + + const output = await selfUpdate.handler(opts) + + expect(output).toBe('The current project has been updated to use pnpm v9.1.0') + + const pkgJson = JSON.parse(fs.readFileSync(path.resolve('package.json'), 'utf8')) + expect(pkgJson.packageManager).toBe('pnpm@9.1.0') +}) diff --git a/tools/plugin-commands-self-updater/tsconfig.json b/tools/plugin-commands-self-updater/tsconfig.json index b02353efb5..e6d4fa1f7c 100644 --- a/tools/plugin-commands-self-updater/tsconfig.json +++ b/tools/plugin-commands-self-updater/tsconfig.json @@ -39,6 +39,9 @@ { "path": "../../pkg-manager/plugin-commands-installation" }, + { + "path": "../../pkg-manifest/read-project-manifest" + }, { "path": "../path" }