Fix vendored requirements.txt re-vendor to a superseding patch (#765) - #766
Open
Mikola Lysenko (mikolalysenko) wants to merge 3 commits into
Open
Mikola Lysenko (mikolalysenko) wants to merge 3 commits into
Mikola Lysenko (mikolalysenko) wants to merge 3 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
A requirements.txt project vendored with one patch could not move to a newer patch for the same package: vendor, get --mode vendored and scan --mode vendored failed with pypi_requirements_already_vendored and pip kept installing the old patch, while --dry-run previewed a re-vendor. The requirements pre-flight now re-wires the vendor lines the older patch's ledger entry recorded, in place, to the new wheel. Each line keeps its marker, hash mode and transitive note, and each record keeps the pre-vendor original, so vendor --revert still restores the user's pin. Without that ledger entry, or when a recorded line has drifted, the re-vendor is still refused before anything is written. (#765) Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko)
marked this pull request as ready for review
October 4, 2026 09:11
Collaborator
Author
|
BugBot review Generated by Claude Code |
The previous commit ran rustfmt across the whole workspace and reformatted 129 files that the requirements.txt re-vendor fix does not touch. That churn hides the real change from reviewers and conflicts with nearly every other open PR. Restore those files to main so the PR only carries the fix: CHANGELOG, vendor/pypi.rs, vendor/pypi_requirements.rs and the mode_migration_pypi test. Co-Authored-By: Claude <noreply@anthropic.com>
Collaborator
Author
|
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 2fad4d0. Configure here.
Collaborator
Author
|
Ready for review at
Generated by Claude Code |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
LLM Description written by Claude Code:claude-opus-5-5
Fixes #765
Summary
A requirements.txt project vendored with one patch can now move to a newer patch for the same package. Before this,
vendor,get <uuid> --mode vendoredandscan --mode vendoredall failed withpypi_requirements_already_vendored(exit 1,partial_failure). requirements.txt, the old uuid dir and the ledger stayed on the old patch, so pip kept installing it. Meanwhile--dry-runpreviewedwould_revendorand CLI_CONTRACT said the package "is still re-vendored automatically".Root cause
preflight_requirements(vendor/pypi_requirements.rs) treated a socket-patch vendor line for the package at a different patch uuid as a hard refusal. The CLI's re-vendor path (record_vendor_entry→sweep_stale_artifact) and the dry-run preview both expect the backend to re-wire in place, as npm, cargo, gem and pnpm do.All the vendored PyPI writers have the same gap: uv (
pypi_uv_source_already_exists, the vendored half of #742), Hatch (vendored half of #650), Poetry, PDM and Pipenv. This PR is the requirements.txt slice only. The other lanes are follow-ups. #742 and #650 stay open, and their hosted halves are in #743.Fix
RequirementsTarget::Rewire. When requirements.txt already routes the package to an older uuid and the ledger holds exactly one pypi entry at that uuid whose records are allrequirements_lines taggedname==version, the pre-flight plans a re-wire from that entry's own records (plan_rewire):(transitive)note.original. Sovendor --revertof the new entry still restores the user's own pin byte for byte, andcarry_forward_wiringhas nothing to fill in.write_planand shared bywire_requirementsandrewire_requirements. It keeps the symlink refusal and the partial-write unwind.vendor_stale_artifact_removed).Tests (red → green)
crates/socket-patch-cli/tests/mode_migration_pypi.rs::requirements_vendored_revendors_superseding_patch: vendor A, switch the manifest to B (different patched bytes),vendor, then re-run (in sync), thenvendor --revert. Covers an unhashed tree and a hashed tree with a marker and a\continuationpartialFailure/pypi_requirements_already_vendoredvendor_stale_artifact_removed), ledger on B, revert restores the original bytesvendor::pypi::tests::requirements_superseding_uuid_revendors_in_place: unhashed; hashed + marker + continuation + CRLF; a pin in a-rinclude; an appended transitive line. Checks that originals carry over and that revert of the new entry is byte-exactRefused { pypi_requirements_already_vendored }vendor::pypi::tests::requirements_superseding_uuid_without_ledger_refuses(replacesrequirements_stale_uuid_vendor_line_refuses)vendor::pypi::tests::requirements_superseding_uuid_drifted_line_refusesLocal runs:
cargo fmt --all -- --check: clean.cargo clippy --workspace --all-features -- -D warnings: clean.cargo test --workspace --all-features --no-fail-fast: 9727 passed, 12 failed, 253 ignored. All 12 failures are chmod-denial tests that cannot fail under root, which is what this sandbox runs as:*_write_failure_*,*unremovable*,relax_loop_must_not_traverse_symlinked_root,wire_failure_rolls_back_already_written_filesand others. Re-run as the unprivilegednobodyuser, every one of them passes. That includes the requirements write-unwind test thatwrite_plannow serves.mode_migration_pypi: 13/13.Follow-ups (not in this PR)
🤖 Generated with Claude Code
Note
Medium Risk
Touches PyPI requirements vendoring and on-disk requirements edits with ledger-driven revert semantics; mistakes could break pins or revert, but scope is limited to requirements.txt and guarded by preflight checks plus broad new tests.
Overview
Fixes #765 for requirements.txt vendoring: when the manifest moves a package from patch A to patch B,
vendor(and vendored-mode flows that call the same backend) now re-wires existing socket vendor lines in place instead of failing withpypi_requirements_already_vendored.Preflight in
pypi_requirementsgainsRequirementsTarget::Rewire: if the tree still points at an older patch uuid but the vendor ledger has exactly one matching pypi entry, it plans an in-place swap via newrewire_requirements/plan_rewire, preserving markers, hash mode,-rincludes, and pre-vendororiginalbytes sovendor --reverton the new entry still restores the user’s pins. Shared file writes go throughwrite_plan.Re-vendor is still refused (unchanged error family) when the ledger is missing, lines drifted from what was recorded, or live vendor lines don’t match the ledger.
The PyPI orchestrator adds
WiringPlan::RequirementsRewire; stale patch A artifacts continue to be reclaimed (vendor_stale_artifact_removed). CHANGELOG documents the behavior. uv / Poetry / PDM / Pipenv superseding-patch re-vendor is explicitly out of scope here.Tests: CLI
requirements_vendored_revendors_superseding_patchand core cases for multiple requirement shapes, drift, and missing ledger.Reviewed by Cursor Bugbot for commit 2fad4d0. Configure here.
Generated by Claude Code