Skip to content

feat(npm): prompt for global install lifecycle scripts#34850

Open
divybot wants to merge 9 commits into
mainfrom
orch/divybot-458
Open

feat(npm): prompt for global install lifecycle scripts#34850
divybot wants to merge 9 commits into
mainfrom
orch/divybot-458

Conversation

@divybot

@divybot divybot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Adds lifecycle-script handling for global npm installs by detecting packages with install scripts, prompting before approving them, and writing the resulting allowScripts setting into the hidden per-command config so scripts run in that install's local node_modules instead of the global npm cache.

Adds regression coverage for a global npm install whose bin entry is generated by an install script.

Tests:

  • cargo build -p deno --bin deno
  • cargo build -p test_server
  • cargo test --test specs global_lifecycle_scripts -- --nocapture
  • cargo test -p deno tools::installer::global::tests::install_with_allow_scripts_writes_config -- --nocapture

Closes #16164
Closes denoland/divybot#458

@littledivy littledivy force-pushed the orch/divybot-458 branch 6 times, most recently from f6798dd to 5d7fdc0 Compare June 5, 2026 11:36

@bartlomieju bartlomieju left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The core feature is well put together: the resolve_npm_version_info extraction is a clean refactor, the default is safe (non-tty or a "no" answer means scripts don't run), and the config-merge logic correctly updates an existing allowScripts or appends one. Two things to sort out before this can land, plus a couple of nits.

Scope (main blocker): this PR bundles unrelated WPT-runner changes -- tests/wpt/wpt.ts (+66/-12: stableExpectedFailures, isFlakyExpectation, the flaky-handling refactor) and tests/wpt/runner/expectations/workers.json, under a separate "test(wpt): stabilize worker expectations" commit. They look reasonable on their own, but they have nothing to do with npm lifecycle scripts and should go in their own PR per the focused-scope rule. Please split them out.

CI: no checks have reported yet -- needs to be green before merge.

See inline comments for the transitive-deps limitation and the test-coverage gap.

nits:

  • maybe_prompt_allow_lifecycle_scripts parses NpmPackageReqReference::from_specifier for the is_err() guard, then resolve_npm_lifecycle_scripts_package_req parses the same URL again with ?. You could pass the parsed ref through and skip the double parse.
  • VersionReq::parse_from_npm(&version_info.version.to_string()) round-trips through a string to build an exact-version req; works, just slightly roundabout.

|| version_info.scripts.contains_key("preinstall")
|| version_info.scripts.contains_key("install")
|| version_info.scripts.contains_key("postinstall");
if !has_lifecycle_scripts {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Detection only covers the top-level package: has_install_script/scripts reflect only the directly-installed package's own registry metadata, and allow_scripts is then set to Some(vec![that_one_package]). A package whose install scripts live in a transitive dependency (e.g. something depending on esbuild) won't trigger the prompt, and even on a "yes" answer those dep scripts wouldn't be in the allow-list. Is this an intentional first-step limitation? Either way, worth a code comment noting it, and possibly a follow-up issue.

Ok(())
}

async fn maybe_prompt_allow_lifecycle_scripts(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actual detection/prompt path here is untested. The spec test passes --allow-scripts=... explicitly, so it hits the !matches!(..., None) early return and never reaches resolve_npm_lifecycle_scripts_package_req or confirm; the unit test sets allow_scripts directly. So the feature's core detection logic has no coverage. A spec test that installs a script-package without --allow-scripts (the spec harness is non-tty, so confirm returns None) would exercise detection + the safe-default skip and let you assert the script did not run.

Comment thread tests/wpt/wpt.ts Outdated
return currentExpectation;
}

function stableExpectedFailures(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This WPT-runner refactor (and the workers.json flaky change) is unrelated to the npm lifecycle-scripts feature. Please move it to its own PR -- it's a fine change, just out of scope here, and keeping it separate keeps the review and the squash-merge history focused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for install scripts (ex. "postinstall") for "npm:" specifiers with global node modules

2 participants