fix: preserve tarball dependency integrity in the lockfile#12040
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📜 Recent 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). (1)
🧰 Additional context used📓 Path-based instructions (2)pacquet/**/*.rs📄 CodeRabbit inference engine (pacquet/AGENTS.md)
Files:
pacquet/**/{tests,test}/*.rs📄 CodeRabbit inference engine (pacquet/AGENTS.md)
Files:
🧠 Learnings (3)📚 Learning: 2026-05-20T19:40:55.051ZApplied to files:
📚 Learning: 2026-05-22T00:08:44.646ZApplied to files:
📚 Learning: 2026-05-20T23:07:58.444ZApplied to files:
🔇 Additional comments (1)
📝 WalkthroughWalkthroughPreserves tarball resolution ChangesTarball Integrity Preservation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 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)
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 |
Review Summary by QodoPreserve tarball dependency integrity in lockfile
WalkthroughsDescription• Preserve tarball integrity from lockfile when resolver returns none • URL/tarball resolvers lack integrity until download completion • Prevents integrity loss during unrelated package installations • Fixes frozen-lockfile failures with ERR_PNPM_MISSING_TARBALL_INTEGRITY Diagramflowchart LR
A["Tarball Resolver<br/>no integrity"] -->|resolveAndFetch| B["Check if Updated"]
B -->|not updated| C["Carry Over<br/>Previous Integrity"]
B -->|updated| D["Use New<br/>Resolution"]
C --> E["Lockfile Entry<br/>with Integrity"]
D --> E
File Changes1. installing/package-requester/src/packageRequester.ts
|
There was a problem hiding this comment.
Pull request overview
Fixes #12001 where the integrity field of a remote https-tarball dependency was dropped from the lockfile when an unrelated package was installed later, because URL/tarball resolvers don't return integrity and the freshly-resolved resolution overwrote the lockfile one without re-fetching.
Changes:
- In
resolveAndFetch, when the resolver returns no integrity and the package was not updated, carry over the previous integrity from the current (lockfile) resolution onto the new tarball resolution. - Adds a unit test driving the buggy path with a custom resolver returning no integrity and a
currentPkgthat has one. - Adds a changeset documenting the patch.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| installing/package-requester/src/packageRequester.ts | Preserves tarball integrity from current resolution when resolver returns none and package is not updated. |
| installing/package-requester/test/index.ts | New test asserting integrity is preserved for tarball dependencies when the resolver returns none. |
| .changeset/fix-tarball-integrity-dropped.md | Patch changeset describing the fix. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Actionable comments posted: 0 |
URL/tarball resolvers do not return an integrity (it is only known after the tarball is downloaded). When a remote-tarball dependency was reused from the lockfile without being re-fetched, the freshly resolved resolution had no integrity and the existing one was dropped, breaking subsequent --frozen-lockfile installs under the lockfile-integrity hardening (ERR_PNPM_MISSING_TARBALL_INTEGRITY). Carry the integrity over from the current resolution instead. Closes pnpm#12001 Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
15f722b to
8920c44
Compare
Align the integrity carryover added in the previous commit with its sibling block in the download path: use `!resolution.type` (the idiom already used there) and drop the `newIntegrity == null` clause, which is redundant once `resolution` is the freshly resolved resolution. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…12001) pnpm pnpm#12040 fixes dropping a remote-tarball dependency's integrity when an unrelated package is installed afterwards. Pacquet can't reach that scenario yet: a non-registry https-tarball direct dependency hits the TarballResolver, which returns no name_ver/integrity, so lockfile build panics with MissingSuffix. Add the regression test for the target behavior, gated with allow_known_failure! until external tarball deps install. Tracked in pnpm#12053. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #12040 +/- ##
==========================================
+ Coverage 88.94% 88.95% +0.01%
==========================================
Files 235 235
Lines 30839 30930 +91
==========================================
+ Hits 27429 27514 +85
- Misses 3410 3416 +6 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Integrated-Benchmark Report (Linux)Scenario: Isolated linker: fresh restore, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 2.9031439584400003,
"stddev": 0.47314495315963584,
"median": 2.79288601574,
"user": 2.09044614,
"system": 2.4978693400000003,
"min": 2.31761192174,
"max": 3.88386585174,
"times": [
2.8053894827400003,
2.71828572774,
3.15561667274,
2.62078264274,
2.87030676074,
3.88386585174,
2.31761192174,
3.44165807374,
2.78038254874,
2.43753990174
]
},
{
"command": "pacquet@main",
"mean": 2.29243138394,
"stddev": 0.2153112609049636,
"median": 2.2093781642400003,
"user": 2.10419704,
"system": 2.48583174,
"min": 2.03949965474,
"max": 2.72049411874,
"times": [
2.1503075807400003,
2.2055122057400003,
2.5845158117400002,
2.03949965474,
2.21324412274,
2.17080390374,
2.23693982674,
2.42925443774,
2.1737421767400003,
2.72049411874
]
}
]
}Scenario: Isolated linker: fresh restore, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.7895145084800002,
"stddev": 0.09209700892819776,
"median": 0.7640212622800001,
"user": 0.28712652,
"system": 1.0272518000000002,
"min": 0.7227021872800001,
"max": 1.02970735828,
"times": [
1.02970735828,
0.7418368502800001,
0.7862056742800001,
0.8128865832800001,
0.7241934432800001,
0.7288461112800001,
0.7990431322800001,
0.7227021872800001,
0.7369001242800001,
0.8128236202800001
]
},
{
"command": "pacquet@main",
"mean": 1.0654062142799998,
"stddev": 0.4861749470398279,
"median": 0.9159630687800001,
"user": 0.28357962,
"system": 1.0386517999999998,
"min": 0.71823979928,
"max": 2.29620017528,
"times": [
2.29620017528,
0.83187058428,
0.7640364832800001,
0.9390743392800001,
0.72728724728,
0.71823979928,
0.9169228722800001,
0.9150032652800001,
1.06577347128,
1.47965390528
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 2.57809216502,
"stddev": 0.22672964249687944,
"median": 2.60340647172,
"user": 3.06795368,
"system": 2.4578305199999995,
"min": 2.28869623522,
"max": 2.88321406122,
"times": [
2.28869623522,
2.7071145692200003,
2.40758811822,
2.36643241622,
2.75148364422,
2.49969837422,
2.31296932622,
2.72826968322,
2.88321406122,
2.8354552222200002
]
},
{
"command": "pacquet@main",
"mean": 2.4050073783199997,
"stddev": 0.3616198284064763,
"median": 2.23565607372,
"user": 3.02922798,
"system": 2.40437852,
"min": 2.22005801922,
"max": 3.15284093922,
"times": [
3.02374934122,
3.15284093922,
2.22005801922,
2.2521307582200003,
2.23233831722,
2.22474420722,
2.25701891922,
2.22087980822,
2.22733964322,
2.23897383022
]
}
]
}Scenario: Isolated linker: fresh install, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 1.46821183506,
"stddev": 0.01661131516353495,
"median": 1.47028000796,
"user": 1.37153164,
"system": 1.4836646199999997,
"min": 1.44042489146,
"max": 1.48796435746,
"times": [
1.44042489146,
1.48471888946,
1.47460281146,
1.48796435746,
1.46595720446,
1.4796820664600001,
1.45568518046,
1.4850463574600001,
1.45028082146,
1.4577557704600002
]
},
{
"command": "pacquet@main",
"mean": 1.44251585276,
"stddev": 0.1354950326447229,
"median": 1.41705637546,
"user": 1.38729334,
"system": 1.44172302,
"min": 1.33287933646,
"max": 1.79835287946,
"times": [
1.4555039784600001,
1.33287933646,
1.3444379964600002,
1.44873659446,
1.36714859246,
1.79835287946,
1.39579634546,
1.3585881744600001,
1.43831640546,
1.4853982244600001
]
}
]
} |
|
| Branch | pr/12040 |
| Testbed | pacquet |
🚨 1 Alert
| Benchmark | Measure Units | View | Benchmark Result (Result Δ%) | Upper Boundary (Limit %) |
|---|---|---|---|---|
| isolated-linker.fresh-restore.cold-cache.cold-store | Latency seconds (s) | 📈 plot 🚷 threshold 🚨 alert (🔔) | 2.90 s(+38.29%)Baseline: 2.10 s | 2.52 s (115.24%) |
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 | 2,578.09 ms(+9.58%)Baseline: 2,352.69 ms | 2,823.23 ms (91.32%) |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 1,468.21 ms(-0.04%)Baseline: 1,468.80 ms | 1,762.56 ms (83.30%) |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot 🚷 view threshold 🚨 view alert (🔔) | 2,903.14 ms(+38.29%)Baseline: 2,099.30 ms | 2,519.16 ms (115.24%) |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 789.51 ms(+15.56%)Baseline: 683.20 ms | 819.84 ms (96.30%) |
|
Congrats on merging your first pull request! 🎉🎉🎉 |
* fix: preserve tarball dependency integrity in the lockfile URL/tarball resolvers do not return an integrity (it is only known after the tarball is downloaded). When a remote-tarball dependency was reused from the lockfile without being re-fetched, the freshly resolved resolution had no integrity and the existing one was dropped, breaking subsequent --frozen-lockfile installs under the lockfile-integrity hardening (ERR_PNPM_MISSING_TARBALL_INTEGRITY). Carry the integrity over from the current resolution instead. Closes #12001 Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * refactor(package-requester): simplify tarball integrity carryover guard Align the integrity carryover added in the previous commit with its sibling block in the download path: use `!resolution.type` (the idiom already used there) and drop the `newIntegrity == null` clause, which is redundant once `resolution` is the freshly resolved resolution. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * test(pacquet): cover tarball-dependency integrity preservation (#12001) pnpm #12040 fixes dropping a remote-tarball dependency's integrity when an unrelated package is installed afterwards. Pacquet can't reach that scenario yet: a non-registry https-tarball direct dependency hits the TarballResolver, which returns no name_ver/integrity, so lockfile build panics with MissingSuffix. Add the regression test for the target behavior, gated with allow_known_failure! until external tarball deps install. Tracked in #12053. --------- Co-authored-by: Zoltan Kochan <[email protected]>
|
@zkochan would it be possible to release today a new |
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.
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.
…on (#12096) * fix: preserve integrity of remote tarball dependencies on re-resolution 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 #12040, which fixes the same class of bug in the package-requester layer but does not cover this re-resolution path. Closes #12067. * test: cover integrity carryover on tarball re-resolution * refactor: check integrity before type in the tarball carryover guard * perf(pacquet): reuse the warm-store tarball on re-resolution instead of re-downloading --------- Co-authored-by: Zoltan Kochan <[email protected]>
What
Fixes #12001 — the
integrityfield is dropped from the lockfile entry of a remote (non-registry) https-tarball dependency whenever an unrelated package is installed afterwards.Why
URL/tarball resolvers (
@pnpm/resolving.tarball-resolver) intentionally do not return anintegrity— it is only known after the tarball is downloaded and hashed. InresolveAndFetch, the freshly resolved resolution overwrites the previous one:The integrity is re-attached only on the download path (
if (fetchedResult.integrity ...)). So when a tarball dependency is reused from the lockfile without being re-fetched (e.g. any subsequentpnpm addof an unrelated package), its integrity is never restored and is written out of the lockfile.Since the lockfile-integrity hardening, a hash-less remote-tarball entry then makes the next
--frozen-lockfileinstall fail closed withERR_PNPM_MISSING_TARBALL_INTEGRITY. This bites CI frequently when a bot (e.g. Renovate) regenerates the lockfile for an unrelated bump.How
In
installing/package-requester, when the resolver returns no integrity and the package was not updated, carry the integrity over from the current (lockfile) resolution instead of dropping it. The change is narrowly guarded to tarball-type resolutions whose id is unchanged.Reproduction
Test
Added a unit test in
package-requesterthat drives the buggy path with a custom resolver returning no integrity and acurrentPkgthat has one, asserting the integrity is preserved. It fails onmainand passes with this change. Existingpackage-requester(21) anddeps-installertarball/lockfile/local/custom-resolver suites remain green.Summary by CodeRabbit