Repository navigation
Fix vendored Hatch refs in %/#/;/? checkouts (#547) - #1428
Conversation
Assisted-by: Claude Code:claude-opus-5-5
Vendored Hatch wiring points at {root:uri}/.socket/vendor/..., but
Hatch expands {root:uri} escaping only spaces. In a checkout whose path
holds %, #, ? or ; (a Jenkins multibranch workspace like
app_feature%2Fx), pip decodes, truncates or rejects the URL, so every
Hatch env failed to install while the scan reported success.
vendor and scan --mode vendored now refuse such a root with
pypi_hatch_unsupported before writing anything, including a re-run of a
checkout that was vendored elsewhere and then moved. The hatch testing
doc notes the relocation limit.
Fixes #547
Assisted-by: Claude Code:claude-opus-5-5
Hatch builds {root:uri} from the real project path, so a checkout
reached through a symlink is now checked at its target. Only a % that
starts a %XX escape is refused (pip leaves 100% alone), and a tab or
other non-space whitespace or control character, which the PEP 508
parser stops at, is refused too. A Windows \\?\ prefix no longer counts
as a ?.
Refs #547
Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
Labeled Ready for review at head
Slack announcement not sent this run (Slack send unavailable); the next run will retry. Generated by Claude Code |
Final review briefWhat it does. Vendored Hatch wiring writes Risk: low. It only adds a refusal on the vendored Hatch path, which runs in Look here
Verified
Changes I made: none. Open questions: none. Nit, not blocking: on a supersede refusal the check runs twice, once in Auto-merge is armed, so approving this sends it straight to the merge queue. Generated by Claude Code |
The coverage job failed this PR's merge-queue run (2ea36d6). That job flakes in in_process_edge_cases (#1416): the cache_env scrub self-tests read the parent env twice while in-process tests set SOCKET_OFFLINE. Port #1418's fix (one snapshot of the parent env) so the queue run is not at the mercy of that race. It no-ops once #1418 lands. Refs #1416 Assisted-by: Claude Code:claude-opus-5-5
|
[agent] This PR was dequeued because I can't read the job log from this sandbox: the log host returns 403, which is the same block noted on #1416. The annotation only says #1418 fixes that flake and is still open, so I ported its one-file test change in 272c022. It becomes a no-op once #1418 merges. Auto-merge was disarmed when the PR was dequeued. It needs to be re-queued once CI is green on 272c022. 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 272c022. Configure here.
|
Final reviewer: re-enqueued once after the 16:02 merge-queue eviction. Head 272c022 (carries the ported #1418 coverage-flake fix) is approved on head, Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Fixes #547
Summary
vendor/scan --mode vendoredon a Hatch project now refuses(
pypi_hatch_unsupported, nothing written) when the project's real pathcontains a character Hatch's
{root:uri}expands into a broken file URL.Before, the scan reported success and every Hatch env then failed to
install in that checkout. That includes a checkout that was vendored
somewhere else and then moved to such a path: the next
vendorrunfails instead of reporting it as in sync.
Root cause
Vendored Hatch wiring writes
name @ {root:uri}/.socket/vendor/pypi/<uuid>/<wheel>#sha256=….Hatch expands
{root:uri}withhatchling/utils/fs.py:path_to_uri,which escapes only the space. With a
%XXescape,#,?or;in the path (e.g. a Jenkins multibranch workspace
app_feature%2Fx),pip decodes the escape, cuts the path at a fragment or query, or the
PEP 508 parser rejects the requirement. Nothing in
vendor/pypi_hatch.rschecked the root before writing.Change
utils::hatch::root_uri_unsafe_charchecks the real path (symlinksresolved, which is what Hatch expands) for a
%XXescape,#,?,;, and whitespace or control characters other than the space. It looksonly at named path components, so a Windows
\\?\prefix doesn't count.A
%that starts no escape (100%) is allowed, because pip leaves it as is.pypi_hatch::loadandpypi_hatch::preflightrefuse such a root.preflightruns before a superseded patch is unwound, so a refusalnever touches the project's files.
move to.
Hosted and agent mode are unaffected (hosted references are http URLs).
The npm, PyPI and gem wrappers only dispatch to the binary, so they need
no change.
Tests (red → green)
%2F,%20,#,;,?)mode_migration_pypi::hatch_vendored_refuses_a_root_hatch_cannot_expandapp_feature%2Fsix-patch)mode_migration_pypi::hatch_vendored_rerun_refuses_after_relocation_to_unsafe_root100%/@/'/space controls, tab)socket-patch-core vendor::pypi_hatch::tests::root_hatch_cannot_expand_is_refusedload must refuse)Local runs:
cargo fmt --all -- --check: clean.cargo clippy --workspace --all-features -- -D warnings: clean.cargo test --workspace --all-features --no-fail-fast: every Hatch andPyPI test passes. A few tests fail only because of this sandbox, and none
of them touches the Hatch code. The sandbox runs as root, so chmod can't
make a write fail, which breaks the
covgap_commands_vendorstate-writetests, the
repairandin_process_redirectcleanup-failure tests, and4 core lib write-failure tests.
e2e_bun_lockbfails because there is nonetwork (
missing_blob). CI runs as a normal user.e2e_vex_build hatch::) uses temp paths with noneof these characters, so this change doesn't affect it. Left to CI.
Review
/code-review highraised 7 findings. These were fixed in 8d4e9ea:check the real path, not the path as written; allow a
%that starts noescape; refuse whitespace and control characters; ignore the Windows
\\?\prefix; stop the error message from repeating the characterlist by hand.
Not fixed: when the root is refused in the supersede path,
loadandthen
preflightboth run the check. This only happens on a refusal andcosts one extra read of two small files, so it isn't worth a new
refusal code.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Gw7nKi8mXGzQ2JucUXxYRF
Generated by Claude Code
Note
Medium Risk
Changes vendored Hatch preflight/load behavior for checkouts under unusual paths; failures are explicit refusals rather than silent broken installs, with no impact on hosted/agent flows.
Overview
Fixes #547 by refusing vendored Hatch
vendor/ scan work when the project’s canonical checkout path contains characters Hatch’s{root:uri}does not escape (%XX,#,?,;, control/whitespace other than space).loadandpreflightnow callrequire_expandable_root(viaroot_uri_unsafe_char) and returnpypi_hatch_unsupportedwithout writing files or reporting in-sync after a move to an unsafe path. Hosted/agent modes are unchanged.Integration and unit tests cover unsafe Jenkins-style paths (
app_feature%2Fsix-patch), symlink resolution, and safe paths (@,', space, bare%).docs/testing/hatch.mddocuments the restriction.Separately, test
cache_envambient scrubs snapshot the parent environment once throughscrub_parent_varsso self-tests match the scrub and avoid races when in-process tests mutate env between reads.Reviewed by Cursor Bugbot for commit 272c022. Configure here.