Fix pip rollback refusing all-hosted requirements (#410) - #827
Mikola Lysenko (mikolalysenko) wants to merge 6 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
Hosted rollback, remove and the hosted-to-vendored takeover refused a requirements.txt in which every requirement was a hosted pin (a lone `six==1.16.0`, or one beside `-e .`). They couldn't tell whether the original line used pip's hash-checking mode, so the only way back was version control. The restore now counts an editable line as unhashed evidence (pip refuses editables in hash-checking mode). When no other line settles the mode, it reads the hosted line itself: the rewriter writes `--hash` only into an already hashed file and otherwise pins by the url's `#sha256=` fragment. With nothing else in the file to conflict with, either restored form installs. Fixes #410 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
BugBot review Generated by Claude Code |
|
[agent] CI status on
The head is now green (482 succeeded, 6 skipped), Bugbot found no issues, and there are no open threads. It's waiting on human review. Generated by Claude Code |
|
Ready for review — head
Generated by Claude Code |
Resolve the CLI_CONTRACT.md pypi restore bullet: keep main's uv upload_time spelling note and this branch's hash-checking-mode (#410) description. Co-Authored-By: Claude <noreply@anthropic.com>
|
bugbot run Generated by Claude Code |
Assisted-by: Claude Code:claude-opus-5-5
#605 taught the name-keyed npm resolver to probe bundled store trees, so it now finds aliased copies (node_modules/lp) and a nested host's store peers itself. Two vex_consumed tests from #738 assumed that set never held aliases, so main's CI went red after both merged. The tests now feed the alias-free set explicitly to keep covering alias expansion, and also check the resolver's own set reaches the same copies with no duplicates. No production code changes. Assisted-by: Claude Code:claude-opus-5-5 (cherry picked from commit 40dac07)
|
[agent] CI on This isn't this PR's failure: both tests fail the same way on bare Local results on Generated by Claude Code |
|
BugBot review 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 9eb4381. Configure here.
LLM Description written by Claude Code:claude-opus-5-5
Fixes #410
Summary
Hosted
rollback,remove <purl>and the hosted → vendored takeover (vendor/scan --mode vendoredover a hosted pin) no longer refuse arequirements.txtin which every requirement is a hosted pin. Before this fix, the simplest project shape (six==1.16.0, or-e .plus a pin) could be switched to hosted but never switched back. All three commands exited 1 with… is not derivable; restore it from version control instead.Root cause
restore_requirements(crates/socket-patch-core/src/patch/redirect/upstream/pypi.rs) works out pip's hash-checking mode for the restored line from the other requirement lines in the file:(false, false)arm and refused.--prefixed line before counting, so an-e .line, which pip refuses in hash-checking mode, never counted as evidence that the file is unhashed.Fix
-e <path>,-e<path>,--editable <path>,--editable=<path>) now counts as unhashed evidence.pip install -rfor every other unhashed line #376 / Fix requirements.txt writers ignoring pip hash mode (#376) #383, the requirements rewriter writes--hashonly into a file already in hash-checking mode, and otherwise pins by the url's#sha256=fragment. A pre-Fix requirements.txt writers ignoring pip hash mode (#376) #383 hosted line always carried--hashand so restores hashed. With no other requirement in the file to conflict with, either form installs.--require-hashesfile with an-eline (which pip can't install) is now refused through that same "mixes" arm.Test evidence
six==1.16.0only (LF + CRLF, plus comment/option/blank lines)upstream::pypi::tests::requirements_with_only_a_hosted_pin_restores_unhashedsix==1.16.0 --hash=…only lineupstream::pypi::tests::requirements_with_only_a_hashed_hosted_pin_restores_hashed-e .+ pin (5 editable spellings × fragment /--hashhosted line)upstream::pypi::tests::an_editable_line_settles_requirements_as_unhashedupstream::pypi::tests::other_requirement_lines_still_settle_the_mode--require-hashes+-erefusalupstream_restore_golden::requirements_hash_mode_ambiguity_is_refused(updated: it asserted the #410 refusal)scanthenrollback/remove/vendortakeover,six==1.16.0mode_migration_pypi::requirements_sole_hosted_pin_unwinds-e .+six==1.16.0mode_migration_pypi::requirements_editable_beside_hosted_pin_unwindsCommands run locally:
cargo test -p socket-patch-core --all-features --lib --test upstream_restore_golden: 4846 passed. 4 tests failed, all unrelated to this change:copy_tree::relax_loop_must_not_traverse_symlinked_root,vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry,pypi_poetry::wire_write_failure_…andpypi_requirements::wire_failure_rolls_back_…. They inject write failures withchmod 0555, which root ignores, and the sandbox runs as uid 0.cargo test -p socket-patch-core --all-features --test upstream_restore_golden: 42 passed.cargo test -p socket-patch-cli --all-features --test in_process_rollback_hosted --test mode_migration_pypi --test in_process_get_hosted_ecosystems: 22 + 14 + 8 passed.cargo clippy --workspace --all-features -- -D warnings: clean.cargo fmt: the changed hunks are formatted.cargo fmt --all -- --checkalready fails onmain(about 130 files; CI runs no fmt job), so those files are left alone here.cargo test --workspaceran out of the sandbox's disk allowance (30 GB of test binaries), so the full matrix is left to CI.CI
f4033e3: 482 check runs succeeded and 6 were skipped. Bugbot found no issues and there are no review threads.PDM patch compatibility / native (ubuntu-latest, 2.1.5)and(…, 2.10.4)failed once onf4033e3in agent-mode cells (marker agent FAIL appliedExactlyOne). That path doesn't touch the requirements restore, and the same workflow passed on191ff15, whose code is identical except for one test file. One rerun of the failed jobs passed.Note
Low Risk
Narrows when PyPI requirements upstream restore refuses vs succeeds; behavior is covered by new unit, golden, and CLI tests with no auth or network policy changes beyond existing restore paths.
Overview
Fixes #410: hosted rollback, remove, and vendor can unwind a
requirements.txtwhose only requirements are hosted pins (e.g. lonesix==1.16.0or-e .plus a pin). Previously upstream restore always refused with “hash-checking mode … not derivable.”Upstream restore (
restore_requirementsinpypi.rs) now infers pip hash-checking mode when no other requirement line settles it: use whether the hosted line used--hashvs a URL#sha256=fragment (as the hosted rewriter recorded). Editable lines (-e/--editable) count as evidence the file is unhashed, since pip disallows editables in hash-checking mode. Truly mixed hashed/unhashed files are still refused.CLI_CONTRACT.md documents this behavior. Tests add core unit coverage, golden round-trips for sole-pin files, and CLI migration tests for rollback/remove/vendor. vex_consumed npm hosted tests are adjusted so alias expansion is still exercised after #605’s resolver changes.
Reviewed by Cursor Bugbot for commit 9eb4381. Configure here.
Generated by Claude Code