Skip to content

Fix requirements.txt writers ignoring pip hash mode (#376, #378) - #383

Open
Mikola Lysenko (mikolalysenko) wants to merge 9 commits into
mainfrom
agent/fix-requirements-hash-mode
Open

Mikola Lysenko (mikolalysenko) wants to merge 9 commits into
mainfrom
agent/fix-requirements-hash-mode

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #376
Fixes #378

Summary

Hosted and vendored scans no longer break pip install -r on a requirements.txt that has no hashes. setup no longer reports success after writing a line that pip will refuse in a hash-pinned requirements.txt.

Root cause

pip's hash-checking mode is all or nothing. It turns on for the whole install as soon as any requirement carries --hash, and then every requirement, transitive dependencies included, has to be ==-pinned and hashed. None of the three requirements.txt writers checked which mode the requirements set was in:

Fix

Per-issue checklist

Test updates (intended behavior change)

Several existing tests expected --hash in unhashed files, which is the #376 bug itself. They now expect the unhashed forms:

  • the unit tests in the hosted and vendored writers;
  • the redirect fixture and the VEX discovery golden (redirect-pypi.json);
  • in_process_get_hosted_ecosystems, e2e_hosted_production, e2e_vendored_production and e2e_vendor_pypi_build.

Two tests needed more than a new expected string:

  • vendor_ledger_schema_e2e: the base-binary parity test now names this one intended difference. The legacy fixtures are unchanged, so the legacy-revert coverage stays.
  • The pin-guard tests and the marker e2e rely on a hashed pin, so their inputs are now hash-pinned.

An earlier commit on this branch accidentally reformatted about 55 unrelated files, and e0ed1aa reverts them. main isn't rustfmt-clean and CI doesn't check formatting, so this PR leaves formatting alone. main (including #277) was merged in at a2dedc5.

Evidence

  • cargo clippy --workspace --all-features -- -D warnings: clean on fcc4fcc.
  • cargo test --workspace --all-features (local, sandbox runs as root): every failure is a test that needs a write, removal or permission check to fail, which root bypasses. None exercises code this PR changes.
  • SOCKET_PATCH_PIP_E2E_VERSIONS=24 SOCKET_PATCH_PIP_E2E_REQUIRED=1 cargo test -p socket-patch-cli --all-features --test e2e_vex_build -- pip:: --ignored: passes for all 4 cells × 2 modes (pre-merge).
  • CI is green on fcc4fcc.
  • Not touched: the npm, pypi and gem wrappers (they only dispatch to the binary).

Follow-ups

🤖 Generated with Claude Code

https://claude.ai/code/session_017aQf44e9818AbFKDYnuAHZ

Assisted-by: Claude Code:claude-opus-5-5
A hosted or vendored scan added --hash to the patched line of a
requirements file that had no hashes. pip then turns on hash-checking
mode for the whole install, so every other requirement and every
transitive dependency failed to install (#376).

Both writers now check whether the requirements set is already in
hash-checking mode. If it is, they keep writing --hash as before. If
not, hosted pins the patched wheel with the url's #sha256= fragment,
which pip still verifies, and vendored writes the committed wheel path
without a hash.

Assisted-by: Claude Code:claude-opus-5-5
socket-patch setup appended an unpinned, unhashed socket-patch[hook]
line to requirements.txt even when the file (or an -r include) was in
pip's hash-checking mode. pip then refused to install anything from it,
while setup reported success (#378).

setup now reports an error and leaves such a file untouched. setup
--remove also drops a hook line together with its backslash
continuation lines, so no stray --hash line is left behind.

Assisted-by: Claude Code:claude-opus-5-5
Adds an 'unhashed' cell (six plus idna, no hashes) to the real-pip
capstone. It asserts the wiring adds no --hash and that pip installs
the patched six. Also updates the redirect fixture to the #sha256=
url form written for unhashed files.

Assisted-by: Claude Code:claude-opus-5-5
The production and vendored e2e legs write a one-line unhashed
requirements.txt and then asserted a --hash pin, which is the #376
behavior itself. They now assert that no --hash is added. The hosted
leg also asserts the #sha256= url pin, which pip and uv both verify.

Assisted-by: Claude Code:claude-opus-5-5
- The VEX discovery golden now shows the #sha256= url on the hosted
  requirements ref.
- The hosted get test expects the url-fragment pin.
- The vendor ledger parity test allows the one intended change from
  the base binary: no --hash in an unhashed requirements.txt.
- The marker e2e installs with --require-hashes, so its input is now
  hash-pinned the way pip-compile writes it.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review September 30, 2026 22:17
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

An earlier cargo fmt --all run reformatted about 55 files that this
fix doesn't otherwise touch; main isn't rustfmt-clean and CI doesn't
check formatting. This restores those files to main so the PR diff
only holds the hash-mode change and its tests.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Bring the pip hash-mode fix onto the v5 workflow from #277. Main removed
the setup command, so the #378 hook-dependency guard in
setup/pypi/edit.rs has no home and is dropped with the file. The #376
half still applies: hosted and vendored requirements.txt writers only
emit --hash when the tree is already in pip's hash-checking mode. The
hosted-get test docs keep main's "no ledger" wording with the fragment
pin, and CLI_CONTRACT.md's requirements rows now describe the
conditional --hash.

Co-Authored-By: Claude <[email protected]>
Claude-Session: https://claude.ai/code/session_01GQoii5oP1pwcJh5mzzo1HU
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

bugbot run


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issue.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit a2dedc5. Configure here.

Comment thread crates/socket-patch-core/src/vendor/pypi_requirements.rs
A requirements vendor line written into an unhashed requirements set has
no --hash, so wired_pin_in returned no pin and a ledgerless in-sync
rebuild skipped the guard entirely, including the path check. A rebuilt
wheel at another filename would then leave the wired line pointing at a
file that does not exist.

wired_pin_in now pins the path of a hashless line with an empty sha256,
and the guard treats an empty pinned sha256 as path-only. A hash that is
present but malformed still pins nothing, as before.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017aQf44e9818AbFKDYnuAHZ

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants