fix: accept --ignore-scripts on update (#14513)

Pacquet omitted the lifecycle-script flags from its update argument set even though the TypeScript CLI accepts them. Add both boolean forms and merge them into the update config before dispatch, matching the existing install-family behavior.

Fixes pnpm/pnpm#14512.
This commit is contained in:
Zoltan Kochan authored and GitHub committed 2026-09-03 22:49:49 +02:00
1 parent 543c857181
commit b45ae69f45
5 files changed
+102 -1

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
---
"pacquet": patch
---
`pnpm update` now accepts `--ignore-scripts` and skips lifecycle scripts during the update [pnpm/pnpm#14512](https://github.com/pnpm/pnpm/issues/14512).
+14
View File
@@ -1,6 +1,7 @@
use crate::{
State,
cli_args::{
install::resolve_bool_override,
lockfile_dir::LockfileDirArg,
pipelines::InstallFamilySelection,
recursive,
@@ -148,6 +149,14 @@ pub struct UpdateArgs {
#[clap(long = "ignore-pnpmfile")]
pub ignore_pnpmfile: bool,
/// Don't run lifecycle scripts of the project or its dependencies.
#[clap(long = "ignore-scripts", overrides_with = "no_ignore_scripts")]
pub ignore_scripts: bool,
/// Run lifecycle scripts even when the configuration disables them.
#[clap(long = "no-ignore-scripts", overrides_with = "ignore_scripts")]
pub no_ignore_scripts: bool,
/// URL of a pnpr server to offload revision refresh resolution to.
#[clap(long = "pnpr-server")]
pub pnpr_server: Option<String>,
@@ -179,6 +188,11 @@ struct PatchesWithSelectorError;
impl UpdateArgs {
pub(crate) fn apply_cli_config(&self, config: &mut Config) {
config.ignore_scripts = resolve_bool_override(
self.ignore_scripts,
self.no_ignore_scripts,
config.ignore_scripts,
);
config.ignore_pnpmfile = self.ignore_pnpmfile || config.ignore_pnpmfile;
if let Some(pnpr_server) = self.pnpr_server.clone() {
config.pnpr_server = Some(pnpr_server);
@@ -130,6 +130,23 @@ fn ignore_pnpmfile_flag_applies_to_config() {
assert!(config.ignore_pnpmfile, "flag present → config set");
}
#[test]
fn ignore_scripts_flags_apply_to_config() {
let mut config = Config::default();
assert!(!config.ignore_scripts);
config.ignore_scripts = true;
update_args(&[]).apply_cli_config(&mut config);
assert!(config.ignore_scripts, "flags absent -> config unchanged");
config.ignore_scripts = false;
update_args(&["--ignore-scripts"]).apply_cli_config(&mut config);
assert!(config.ignore_scripts, "--ignore-scripts enables the setting");
update_args(&["--no-ignore-scripts"]).apply_cli_config(&mut config);
assert!(!config.ignore_scripts, "--no-ignore-scripts disables the setting");
}
#[test]
fn pnpr_server_flag_applies_to_config() {
let mut config = Config::default();
+22
View File
@@ -972,6 +972,28 @@ fn update_workspace_that_links_nothing_still_runs_project_scripts() {
drop((root, anchor));
}
#[test]
fn update_ignore_scripts_skips_project_scripts() {
let root = TempDir::new().expect("create temp directory");
let workspace = root.path().to_path_buf();
fs::write(
workspace.join("package.json"),
r#"{ "name": "test-update", "version": "1.0.0",
"scripts": { "postinstall": "node -e \"require('fs').writeFileSync('postinstall-ran', '')\"" } }"#,
)
.expect("write package.json");
pacquet(&workspace, ["update", "--ignore-scripts"]).assert().success();
assert!(
!workspace.join("postinstall-ran").exists(),
"--ignore-scripts should skip the project's lifecycle scripts",
);
drop(root);
}
/// Naming a dependency that no workspace project publishes fails, since
/// there is nothing to link it to.
#[test]
@@ -1,15 +1,22 @@
import { execFile } from 'node:child_process'
import path from 'node:path'
import { promisify } from 'node:util'
import { beforeAll, describe, expect, it, test } from '@jest/globals'
import type { PnpmError } from '@pnpm/error'
import { install, update } from '@pnpm/installing.commands'
import { prepare, preparePackages } from '@pnpm/prepare'
import { addDistTag } from '@pnpm/testing.registry-mock'
import { createTestIpcServer } from '@pnpm/test-ipc-server'
import { addDistTag, REGISTRY_MOCK_PORT } from '@pnpm/testing.registry-mock'
import type { ProjectManifest } from '@pnpm/types'
import { loadJsonFileSync } from 'load-json-file'
import { DEFAULT_OPTS } from '../utils/index.js'
const execFileAsync = promisify(execFile)
const pnpmBin = path.join(import.meta.dirname, '../../../../pnpm/bin/pnpm.mjs')
const registry = `http://localhost:${REGISTRY_MOCK_PORT}/`
test.each([
{ dependencies: ['is-positive'], options: { patches: true } },
{ dependencies: [], options: { latest: true, patches: true } },
@@ -25,6 +32,42 @@ test.each([
})
})
test('update ignores lifecycle scripts when --ignore-scripts is used', async () => {
await using server = await createTestIpcServer()
const project = prepare({
dependencies: {
'@pnpm.e2e/foo': '1.0.0',
},
})
await execFileAsync(process.execPath, [
pnpmBin,
'install',
`--registry=${registry}`,
])
project.writePackageJson({
dependencies: {
'@pnpm.e2e/foo': '1.0.0',
},
scripts: {
postinstall: server.sendLineScript('postinstall'),
},
})
await execFileAsync(process.execPath, [
pnpmBin,
'update',
'@pnpm.e2e/foo@2.0.0',
'--ignore-scripts',
`--registry=${registry}`,
])
expect(loadJsonFileSync<ProjectManifest>('package.json').dependencies?.['@pnpm.e2e/foo']).toBe('2.0.0')
expect(server.getLines()).toStrictEqual([])
})
test('update with "*" pattern', async () => {
await addDistTag({ package: '@pnpm.e2e/peer-a', version: '1.0.1', distTag: 'latest' })
await addDistTag({ package: '@pnpm.e2e/peer-c', version: '2.0.0', distTag: 'latest' })