fix(dlx): shorten dlx cache path to avoid Windows MAX_PATH failures#12605
Conversation
The dlx cache path is `<cacheDir>/dlx/<key>/<prepare>/node_modules/.pnpm/ <pkgId>/node_modules/<pkg>`. The `<key>` (a 64-char sha256 hex) and `<prepare>` (`<time>-<pid>` in hex) segments are dlx overhead on top of pnpm's already-deep virtual-store layout. For a transitive dep with a long name this tips the package directory over Windows' MAX_PATH (260) — measured at 259 chars for `@pnpm.e2e/pre-and-postinstall-scripts-example` in the dlx e2e test. A lifecycle script then runs with that directory as its cwd, and CreateProcess fails to resolve an over-length cwd, which Node surfaces as the confusing `spawn C:\Windows\system32\cmd.exe ENOENT`. It was flaky rather than constant because the `<prepare>` and temp-dir segments vary in length run to run, straddling the 260 boundary. Shorten both dlx-specific segments: - cache key: use createShortHash (32 hex chars, 128 bits) instead of the full createHexHash (64). pacquet already truncated to 32 via create_short_hash, so this also brings the two stacks back in parity. - prepare dir: encode time and pid in base36 instead of hex. This keeps the package directory comfortably under MAX_PATH for realistic package names, not just the test.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe PR shortens Changesdlx cache path shortening
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Summary by QodoShorten pnpm dlx cache paths to avoid Windows MAX_PATH spawn failures Description
Diagram
High-Level Assessment
Files changed (5)
|
Code Review by Qodo
1.
|
Micro-Benchmark ResultsLinux |
|
Code review by qodo was updated up to the latest commit 6d035b5 |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #12605 +/- ##
==========================================
- Coverage 88.15% 88.13% -0.03%
==========================================
Files 326 329 +3
Lines 46556 47045 +489
==========================================
+ Hits 41043 41461 +418
- Misses 5513 5584 +71 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Integrated-Benchmark Report (Linux)Each scenario reports direct installs and pnpr installs. Bencher consumes pacquet@HEAD and pnpr@HEAD. Scenario: Isolated linker: fresh restore, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 4.762650982320001,
"stddev": 0.16376580814532576,
"median": 4.71740543412,
"user": 3.8535771199999997,
"system": 3.4855510599999997,
"min": 4.587513249120001,
"max": 4.9939351191200005,
"times": [
4.94540030012,
4.8886310251200005,
4.9939351191200005,
4.93932697412,
4.75449385712,
4.587513249120001,
4.598439642120001,
4.6803170111200005,
4.58945352012,
4.6489991251200005
]
},
{
"command": "pacquet@main",
"mean": 4.74757047402,
"stddev": 0.16412195864157894,
"median": 4.697598257120001,
"user": 3.8494857200000006,
"system": 3.47487326,
"min": 4.58186413812,
"max": 5.03587706012,
"times": [
4.943364386120001,
4.909829090120001,
4.58826319012,
4.58186413812,
4.597011305120001,
4.63661793612,
4.78768112012,
5.03587706012,
4.687083616120001,
4.7081128981200004
]
},
{
"command": "pnpr@HEAD",
"mean": 2.8763414830200005,
"stddev": 0.11853029691424062,
"median": 2.8420608651200006,
"user": 2.70594372,
"system": 3.0220651600000004,
"min": 2.74116287612,
"max": 3.08782421812,
"times": [
2.80545467712,
2.8521790421200004,
2.8098339401200003,
3.0852277791200002,
2.7926783321200004,
2.74116287612,
2.8319426881200003,
2.8985631001200005,
2.8585481771200003,
3.08782421812
]
},
{
"command": "pnpr@main",
"mean": 2.8709170147200003,
"stddev": 0.09854207661542384,
"median": 2.8505564316200003,
"user": 2.68687342,
"system": 3.02689176,
"min": 2.7556562651200003,
"max": 3.03366605812,
"times": [
3.03366605812,
2.7928578991200004,
2.85934514812,
2.8005082321200003,
2.89761281712,
2.96417276412,
2.76790012012,
2.8417677151200005,
2.9956831281200005,
2.7556562651200003
]
}
]
}Scenario: Isolated linker: fresh restore, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.62744251726,
"stddev": 0.01758221967086557,
"median": 0.6314405752600001,
"user": 0.36718579999999995,
"system": 1.3207751399999998,
"min": 0.60580418126,
"max": 0.6529372012600001,
"times": [
0.64184394026,
0.6529372012600001,
0.6058990602600001,
0.6083381612600001,
0.6374828652600001,
0.6253982852600001,
0.6383459732600001,
0.6150889742600001,
0.60580418126,
0.64328653026
]
},
{
"command": "pacquet@main",
"mean": 0.62721179636,
"stddev": 0.006604220255775496,
"median": 0.6281929687600001,
"user": 0.3762709,
"system": 1.3201397400000001,
"min": 0.6168434582600001,
"max": 0.63651335626,
"times": [
0.6168434582600001,
0.62535015126,
0.6331384802600001,
0.6286540812600001,
0.6319005672600001,
0.63651335626,
0.62773185626,
0.61769490226,
0.62240142726,
0.6318896832600001
]
},
{
"command": "pnpr@HEAD",
"mean": 0.7163789955600002,
"stddev": 0.10184192602034102,
"median": 0.6846512092600001,
"user": 0.38983729999999994,
"system": 1.3570096399999998,
"min": 0.6549659412600001,
"max": 1.0003352482599999,
"times": [
0.6680092332600001,
0.66875502326,
0.6549659412600001,
0.6844077502600001,
0.6848946682600001,
0.6740625032600001,
0.6996003582600001,
0.7033800442600001,
1.0003352482599999,
0.7253791852600001
]
},
{
"command": "pnpr@main",
"mean": 0.7370623001600002,
"stddev": 0.07946595799829897,
"median": 0.71217460026,
"user": 0.40081950000000005,
"system": 1.36941144,
"min": 0.69697829826,
"max": 0.9599737782600001,
"times": [
0.73402409826,
0.7020088252600001,
0.69858876526,
0.7208147882600001,
0.7226705002600001,
0.70353441226,
0.9599737782600001,
0.72901246626,
0.69697829826,
0.7030170692600001
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 4.80697255416,
"stddev": 0.07639174372241217,
"median": 4.82757268666,
"user": 3.80469108,
"system": 3.44058524,
"min": 4.68052484716,
"max": 4.89592437216,
"times": [
4.74370799916,
4.68052484716,
4.69250113516,
4.8407924821599995,
4.87706027216,
4.89592437216,
4.85022048616,
4.80824476016,
4.86639629616,
4.8143528911599995
]
},
{
"command": "pacquet@main",
"mean": 4.84246465056,
"stddev": 0.041729725050824944,
"median": 4.852279194159999,
"user": 3.8425885799999997,
"system": 3.4453095400000002,
"min": 4.77302882416,
"max": 4.9103849431599995,
"times": [
4.8626727251599995,
4.81063687916,
4.77302882416,
4.84322284116,
4.864543181159999,
4.9103849431599995,
4.88246596816,
4.8613355471599995,
4.80493533616,
4.811420260159999
]
},
{
"command": "pnpr@HEAD",
"mean": 3.1743707054600003,
"stddev": 0.0564116052281647,
"median": 3.1693977116600003,
"user": 2.76313588,
"system": 3.1477591400000002,
"min": 3.10805634416,
"max": 3.28792631016,
"times": [
3.17367397716,
3.2048096211600003,
3.17857682516,
3.23599534416,
3.14633761716,
3.28792631016,
3.16512144616,
3.11250080816,
3.13070876116,
3.10805634416
]
},
{
"command": "pnpr@main",
"mean": 3.18344877986,
"stddev": 0.1141784185012634,
"median": 3.16612829566,
"user": 2.77242868,
"system": 3.1497136399999994,
"min": 3.02475484216,
"max": 3.4306517971600003,
"times": [
3.24775456416,
3.11240247616,
3.02475484216,
3.19805838516,
3.4306517971600003,
3.1799540731600002,
3.1523025181600004,
3.27612255416,
3.1148730921600003,
3.09761349616
]
}
]
}Scenario: Isolated linker: fresh install, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 1.36324213766,
"stddev": 0.008608602974314185,
"median": 1.36313589296,
"user": 1.3459069599999998,
"system": 1.6999346799999997,
"min": 1.34938473446,
"max": 1.37562550546,
"times": [
1.35720330446,
1.35500098246,
1.3574383434600001,
1.37091381346,
1.37240780546,
1.36817510146,
1.36028036946,
1.37562550546,
1.36599141646,
1.34938473446
]
},
{
"command": "pacquet@main",
"mean": 1.35520702806,
"stddev": 0.024172138488727794,
"median": 1.3493556774600002,
"user": 1.3324311599999998,
"system": 1.69439538,
"min": 1.33529152546,
"max": 1.41902187146,
"times": [
1.35845762346,
1.41902187146,
1.33774868046,
1.34284938546,
1.34360667646,
1.33529152546,
1.3652626514600001,
1.3499629184600002,
1.35112051146,
1.34874843646
]
},
{
"command": "pnpr@HEAD",
"mean": 1.43298634186,
"stddev": 0.020190174897126996,
"median": 1.4298512739600002,
"user": 0.5897201599999999,
"system": 1.59261478,
"min": 1.40875568046,
"max": 1.47362658646,
"times": [
1.45913165046,
1.4164084564600001,
1.47362658646,
1.43607234046,
1.40875568046,
1.4371300474600002,
1.42130092846,
1.4245535014600001,
1.41773518046,
1.43514904646
]
},
{
"command": "pnpr@main",
"mean": 1.45802681706,
"stddev": 0.05986159149546809,
"median": 1.43932987696,
"user": 0.5862163600000001,
"system": 1.56574088,
"min": 1.42464610146,
"max": 1.62290858746,
"times": [
1.42549998146,
1.46873793246,
1.62290858746,
1.4270207674600002,
1.45946895146,
1.44135147246,
1.44651436246,
1.42681173246,
1.43730828146,
1.42464610146
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 3.085984677,
"stddev": 0.01363895141559595,
"median": 3.0873756971999997,
"user": 1.8121735600000002,
"system": 2.0125989199999994,
"min": 3.0676485387,
"max": 3.1030131727,
"times": [
3.0803160026999996,
3.0919457017,
3.0700379866999996,
3.1030131727,
3.1026412707,
3.0931560926999997,
3.0676485387,
3.0828056926999996,
3.0979300337,
3.0703522776999996
]
},
{
"command": "pacquet@main",
"mean": 3.1006358134,
"stddev": 0.07726993910203878,
"median": 3.0629010932,
"user": 1.83490776,
"system": 1.98734942,
"min": 3.0334618997,
"max": 3.2476012546999997,
"times": [
3.0334618997,
3.0465363176999998,
3.1190338116999996,
3.2476012546999997,
3.1154914967,
3.0499292707,
3.0451698207,
3.0492047277,
3.2240566186999997,
3.0758729156999998
]
},
{
"command": "pnpr@HEAD",
"mean": 1.4512084001,
"stddev": 0.016665224102967433,
"median": 1.4542618507,
"user": 0.59121536,
"system": 1.6222485199999999,
"min": 1.4222778297,
"max": 1.4831905517000001,
"times": [
1.4604653487,
1.4557906737,
1.4222778297,
1.4462966107,
1.4563215067,
1.4340601257,
1.4408484827,
1.4600998437000001,
1.4527330277000001,
1.4831905517000001
]
},
{
"command": "pnpr@main",
"mean": 1.4381860873,
"stddev": 0.028316341307965984,
"median": 1.4419522607,
"user": 0.5890966599999999,
"system": 1.5595908200000002,
"min": 1.4037854927,
"max": 1.4969539047,
"times": [
1.4427924697,
1.4425819507,
1.4969539047,
1.4437125017,
1.4072432977,
1.4037854927,
1.4660479067,
1.4413225707,
1.4169761177,
1.4204446607
]
}
]
} |
|
| Branch | pr/12605 |
| Testbed | pacquet |
Click to view all benchmark results
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| isolated-linker.fresh-install.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 4,806.97 ms(+7.55%)Baseline: 4,469.54 ms | 5,363.45 ms (89.62%) |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot 🚷 view threshold | 3,085.98 ms(+1.91%)Baseline: 3,028.16 ms | 3,633.79 ms (84.92%) |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 1,363.24 ms(+1.92%)Baseline: 1,337.53 ms | 1,605.04 ms (84.94%) |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 4,762.65 ms(+14.64%)Baseline: 4,154.59 ms | 4,985.50 ms (95.53%) |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 627.44 ms(+0.68%)Baseline: 623.22 ms | 747.87 ms (83.90%) |
|
| Branch | pr/12605 |
| Testbed | pnpr |
⚠️ WARNING: No Threshold found!Without a Threshold, no Alerts will ever be generated.
Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the--ci-only-thresholdsflag.
Click to view all benchmark results
| Benchmark | Latency | milliseconds (ms) |
|---|---|---|
| isolated-linker.fresh-install.cold-cache.cold-store | 📈 view plot | 3,174.37 ms |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot | 1,451.21 ms |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot | 1,432.99 ms |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot | 2,876.34 ms |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot | 716.38 ms |
getPrepareDir now encodes time and pid in base36, so the dlx cache e2e helper must accept letters g-z. The previous hex check (with buggy && logic) could throw on a valid base36 name.
|
Code review by qodo was updated up to the latest commit f19adba |
1 similar comment
|
Code review by qodo was updated up to the latest commit f19adba |
Summary
Follow-up to the dlx cleanup fix (#12575). With the error-masking gone, the Windows CI flake on
dlx prompts to approve ignored builds when invoked with a commands mapnow shows its true cause:spawn <full-path-to-cmd.exe> ENOENT— wherecmd.execlearly exists — is the classic Windows signature of the child's cwd not resolving. The lifecycle script runs withcwd = <pkgRoot>, and for that transitive dep the directory is 259 chars, right at Windows'MAX_PATH(260):CreateProcesscan't resolve an over-lengthlpCurrentDirectory, and Node surfaces it as the misleadingspawn cmd.exe ENOENT. It's flaky rather than constant because the<prepare>segment (<time>-<pid>in hex) and the temp-dir segment vary in length run to run, straddling 260. It only hits this test because most deps have their builds skipped (no script spawned in the deep dir); this is the one that triggers the approve-builds rebuild.The two dlx-specific path segments are pure overhead on top of pnpm's already-deep virtual store, so this shortens both:
createHexHash(64 hex chars) →createShortHash(32 chars / 128 bits, ample for a cache key). pacquet already truncated to 32 viacreate_short_hash, so this also restores parity between the two stacks.time/pidin base36 instead of hex.This takes the measured directory from 259 → ~225 chars, comfortably under
MAX_PATHfor realistic package names — not just the test.Squash Commit Body
Checklist
pacquet/port, or the description notes what still needs porting.pnpm changeset) if this PR changes any publishedpackage. Keep it short and written for pnpm users — it becomes a release note.
Written by an agent (Claude Code, claude-opus-4-8).
Summary by CodeRabbit
Bug Fixes
pnpm dlxon Windows failing due to overly long cache/lifecycle working paths hittingMAX_PATH, causing errors like failedcmd.exespawns.dlxcache key and temporary prepare-directory naming to use a shorter base36 encoding, reducing path length while keeping uniqueness to avoid collisions.