diff --git a/.changeset/six-carrots-cross.md b/.changeset/six-carrots-cross.md new file mode 100644 index 0000000000..6e6a644e37 --- /dev/null +++ b/.changeset/six-carrots-cross.md @@ -0,0 +1,6 @@ +--- +"@pnpm/releasing.commands": patch +"pnpm": patch +--- + +Run `preversion`, `version`, and `postversion` lifecycle scripts for `pnpm version`. diff --git a/releasing/commands/src/version/index.ts b/releasing/commands/src/version/index.ts index a1ad99d7e9..bad764c151 100644 --- a/releasing/commands/src/version/index.ts +++ b/releasing/commands/src/version/index.ts @@ -3,6 +3,7 @@ import path from 'node:path' import { readProjectManifest } from '@pnpm/cli.utils' import { type Config, types as allTypes } from '@pnpm/config.reader' import { PnpmError } from '@pnpm/error' +import { runLifecycleHook, type RunLifecycleHookOptions } from '@pnpm/exec.lifecycle' import { isGitRepo, isWorkingTreeClean } from '@pnpm/network.git-utils' import { filterProjectsFromDir, type WorkspaceFilter } from '@pnpm/workspace.projects-filter' import { safeExeca as execa } from 'execa' @@ -196,6 +197,8 @@ export async function handler ( await commitAndTag(changes, { ...opts, cwd: gitCwd }) } + await Promise.all(changes.map(change => runVersionLifecycleHook('postversion', change, opts))) + if (opts.json) { return JSON.stringify(changes.map(({ manifestPath: _manifestPath, ...change }) => change), null, 2) } @@ -226,6 +229,15 @@ async function bumpPackageVersion ( throw new PnpmError('INVALID_VERSION', `Invalid version in ${pkgDir}: ${currentVersion}`) } + const preVersionChange: VersionChange = { + name: manifest.name, + currentVersion, + newVersion: currentVersion, + path: pkgDir, + manifestPath: path.join(pkgDir, fileName), + } + await runVersionLifecycleHook('preversion', preVersionChange, opts) + const newVersion = explicitVersion ?? inc(currentVersion, rawBump as BumpType, false, opts.preid) if (!newVersion) { @@ -239,13 +251,37 @@ async function bumpPackageVersion ( manifest.version = newVersion await writeProjectManifest(manifest) - return { + const change = { name: manifest.name, currentVersion, newVersion, path: pkgDir, manifestPath: path.join(pkgDir, fileName), } + await runVersionLifecycleHook('version', change, opts) + + return change +} + +async function runVersionLifecycleHook (stage: 'preversion' | 'version' | 'postversion', change: VersionChange, opts: VersionHandlerOptions): Promise { + if (opts.ignoreScripts === true) return + + const { manifest } = await readProjectManifest(change.path) + const lifecycleOpts: RunLifecycleHookOptions = { + depPath: change.name, + extraBinPaths: opts.extraBinPaths, + extraEnv: opts.extraEnv, + initCwd: opts.dir, + pkgRoot: change.path, + rootModulesDir: path.join(change.path, opts.modulesDir ?? 'node_modules'), + scriptShell: opts.scriptShell, + scriptsPrependNodePath: opts.scriptsPrependNodePath, + shellEmulator: opts.shellEmulator, + stdio: 'inherit', + unsafePerm: opts.unsafePerm ?? false, + userAgent: opts.userAgent, + } + await runLifecycleHook(stage, manifest, lifecycleOpts) } async function commitAndTag (changes: VersionChange[], opts: VersionHandlerOptions & { cwd: string }): Promise { diff --git a/releasing/commands/test/version/index.test.ts b/releasing/commands/test/version/index.test.ts index d603d6442c..b98c8b019c 100644 --- a/releasing/commands/test/version/index.test.ts +++ b/releasing/commands/test/version/index.test.ts @@ -229,6 +229,38 @@ describe('version command', () => { ).rejects.toMatchObject({ code: 'ERR_PNPM_INVALID_VERSION' }) }) + it('should run version lifecycle scripts in order', async () => { + const lifecycleLog = path.join(tempDir, 'lifecycle.log') + const logLifecycleScript = path.join(tempDir, 'log-lifecycle.cjs') + fs.writeFileSync(logLifecycleScript, `const fs = require('fs') +const path = require('path') +const manifest = require(path.join(process.cwd(), 'package.json')) +fs.appendFileSync(process.argv[2], process.argv[3] + ':' + manifest.version + '\\n') +`) + fs.writeFileSync(path.join(tempDir, 'package.json'), JSON.stringify({ + name: 'test-pkg', + version: '1.0.0', + scripts: { + preversion: `node ${JSON.stringify(logLifecycleScript)} ${JSON.stringify(lifecycleLog)} preversion`, + version: `node ${JSON.stringify(logLifecycleScript)} ${JSON.stringify(lifecycleLog)} version`, + postversion: `node ${JSON.stringify(logLifecycleScript)} ${JSON.stringify(lifecycleLog)} postversion`, + }, + })) + + await handler({ + dir: tempDir, + workspaceDir: tempDir, + gitChecks: false, + gitTagVersion: false, + } as any, ['patch']) // eslint-disable-line @typescript-eslint/no-explicit-any + + expect(fs.readFileSync(lifecycleLog, 'utf-8')).toBe( + 'preversion:1.0.0\n' + + 'version:1.0.1\n' + + 'postversion:1.0.1\n' + ) + }) + describe('git integration', () => { let origCwd: string