fix(global): strip Windows \\?\\ verbatim prefix from canonicalized install dir#243
fix(global): strip Windows \\?\\ verbatim prefix from canonicalized install dir#243
Conversation
…stall dir
`run_global` calls `std::fs::canonicalize` on the freshly-created install
directory and on `layout.pkg_dir`. On Windows those calls return the
extended-length form `\\?\C:\…`. Every downstream step that concatenates
this dir into another path then carries the verbatim prefix forward, and
the bin-shim writer in particular ends up baking it into the relative
target string it interpolates into `%~dp0\{rel}`. The resulting `.cmd`
points at `<bin_dir>\\\\?\\C:\…\prettier.cjs`, which `cmd.exe` collapses
to `<bin_dir>\?\C:\…\prettier.cjs`, which Node then refuses with:
Cannot find module 'C:\…\bin\?\C:\…\node_modules\prettier\bin\prettier.cjs'
There is already an inline fix for the same class of bug for the per-dep
bin dir at `aube::commands::install::link_bin_via_dep`. This PR generalizes
it into a reusable `dirs::canonicalize` helper (matches `dunce::canonicalize`
without adding the dep) and routes the three `run_global` call sites
through it so the install dir, the layout pkg dir, and the symmetric
post-install equality checks all see the same plain drive-letter form.
Reproduced via mise's `windows-e2e` job:
https://github.com/jdx/mise/actions/runs/24852804095 (the
`mise install npm:[email protected] -- prettier --version` step).
Validation:
- `cargo test --bin aube` — 281 pass (1 new, `canonicalize_round_trips_an_existing_path`;
also a `#[cfg(windows)]` `canonicalize_strips_verbatim_drive_prefix`)
- `cargo fmt --check`
- `cargo clippy --all-targets`
Signed-off-by: jdx <[email protected]>
…oml" Re-add aube to mise.toml and route the workflow back through `mise install aube` + `mise x aube --`. The Windows-e2e regression that motivated the previous commit was actually a bug in aube's global-install bin shim on Windows (canonicalize returning the `\\?\` verbatim prefix); fixed upstream in endevco/aube#243. This PR depends on that landing in an aube release. This reverts commit 1c3bca3.
Greptile SummaryIntroduces Both previously flagged findings are resolved: Confidence Score: 5/5Safe to merge — all prior P1/P2 findings are addressed and no new issues were found. Both previously raised concerns (stale hash pointers from verbatim-prefixed No files require special attention.
|
| Filename | Overview |
|---|---|
| crates/aube/src/dirs.rs | New canonicalize helper: calls std::fs::canonicalize, then on Windows strips \\?\ prefix unless it's a real \\?\UNC\ share path. Well-documented, cross-platform (no-op on Unix), includes unit tests for both the round-trip case and the Windows-only prefix-stripping assertion. |
| crates/aube/src/commands/add.rs | Routes all four run_global / run_global_inner canonicalization call sites through crate::dirs::canonicalize so install_dir, layout.pkg_dir, the error-cleanup equality check, and the prior-detection check all use consistent non-verbatim paths on Windows. |
| crates/aube/src/commands/global.rs | Both scan_packages and remove_package now use crate::dirs::canonicalize; addresses the prior P1 (stale-hash-pointer accumulation because scan_packages was still returning verbatim \\?\ paths) and ensures starts_with in remove_package stays consistent. |
| crates/aube/src/commands/install/bin_linking.rs | Replaces the inline verbatim-strip (missing UNC guard) with crate::dirs::canonicalize, addressing the prior P2 and eliminating the code duplication in one step. |
Reviews (2): Last reviewed commit: "fix(global): route scan_packages and bin..." | Re-trigger Greptile
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 27347e6. Configure here.
… dirs::canonicalize Two follow-on fixes to the same Windows verbatim-prefix class of bug as the parent commit: - `global::scan_packages` (and via it, `find_package`) populated `GlobalPackageInfo.install_dir` from `std::fs::canonicalize`, so the field still carried `\\?\C:\…`. The new `run_global_inner` prior-cleanup code compares those paths against `install_dir` (now stripped via `dirs::canonicalize`) at `add.rs:1064-1077`. Without this fix the comparisons never match on Windows and stale install dirs leak every reinstall of the same alias set. `remove_package` had the same shape — `pkg_canon` was verbatim, but `info.install_dir` is now stripped, so the `starts_with` guard would fail and the install dir would never be removed. - `commands::install::bin_linking` carried an inline `s.strip_prefix(r"\\?\")` that the parent commit's helper was extracted from. The inline version unconditionally strips the prefix, including from real `\\?\UNC\server\share` paths that have no non-verbatim equivalent. Replacing it with `dirs::canonicalize` (which preserves UNC share paths) keeps the behavior identical for the only path it ever sees in practice today (drive-letter junctions) and removes the only divergent copy of this logic. Greptile flagged both during review of #243 (P1 + P2). Validation: - `cargo test --bin aube` — 281 pass - `cargo fmt --check`, `cargo clippy --all-targets` Signed-off-by: jdx <[email protected]>
…oml" Re-add aube to mise.toml and route the workflow back through `mise install aube` + `mise x aube --`. The Windows-e2e regression that motivated the previous commit was actually a bug in aube's global-install bin shim on Windows (canonicalize returning the `\\?\` verbatim prefix); fixed upstream in endevco/aube#243. This PR depends on that landing in an aube release. This reverts commit 1c3bca3.

Summary
run_globalcallsstd::fs::canonicalizeon the freshly-created install directory and onlayout.pkg_dir. On Windows those calls return the extended-length form\\?\C:\…. Every downstream step that concatenates this dir into another path then carries the verbatim prefix forward, and the bin-shim writer in particular ends up baking it into the relative target string it interpolates into%~dp0\{rel}.The resulting
.cmdpoints at<bin_dir>\\\\?\\C:\…\prettier.cjs, whichcmd.execollapses to<bin_dir>\?\C:\…\prettier.cjs, which Node then refuses with:There is already an inline fix for the same class of bug for the per-dep bin dir at
aube::commands::install::link_bin_via_dep. This PR generalizes it into a reusabledirs::canonicalizehelper (matchesdunce::canonicalizewithout adding the dep) and routes the threerun_globalcall sites through it so the install dir, the layout pkg dir, and the symmetric post-install equality checks all see the same plain drive-letter form.Approach
aube::dirs::canonicalize(incrates/aube/src/dirs.rs) — runsstd::fs::canonicalize, then strips the\\?\prefix on Windows when the result wasn't a real\\?\UNC\…share path. No-op on Unix.crates/aube/src/commands/add.rs::run_globalswitch fromstd::fs::canonicalize→crate::dirs::canonicalize. Two of them are equality checks againstinstall_dir; ifinstall_diris now stripped, the other side has to be stripped the same way or the comparisons silently regress.Reproduced via
mise's
windows-e2etranche, after switching mise's npm-publish to aube and triggeringmise install npm:[email protected]. The CI run that surfaced it: https://github.com/jdx/mise/actions/runs/24852804095.Test plan
cargo test --bin aube— 281 pass (1 new:canonicalize_round_trips_an_existing_path; plus a#[cfg(windows)]guard testcanonicalize_strips_verbatim_drive_prefix)cargo fmt --checkcargo clippy --all-targets🤖 Generated with Claude Code
Note
Medium Risk
Touches global install/bin-linking path canonicalization and cleanup logic; mainly Windows-specific but could affect how paths compare and which directories get cleaned up.
Overview
Fixes Windows global installs by routing canonicalization through a new
crate::dirs::canonicalizehelper that strips the\\?\verbatim-drive prefix (while preserving real\\?\UNC\…shares).add -gand global package scanning/removal now use this helper so path comparisons (==/starts_with) and bin-shim target construction don’t break on Windows, preventing broken.cmdshims and leaked/stale global install dirs/hash pointers. Bin-linking’s Windows mkdir workaround is simplified to rely on the same helper, and new unit tests cover the canonicalize behavior.Reviewed by Cursor Bugbot for commit 6018f5e. Bugbot is set up for automated code reviews on this repo. Configure here.