diff --git a/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap b/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap index 085721749d90013a402f1fa67c3ccdd0166c492b..780b7acd945d69e7a99f39984d2a73a7320a76dc 100644 --- a/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap +++ b/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap @@ -34,7 +34,6 @@ Object { "constraints": Object { "node": ">= 8.9.2", "npm": "^8.0.0", - "pnpm": "^1.2.0", "vscode": ">=1.49.3", "yarn": "disabled", }, diff --git a/lib/modules/manager/npm/extract/index.spec.ts b/lib/modules/manager/npm/extract/index.spec.ts index 8772fc9bbc0111bb4c0fa77fab8f81959cacf0d1..734463dfe679c8d4107764627dcf3f48f52d1b6a 100644 --- a/lib/modules/manager/npm/extract/index.spec.ts +++ b/lib/modules/manager/npm/extract/index.spec.ts @@ -327,7 +327,6 @@ describe('modules/manager/npm/extract/index', () => { constraints: { node: '>= 8.9.2', npm: '^8.0.0', - pnpm: '^1.2.0', vscode: '>=1.49.3', yarn: 'disabled', }, diff --git a/lib/modules/manager/npm/extract/index.ts b/lib/modules/manager/npm/extract/index.ts index 180c5e9eb9f84c8c776df37c966fe81811245adf..7d9dcc4d612e2d2796e1646b71c20971923d06cf 100644 --- a/lib/modules/manager/npm/extract/index.ts +++ b/lib/modules/manager/npm/extract/index.ts @@ -209,7 +209,6 @@ export async function extractPackageFile( } else if (depName === 'pnpm') { dep.datasource = NpmDatasource.id; dep.commitMessageTopic = 'pnpm'; - constraints.pnpm = dep.currentValue; } else if (depName === 'vscode') { dep.datasource = GithubTagsDatasource.id; dep.packageName = 'microsoft/vscode'; diff --git a/lib/modules/manager/npm/extract/types.ts b/lib/modules/manager/npm/extract/types.ts index cbdba5428f5e0dc5413cad892d0bdf229597e78b..8d01d3d18d0e55ffc902d4da999ceefa1c393a39 100644 --- a/lib/modules/manager/npm/extract/types.ts +++ b/lib/modules/manager/npm/extract/types.ts @@ -12,6 +12,7 @@ export interface NpmPackage extends PackageJson { _args?: any; _id?: any; dependenciesMeta?: DependenciesMeta; + packageManager?: string; } export type LockFileEntry = Record< diff --git a/lib/modules/manager/npm/post-update/__fixtures__/manager-field/package.json b/lib/modules/manager/npm/post-update/__fixtures__/manager-field/package.json new file mode 100644 index 0000000000000000000000000000000000000000..c4623f972e02c438249b2f5c4ce7380d7a973b74 --- /dev/null +++ b/lib/modules/manager/npm/post-update/__fixtures__/manager-field/package.json @@ -0,0 +1,9 @@ +{ + "name": "parent", + "version": "1.0.0", + "engines": { + "pnpm": "=6.15.0" + }, + "engine-strict": true, + "packageManager": "pnpm@6.15.0" +} diff --git a/lib/modules/manager/npm/post-update/__fixtures__/parent/package.json b/lib/modules/manager/npm/post-update/__fixtures__/parent/package.json new file mode 100644 index 0000000000000000000000000000000000000000..f990d3e58f45015e92d6200dc43aef881250d4f7 --- /dev/null +++ b/lib/modules/manager/npm/post-update/__fixtures__/parent/package.json @@ -0,0 +1,7 @@ +{ + "name": "parent", + "version": "1.0.0", + "engines": { + "pnpm": "=6.16.0" + } +} diff --git a/lib/modules/manager/npm/post-update/__snapshots__/pnpm.spec.ts.snap b/lib/modules/manager/npm/post-update/__snapshots__/pnpm.spec.ts.snap index 5ae51b636ad413ab2231f7de6258ab45ed92b933..d35d6ff76a2e5a6484a6caf692ab4f1cf28d5156 100644 --- a/lib/modules/manager/npm/post-update/__snapshots__/pnpm.spec.ts.snap +++ b/lib/modules/manager/npm/post-update/__snapshots__/pnpm.spec.ts.snap @@ -92,6 +92,52 @@ Array [ ] `; +exports[`modules/manager/npm/post-update/pnpm uses constraint version if parent json has constraints 1`] = ` +Array [ + Object { + "cmd": "pnpm install --recursive --lockfile-only --ignore-scripts --ignore-pnpmfile", + "options": Object { + "cwd": "some-folder", + "encoding": "utf-8", + "env": Object { + "HOME": "/home/user", + "HTTPS_PROXY": "https://example.com", + "HTTP_PROXY": "http://example.com", + "LANG": "en_US.UTF-8", + "LC_ALL": "en_US", + "NO_PROXY": "localhost", + "PATH": "/tmp/path", + }, + "maxBuffer": 10485760, + "timeout": 900000, + }, + }, +] +`; + +exports[`modules/manager/npm/post-update/pnpm uses packageManager version and puts it into constraint 1`] = ` +Array [ + Object { + "cmd": "pnpm install --recursive --lockfile-only --ignore-scripts --ignore-pnpmfile", + "options": Object { + "cwd": "some-folder", + "encoding": "utf-8", + "env": Object { + "HOME": "/home/user", + "HTTPS_PROXY": "https://example.com", + "HTTP_PROXY": "http://example.com", + "LANG": "en_US.UTF-8", + "LC_ALL": "en_US", + "NO_PROXY": "localhost", + "PATH": "/tmp/path", + }, + "maxBuffer": 10485760, + "timeout": 900000, + }, + }, +] +`; + exports[`modules/manager/npm/post-update/pnpm uses the new version if packageManager is updated 1`] = ` Array [ Object { diff --git a/lib/modules/manager/npm/post-update/pnpm.spec.ts b/lib/modules/manager/npm/post-update/pnpm.spec.ts index 35222dc255a3a071759ecf78d25755dbce59d38d..d5dba94c8ae5065dedff85ad86cc7ab82167252d 100644 --- a/lib/modules/manager/npm/post-update/pnpm.spec.ts +++ b/lib/modules/manager/npm/post-update/pnpm.spec.ts @@ -1,5 +1,6 @@ import { exec as _exec } from 'child_process'; import { envMock, mockExecAll } from '../../../../../test/exec-util'; +import { Fixtures } from '../../../../../test/fixtures'; import { mocked } from '../../../../../test/util'; import * as _env from '../../../../util/exec/env'; import * as _fs from '../../../../util/fs/proxies'; @@ -77,4 +78,90 @@ describe('modules/manager/npm/post-update/pnpm', () => { expect(execSnapshots).toMatchSnapshot(); // TODO: check docker preCommands }); + + it('uses constraint version if parent json has constraints', async () => { + const execSnapshots = mockExecAll(exec); + const configTemp = { cacheDir: 'some-cache-dir' }; + const fileContent = Fixtures.get('parent/package.json'); + fs.readFile = jest + .fn() + .mockReturnValueOnce(fileContent) + .mockReturnValue('package-lock-contents'); + const res = await pnpmHelper.generateLockFile( + 'some-folder', + {}, + configTemp, + [ + { + depType: 'packageManager', + depName: 'pnpm', + }, + ] + ); + expect(fs.readFile).toHaveBeenCalledTimes(2); + expect(res.lockFile).toBe('package-lock-contents'); + expect(execSnapshots).toMatchSnapshot([ + { + cmd: 'pnpm install --recursive --lockfile-only --ignore-scripts --ignore-pnpmfile', + options: { + cwd: 'some-folder', + encoding: 'utf-8', + env: { + HTTP_PROXY: 'http://example.com', + HTTPS_PROXY: 'https://example.com', + NO_PROXY: 'localhost', + HOME: '/home/user', + PATH: '/tmp/path', + LANG: 'en_US.UTF-8', + LC_ALL: 'en_US', + }, + maxBuffer: 10485760, + timeout: 900000, + }, + }, + ]); + }); + + it('uses packageManager version and puts it into constraint', async () => { + const execSnapshots = mockExecAll(exec); + const configTemp = { cacheDir: 'some-cache-dir' }; + const fileContent = Fixtures.get('manager-field/package.json'); + fs.readFile = jest + .fn() + .mockReturnValueOnce(fileContent) + .mockReturnValue('package-lock-contents'); + const res = await pnpmHelper.generateLockFile( + 'some-folder', + {}, + configTemp, + [ + { + depType: 'packageManager', + depName: 'pnpm', + }, + ] + ); + expect(fs.readFile).toHaveBeenCalledTimes(2); + expect(res.lockFile).toBe('package-lock-contents'); + expect(execSnapshots).toMatchSnapshot([ + { + cmd: 'pnpm install --recursive --lockfile-only --ignore-scripts --ignore-pnpmfile', + options: { + cwd: 'some-folder', + encoding: 'utf-8', + env: { + HTTP_PROXY: 'http://example.com', + HTTPS_PROXY: 'https://example.com', + NO_PROXY: 'localhost', + HOME: '/home/user', + PATH: '/tmp/path', + LANG: 'en_US.UTF-8', + LC_ALL: 'en_US', + }, + maxBuffer: 10485760, + timeout: 900000, + }, + }, + ]); + }); }); diff --git a/lib/modules/manager/npm/post-update/pnpm.ts b/lib/modules/manager/npm/post-update/pnpm.ts index 3f821ec37adc782f33ffbc906f1f23394df13c0e..0b46c381b845cbbef40b7b44785ef162934f4ba9 100644 --- a/lib/modules/manager/npm/post-update/pnpm.ts +++ b/lib/modules/manager/npm/post-update/pnpm.ts @@ -6,6 +6,7 @@ import { exec } from '../../../../util/exec'; import type { ExecOptions, ToolConstraint } from '../../../../util/exec/types'; import { readFile, remove } from '../../../../util/fs'; import type { PostUpdateConfig, Upgrade } from '../../types'; +import type { NpmPackage } from '../extract/types'; import { getNodeConstraint } from './node-version'; import type { GenerateLockFileResult } from './types'; @@ -22,13 +23,9 @@ export async function generateLockFile( let stderr: string; let cmd = 'pnpm'; try { - const pnpmUpdate = upgrades.find( - (upgrade) => - upgrade.depType === 'packageManager' && upgrade.depName === 'pnpm' - ); const pnpmToolConstraint: ToolConstraint = { toolName: 'pnpm', - constraint: pnpmUpdate ? pnpmUpdate.newValue : config.constraints?.pnpm, + constraint: config.constraints?.pnpm ?? (await getPnpmContraint(cwd)), }; const tagConstraint = await getNodeConstraint(config); const execOptions: ExecOptions = { @@ -91,3 +88,26 @@ export async function generateLockFile( } return { lockFile }; } + +async function getPnpmContraint(cwd: string): Promise<string> { + let result; + const rootPackageJson = upath.join(cwd, 'package.json'); + const content = await readFile(rootPackageJson, 'utf8'); + if (content) { + const packageJson: NpmPackage = JSON.parse(content); + const packageManager = packageJson?.packageManager; + if (packageManager?.includes('@')) { + const nameAndVersion = packageManager.split('@'); + const name = nameAndVersion[0]; + if (name === 'pnpm') { + result = nameAndVersion[1]; + } + } else { + const engines = packageJson?.engines; + if (engines) { + result = engines['pnpm']; + } + } + } + return result; +}