Repository navigation
Fix Deno --global-prefix cache and tool roots (#444) - #1741
Merged
Mikola Lysenko (mikolalysenko) merged 4 commits intoOct 27, 2026
Merged
Conversation
Assisted-by: Claude Code:claude-opus-5-5
--global-prefix naming $DENO_DIR or $DENO_DIR/npm found nothing: only the registry dir itself worked. It now expands to every registry dir below it, the same ones -g crawls. On Deno 2.7+, a --global-prefix naming a tool's per-tool bin/.<tool>/node_modules that Deno never loads (its deno.json sets no nodeModulesDir) reported applied and a VEX not_affected while the tool kept running the unpatched cache copy. That prefix is now a usage error (exit 2) pointing at -g or $DENO_DIR. Trees Deno does load (npm: entrypoint tools, nodeModulesDir auto/manual, or a package.json) are still accepted. Fixes #444 Assisted-by: Claude Code:claude-opus-5-5
The crawler architecture test requires project files to be read through the FIFO-safe reader, so a named pipe at deno.json can't hang a scan. Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko)
marked this pull request as ready for review
October 27, 2026 13:33
Collaborator
Author
|
BugBot review Generated by Claude Code |
Review fixes for the #444 follow-up: - rollback, remove, vendor --revert and repair still accept a Deno tool tree, so files an earlier release patched there can be restored. - An explicit nodeModulesDir "none" or false counts even beside a package.json; the config is read as JSONC (comments, trailing commas, BOM), as Deno reads it. - A prefix inside the decoy, or a symlink to it, is refused too. - A dir named npm that holds a node_modules (npm's global prefix on Windows, %APPDATA%\npm) keeps its plain walk instead of being read as Deno's cache, and a relative prefix such as "." is resolved first. Assisted-by: Claude Code:claude-opus-5-5
Collaborator
Author
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit a197537. Configure here.
Tanmay Singla (Tanmay182003)
approved these changes
Oct 27, 2026
Collaborator
Author
|
Ready for review at
Generated by Claude Code |
Collaborator
Author
|
Ready for review at
Generated by Claude Code |
Mikola Lysenko (mikolalysenko)
deleted the
agent/fix-deno-global-prefix-roots
branch
October 27, 2026 14:58
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
LLM Description written by Claude Code:claude-opus-5-5
Fixes #444
#1719 taught global mode to crawl Deno's npm cache and left two parts of #444 open. This PR closes both.
What changes for users
--global-prefix $DENO_DIRand--global-prefix $DENO_DIR/npmnow work. Before, they found nothing (package_not_installed); only the registry dir$DENO_DIR/npm/registry.npmjs.orgdid. They now expand to every registry dir below them, the same ones-gcrawls (jsr.iostays out).bin/.<tool>/node_modulesthat Deno never loads is refused. On Deno 2.7 and later, a tool installed from a local or JSR entrypoint gets that tree, but itsdeno.jsonsets nonodeModulesDir, so Deno loads the$DENO_DIR/npmcopy. Pointing--global-prefixat it used to reportappliedand a VEXnot_affectedwhile the tool kept running the unpatched code. It's now a usage error (exit 2) like a nonexistent prefix, and the message points at-g/$DENO_DIR. A prefix inside the decoy, or a symlink to it, is refused too.rollback,remove,vendor --revertandrepairstill accept it, so files an earlier release patched there can be restored. Trees Deno does load are still accepted: annpm:entrypoint tool ("nodeModulesDir": "manual"+package.json),auto, or any tree with a siblingpackage.json.Root cause
NpmCrawler::node_modules_paths_syncread a--global-prefixas either a Deno registry dir or anode_modules-shaped dir. It never expanded a Deno cache parent into its registry dirs, and nothing checked whether Deno would load a per-toolnode_modules.Implementation
npm_crawler.rs: split the-gwalk intodeno_npm_registry_dirs(npm), and adddeno_cache_parent_registry_dirs(prefix). It only fires for a prefix namednpm, or one with annpmchild, that is not anode_modulesand has nopackage.json. So a globalnode_modulesholding the npm CLI keeps its old walk (regression test included).deno_crawler.rs:is_unloaded_deno_tool_node_modules(path)matches only<..>/bin/.<tool>/node_moduleswith a plain-JSONdeno.json/deno.jsoncwhosenodeModulesDiris unset,"none"orfalse, and nopackage.json. A config that isn't plain JSON (JSONC comments) is not judged, so it fails open to the old behavior rather than refusing a live tree.args.rs:GlobalArgs::validate_pathsrefuses that prefix next to the existing "does not exist" check, so every command (scan/apply/rollback/vex/vendor/...) refuses it the same way.No wrapper (
npm/,pypi/,gem/) changes are needed: they only dispatch to the binary.Tests (red → green)
$DENO_DIR/$DENO_DIR/npmprefixcrawler_npm_e2e::deno_dir_and_npm_dir_prefixes_expand_to_registry_dirsnpmpackage child keeps the walkcrawler_npm_e2e::prefix_with_npm_package_child_keeps_node_modules_walkcli_path_flags_validated::deno_tool_node_modules_deno_never_loads_is_a_usage_errorcli_path_flags_validated::deno_tool_node_modules_deno_loads_is_accepteddeno_crawler::tests::unloaded_deno_tool_node_modules_matches_only_the_decoyLocal:
cargo fmt --all -- --checkOK,cargo clippy --workspace --all-features -- -D warningsOK,crawler_npm_e2e118/118,cli_path_flags_validated28/28, coredenounit tests 39/39. Fullcargo test --workspace --all-features --no-fail-fast: 236 binaries ok. It caught one real issue, fixed in c786cc7: the crawler architecture test requires FIFO-safe reads, sodeno.jsonis now read withread_regular_to_string_sync. The remaining 12 failures are sandbox-only and outside this diff: the sandbox runs as root, so chmod-based write-failure tests can't fail (in_process_redirectwrite failures,vlt_heal,copy_treesymlinked root,pypi_poetry/pypi_requirementswire failures,repair_*unremovable lock), ande2e_bun_lockbneeds patches-api.socket.dev, which the sandbox proxy blocks. CI runs them unprivileged and online./code-review high(run on the diff; fixes in a197537)nodeModulesDir: "none"/falsebeside apackage.jsonnow counts; the config is read as JSONC (comments, trailing comma, BOM) viastrip_jsonc/strip_bom; nested paths inside the decoy and symlinks to it are caught (canonicalized + ancestor walk); restoring commands are exempt; a relative prefix (.) is made absolute; annpmdir holdingnode_modules(npm's Windows global prefix%APPDATA%\npm) is not read as Deno's cache; the error message now says "Deno config leaves nodeModulesDir unset or none". Each has a test.validate_pathsis where every other bad--global-prefixis already refused.Generated by Claude Code