Repository navigation
Fix vendored Pipenv re-vendor to a newer patch (#769) - #825
Mikola Lysenko (mikolalysenko) wants to merge 9 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
ba3463a to
6483c64
Compare
A Pipenv project vendored at one patch never moved to a newer patch for the same package: the re-vendor refused with pypi_pipenv_source_already_exists and the run exited 1, although the dry run previewed would_revendor. When the vendor ledger records the Pipfile.lock entry the older patch wrote, and that entry is unchanged, it is now rewired in place to the new wheel. The record carries the older entry's pre-vendor registry original forward, so vendor --revert still restores the user's pin. Without that record, or after an edit, it still refuses as before. Refs #769 Assisted-by: Claude Code:claude-opus-5-5
6483c64 to
4fc3896
Compare
When a venv was installed from the vendored wheel of an older patch (pipenv sync after vendoring), re-vendoring to a newer patch skipped the package as package_not_installed and exited 1: the installed files are the old patch's bytes, so they failed the new patch's installed-variant check. When the vendor ledger holds exactly this package at an older patch uuid, such an install is now treated like a lock-only checkout: the pristine wheel comes from the lock, registry or patch service, and the package is re-vendored. The service download plan makes the same call. Fixes #769 Assisted-by: Claude Code:claude-opus-5-5
check_target_guards and wire_pipenv now have no production caller (the vendor flow passes the ledger through the _superseding variants), so clippy flagged them as dead code. Compile them for tests only and point the docs at the variants production uses. Refs #769 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
Burn-down agent: labeled Ready for review.
Generated by Claude Code |
Resolve the stage_manifest_with doc comment conflict in mode_migration_pypi.rs: #766 added the same helper on main, so keep main's wording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VSXCFoPbraq7rNKJpXEP2n
Main has been red since 4646693 (#605): two commands::vex_consumed tests built for #738 assume the name-keyed resolver never returns npm-aliased copies, which #605 changed. This is the same test-only change as #851 and becomes a no-op once that lands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VSXCFoPbraq7rNKJpXEP2n
|
I ported #851's test-only change into this PR as 3fade63. It becomes a no-op once #851 merges. Locally, The earlier Generated by Claude Code |
|
bugbot run Generated by Claude Code |
|
Burn-down agent: labeled Ready for review.
Generated by Claude Code |
Resolved conflict in crates/socket-patch-cli/tests/mode_migration_pypi.rs: main (#725) renamed the Pipenv fixture helper to stage_pipenv (returning its wiring files); this branch had added write_pipenv_project for the new #769 re-vendor test. Kept main's stage_pipenv and pointed the branch's pipenv_revendors_to_a_superseding_patch test at it. Co-Authored-By: Claude <noreply@anthropic.com>
|
bugbot run Generated by Claude Code |
Main is red since 1714299 (#865): its production_digests_go_through_the_helpers guard flags the inline digests that #646 added in gradle_cache.rs, jvm_jar.rs and sidecars/maven.rs. This is the same change as #878 and becomes a no-op once that lands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VSXCFoPbraq7rNKJpXEP2n
|
I ported #878's change into this PR as ff5fb6c. It becomes a no-op once #878 merges. Locally, the digest test now passes and clippy is clean. Generated by Claude Code |
|
Several workflows show red on ff5fb6c, but no test failed. Every red job was cancelled between about 20:31 and 20:34 UTC, before finishing and with no failed step: CI ( I re-ran the failed jobs once for the CI, Gradle, PDM and vlt runs. The CodeQL run on this PR can't be retried through the API ("cannot be retried"); it will run again on the next push. Generated by Claude Code |
|
bugbot run 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 ff5fb6c. Configure here.
|
Burn-down agent: labeled Ready for review at
Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Fixes #769
Summary
Before this change, a Pipenv project vendored at patch A could never move
to a newer patch B for the same package.
scan --mode vendoredandget <B> --mode vendoreddownloaded B, reported it as replacing A, andthen exited 1, while
--dry-runpreviewedwould_revendor:failed pypi_pipenv_source_already_existspipenv sync):skipped package_not_installed("no installed package found on disk"), whichis false
Now both shapes re-vendor to B: Pipfile.lock is rewired to B's wheel, A's
artifact is swept (
vendor_stale_artifact_removed), the ledger moves toB, and
vendor --revertof B restores the original registry pin.Root cause
Two gaps, both about socket-patch's own wiring at an older patch uuid:
check_target_guardsinvendor/pypi_pipenv.rsrefused the "ours, but a stale patchgeneration" entry outright, because wiring over it would lose the only
recorded registry original. The original is not lost: the ledger entry
for the older uuid holds it.
commands/vendor.rs, a PyPIinstall is hashed against the new record's
beforeHash. A venvinstalled from A's wheel holds A's patched bytes, so the probe dropped
the package, and it fell through to
package_not_installed.Fix
pypi_preludelooks up the ledger entry that vendored this package atanother uuid and passes it to the new
check_target_guards_superseding/wire_pipenv_superseding. An entryrouted through that older uuid's wheel is rewired in place only when the
ledger records that exact entry (
section:key, unchanged sincevendoring) with a pre-vendor original, and the wheel names the same
release. The new record carries that original forward. With no ledger
record, after an edit, or for another release it still refuses, and
the message now says which.
superseded_installin the vendor loop (and the service download plan,which mirrors the loop): when the probe fails but the ledger holds exactly
this package at an older uuid, the candidate gets the same pristine source
path a lock-only checkout uses, instead of being skipped. This is limited
to a sole candidate, or a ledger key equal to the candidate, so it never
picks among sibling release variants.
This is the Pipenv lane of the #765 family (requirements.txt: #766;
uv/Hatch hosted: #743). It is kept separate so #766, which is ready for
review, doesn't grow. Follow-up (not in scope):
pypi_poetry.rsandpypi_pdm.rshave the same "STALE patch generation" refusal arm. No issuehas been filed for those yet.
No wrapper changes:
npm/,pypi/andgem/only dispatch to the binary.Tests (red → green)
vendor::pypi::tests::pipenv_superseding_uuid_revendors_in_place(core)mode_migration_pypi::pipenv_revendors_to_a_superseding_patch(lock-only lane)mode_migration_pypi::pipenv_revendors_to_a_superseding_patch(venv lane)package_not_installed, as reported)pipenv_superseding_uuid_without_ledger_refusespipenv_superseding_uuid_drifted_entry_refusesThe red runs were done by applying the tests to the pre-fix sources: the
three core tests failed on
maincode, and the venv lane failed withonly the core half applied.
Commands run locally (Linux, root):
cargo clippy --workspace --all-features -- -D warnings: cleancargo test -p socket-patch-core --all-features --lib: 4845 passed.4 failed, all chmod/permission simulations that can't fail as root, and
none in touched code (
copy_tree,vlt_heal, poetry/requirementswrite-failure tests).
cargo test -p socket-patch-cli --all-features --lib: 834 passedmode_migration_pypi,in_process_vendor,in_process_redirect_pipenv,in_process_python_envs,e2e_vendor_pypi_build,e2e_vendored_production,e2e_vex_vendor,vendor_group_commit_e2e,vendor_ledger_schema_e2e,scan_requirements_lock_only,covgap_commands_get: all pass.covgap_commands_vendorhas 3 failures, the state-write-failure tests,which also need a non-root chmod.
SOCKET_PATCH_PIPENV_E2E_REQUIRED=1 SOCKET_PATCH_PIPENV_E2E_VERSIONS=2026.8.0 cargo test -p socket-patch-cli --all-features --test e2e_vex_build -- pipenv:: --ignored: passcargo test --workspaceran out of this session's diskallowance while building every test binary, so CI is the full run.
cargo fmt: the touched code is formatted.mainitself isn'trustfmt-clean under 1.93.1, so the unrelated reformat churn was left out.
🤖 Generated with Claude Code
https://claude.ai/code/session_01VSXCFoPbraq7rNKJpXEP2n
Note
Medium Risk
Changes Pipenv lock wiring and vendor planning logic that affects ledger originals and revert correctness; behavior is gated on strict ledger/lock checks but touches a critical dependency path.
Overview
Fixes #769 so Pipenv projects vendored at patch A can re-vendor in place to a superseding patch B (same release), matching
would_revendor/ CLI behavior for both lock-only checkouts and venvs installed from A’s wheel.Pipfile.lock path:
check_target_guards_superseding/wire_pipenv_supersedingtreat an older.socket/vendor/pypi/<uuid>wheel as re-wirable when the vendor ledger still has that uuid’s entry (unchangedsection:key, same wheel identity) and a pre-vendor registry original to carry forward; revert of B then restores the registry pin. Missing ledger, drifted lock entries, or wrong release still refuse with clearer errors.CLI vendor loop:
superseded_installpluslookup_entry_kvdetect when the installed-variant probe fails because the venv holds A’s patched bytes, not B’s pristine baseline; those packages are sourced like lock-only (.socket/vendor/.uninstalled) instead of being skipped as not installed. The service download planner mirrors the same logic.Tests cover lock-only and venv lanes (CLI), plus core guard/refusal cases. Unrelated: JVM/Gradle checksum code routes SHA-1/SHA-256 through shared
utils::digesthelpers.Reviewed by Cursor Bugbot for commit ff5fb6c. Configure here.
Generated by Claude Code