fix: preserve integrity of remote tarball dependencies on re-resolution#12096
Conversation
📝 WalkthroughWalkthroughThe PR preserves a prior ChangesTarball integrity preservation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
Review Summary by QodoPreserve tarball dependency integrity on re-resolution
WalkthroughsDescription• Preserve integrity field when re-resolving remote tarball dependencies • Carry over integrity from previous lockfile entry when tarball URL unchanged • Prevents ERR_PNPM_MISSING_TARBALL_INTEGRITY on subsequent installs • Complements #12040 fix in package-requester layer for re-resolution path Diagramflowchart LR
A["Re-resolve tarball<br/>dependency"] --> B["toLockfileResolution<br/>creates new resolution"]
B --> C{"Tarball lost<br/>integrity?"}
C -->|Yes| D{"URL unchanged &<br/>prev integrity exists?"}
D -->|Yes| E["Carry over integrity<br/>from previous entry"]
D -->|No| F["Keep resolution<br/>as-is"]
C -->|No| F
E --> G["Lockfile entry<br/>with integrity preserved"]
F --> G
File Changes1. installing/deps-resolver/src/updateLockfile.ts
|
|
Reproduction: cd "$(mktemp -d)"
pnpm init
# blockExoticSubdeps would block node-xlsx's transitive
pnpm config set blockExoticSubdeps false --location project
# fresh store so the tarball is actually downloaded and its integrity recorded
store=$(mktemp -d)
pnpm --store-dir "$store" add node-xlsx
grep -A1 'cdn.sheetjs' pnpm-lock.yaml
# resolution: {integrity: sha512-…, tarball: …}
pnpm --store-dir "$store" update
grep -A1 'cdn.sheetjs' pnpm-lock.yaml
# resolution: {tarball: …}Just a note: This PR does not solve the scenario where the store is already warm. Neither This presents an issue when e.g. adding |
65f9e1a to
19a9680
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
installing/deps-resolver/test/updateLockfile.test.ts (1)
15-43: 💤 Low valueConsider moving helper functions below the tests.
The coding guideline prefers declaring functions after they are used, relying on hoisting, so that test logic appears first in the file. Moving
tarballGraphandlockfileWithbelow line 87 would follow this pattern.As per coding guidelines: "declaring functions after they are used (relying on hoisting)".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@installing/deps-resolver/test/updateLockfile.test.ts` around lines 15 - 43, Move the helper functions tarballGraph and lockfileWith so they appear after the tests (i.e., declared below where they are used) to follow the project's hoisting-first style; locate the definitions of tarballGraph and lockfileWith and cut/paste them beneath the test cases (keeping their signatures and returned types intact) so tests remain at the top and helpers are defined afterwards.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@installing/deps-resolver/test/updateLockfile.test.ts`:
- Around line 15-43: Move the helper functions tarballGraph and lockfileWith so
they appear after the tests (i.e., declared below where they are used) to follow
the project's hoisting-first style; locate the definitions of tarballGraph and
lockfileWith and cut/paste them beneath the test cases (keeping their signatures
and returned types intact) so tests remain at the top and helpers are defined
afterwards.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: b4fd4ad3-dea4-4db9-80c7-259bd7424e11
📒 Files selected for processing (1)
installing/deps-resolver/test/updateLockfile.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Analyze (javascript)
- GitHub Check: Compile & Lint
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Follow Standard Style with trailing commas, preferring functions over classes, and declaring functions after they are used (relying on hoisting)
Use a single options object instead of multiple parameters when a function needs more than two or three arguments
Follow Import Order: Standard libraries first, then external dependencies (alphabetically), then relative imports
Write self-documenting code where function names, parameters, and types explain what a function does without requiring prose comments
Do not write comments that restate what the code already says; refactor via renaming, splitting helpers, or restructuring instead
Do not repeat documentation at call sites that already exists in JSDoc on the callee; update JSDoc once for all call sites to benefit
Use JSDoc only for a function's contract (preconditions, postconditions, edge cases, why the function exists), not for re-narrating the body
Do not record past implementation shape, refactor history, or 'the previous code did X' framing in code; use git log and git blame instead
Write comments only when: the reason for code is non-obvious (hidden invariant, workaround for known bug, deliberate exception), or the right name doesn't fit (temporary technical constraint)
Files:
installing/deps-resolver/test/updateLockfile.test.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use util.types.isNativeError() instead of instanceof Error for error type checking in Jest tests
Files:
installing/deps-resolver/test/updateLockfile.test.ts
🧠 Learnings (1)
📚 Learning: 2026-05-14T09:04:00.133Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11622
File: resolving/npm-resolver/test/publishedBy.test.ts:350-354
Timestamp: 2026-05-14T09:04:00.133Z
Learning: In the pnpm/pnpm repository, ESLint is the authoritative style linter. Do not raise review findings for missing trailing commas in multiline function calls (e.g., `fs.writeFileSync(...)`) when this repo’s ESLint configuration does not report them and lint passes. Prefer deferring to the ESLint results for this specific trailing-comma rule rather than enforcing it manually in code review.
Applied to files:
installing/deps-resolver/test/updateLockfile.test.ts
🔇 Additional comments (2)
installing/deps-resolver/test/updateLockfile.test.ts (2)
1-8: LGTM!
45-87: LGTM!
Re-resolving a remote tarball dependency without re-fetching it (e.g. `pnpm update`) produced a resolution with no integrity, so the previously recorded integrity was dropped from the lockfile, breaking later installs with ERR_PNPM_MISSING_TARBALL_INTEGRITY. Carry the integrity over from the previous lockfile entry when the rebuilt tarball resolution lost it and the URL is unchanged. This complements pnpm#12040, which fixes the same class of bug in the package-requester layer but does not cover this re-resolution path. Closes pnpm#12067.
…of re-downloading
a17f43c to
1126480
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #12096 +/- ##
==========================================
+ Coverage 87.04% 87.05% +0.01%
==========================================
Files 252 252
Lines 28146 28196 +50
==========================================
+ Hits 24499 24546 +47
- Misses 3647 3650 +3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Congrats on merging your first pull request! 🎉🎉🎉 |
During a non-frozen install, reuse the prior pnpm-lock.yaml's resolution and transitive subtree for dependencies still satisfied and not being updated, instead of re-resolving everything from manifests. Faithful port of pnpm's getInfoFromLockfile / resolvedDependencies / parentPkg.updated machinery, adapted to pacquet's resolver as a hybrid resolve: snapshot-walk unchanged subtrees, fresh-resolve new/changed/update-targeted deps, with an `updated` flag propagated down. Semver-satisfies reuse gate (pnpm parity). Conservative by construction — registry resolutions only, gated on the whole subtree being synthesizable; `pacquet update` targets, packageExtensions drift, and dependency cycles all fall through to a fresh resolve. Follow-up to #12096 (which covered only remote tarballs). Lockfile canonical-ordering tracked separately as #12117.
What
Re-resolving a remote (non-registry) tarball dependency — e.g. running
pnpm update, or installing/changing an unrelated dependency — dropped theintegrityfield from its lockfile entry. URL/tarball resolvers don't return an integrity (it's only known after the tarball is downloaded), so when the entry is rebuilt without a re-fetch the freshly resolved resolution has none, and the previously recorded integrity was lost. Later installs then fail withERR_PNPM_MISSING_TARBALL_INTEGRITY.How
toLockfileDependencynow carries the integrity over from the previous lockfile entry when the rebuilt resolution is a tarball that lost its integrity and the tarball URL is unchanged. It's guarded so it never overwrites an integrity that's already present, and never attaches a stale hash to a different URL.Relationship to #12040
This is a follow-up to #12040 ("preserve tarball dependency integrity in the lockfile", which closed #12001). That fix addresses the same root cause but in the package-requester layer — it carries the integrity over from
currentPkgduring resolve, guarded to tarball resolutions with an unchanged id. That carryover doesn't fire on the re-resolution path in #12067 (pnpm update): the resolution reaches the lockfile builder without an integrity, so the entry is still dropped. This PR adds the equivalent carryover one layer down, when the lockfile entry is rebuilt — complementing #12040 rather than replacing it.Closes #12067. Related: #12040, #12001.
pacquet parity
pacquet cannot lose tarball integrity the way pnpm did: it builds the lockfile before the install pass, so its
TarballResolverlearns the integrity directly from the tarball's bytes on every resolution and never drops it. The existing integration test (tarball_url_dependency.rs::remote_tarball_integrity_survives_unrelated_install) is extended to cite this issue and document that immunity — noupdateLockfile-style carryover is needed on the Rust side.Porting it surfaced a performance gap (not a correctness one): pnpm reuses a warm store on re-resolution and skips the fetch, whereas pacquet re-downloaded the tarball every time it re-resolved (e.g.
pacquet update). This PR closes that gap too — the resolver now reuses the prior lockfile's recorded integrity + the store-extracted manifest on a warm hit, and downloads only on a miss (safe by construction: the store key embeds the integrity, so a miss just falls back to a download). A discriminating test (remote_tarball_reresolves_from_warm_store_without_refetch) serves the tarball from a throwaway server, tears it down, then runspacquet update— which succeeds only because the warm store is reused. The test proves the refetch is skipped; it doesn't quantify the speedup (a vlt.sh benchmark would).Written by an agent (Claude Code, claude-opus-4-8).
Summary by CodeRabbit