Skip to content

Fix uv wiring under no-sources = true (#564) - #1434

Merged
Mikola Lysenko (mikolalysenko) merged 5 commits into
mainfrom
agent/fix-uv-no-sources
Oct 12, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 5 commits into
mainfrom
agent/fix-uv-no-sources

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

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

Fixes #564

Summary

uv ignores every [tool.uv.sources] entry when no-sources = true is set, and (uv 0.10+) the entries of the packages listed in no-sources-package. Hosted scan and vendor both wired the patch through such an entry anyway and reported success. Locked installs (uv sync --locked, uv lock --check) then failed, and a plain uv sync installed the unpatched registry wheel.

Both modes now refuse before writing anything:

mode project (uv.lock) PEP 723 script lock
hosted skipped with redirect_uv_project_unsupported (like the other uv shapes hosted can't wire) redirect_uv_script_unsupported
vendored pypi_uv_no_sources (exit 1) pypi_uv_no_sources

Root cause

Both writers that add [tool.uv.sources] never read uv's no-sources setting:

  • hosted: utils/python_script.rs rewrite_sources, reached from plan_python_metadata_with in patch/redirect
  • vendored: vendor/pypi_uv.rs (projects) and vendor/pypi_lock.rs (script locks)

The fix adds one shared check, python_script::refuse_uv_no_sources, which all three call. It follows uv's precedence. I checked each rule against real uv 0.11.32 with uv lock --offline and a local path source:

  • A PEP 723 script's own [tool.uv] wins.
  • Otherwise a project uv.toml replaces pyproject's [tool.uv] settings completely. If a uv.toml exists without the key, a pyproject no-sources = true is ignored.
  • A script without its own setting takes the project's setting (uv.toml, else pyproject [tool.uv]).
  • no-sources-package lists from a script and the project combine; an explicit no-sources = false disables the list; names compare normalized.

Hosted mode now reads uv.toml as advisory input beside a uv lock (hosted/engine.rs). It is never written. A symlinked or unreadable one is left out instead of refusing the run, the same as the other read-only inputs.

Tests (red → green)

Before the fix, each new test failed on main. All pass with it.

Local checks

  • cargo fmt --all -- --check: clean
  • cargo clippy --workspace --all-features -- -D warnings: clean
  • cargo test --workspace --all-features --no-fail-fast: everything passes except 13 tests that also fail in this sandbox for unrelated reasons. 12 depend on chmod denying writes, which root bypasses; 4 of those were re-run as nobody and pass. 1 (e2e_bun_lockb) needs the real patch API, which this sandbox can't reach. None of them touch uv.
  • The real-uv e2e suites (e2e_redirect_uv_build, e2e_vendor_pypi_build) need PyPI and were not run locally; CI covers them. No wrapper (npm/, pypi/, gem/) changes are needed: they only dispatch to the binary.

Review notes (/code-review high)

Fixed: script fallback to pyproject [tool.uv]; uv.toml read as advisory (a strict registry read would have refused unrelated hosted runs); an unparseable or unreadable uv.toml no longer surfaces as a no-sources or lock-parse error; the vendored script check reads the config once per run.

Not changed:

  • Parent-directory uv.toml. Not reproducible for wired projects. uv stops its settings lookup at a pyproject that has a [tool.uv] table, and the [tool.uv.sources] entry the wiring adds is one. Checked with uv 0.11.32.
  • User-level uv.toml, UV_NO_SOURCES, --no-sources. These belong to the installing machine, not the project, so the scan can't see them. This is documented in docs/testing/uv-compatibility.md.
  • An already-vendored in-sync project that later gets no-sources = true now fails with pypi_uv_no_sources instead of reporting InSync. That matches the existing load-time guards (dynamic deps, workspaces). Such a project is not actually patched under uv, so failing loudly is the intent. vendor --revert doesn't go through this loader and still works.
  • Hosted re-checks per patched dep (a few TOML parses), the same as the neighbouring guards. Not worth a cache.

🤖 Generated with Claude Code

https://claude.ai/code/session_014C6hY72T6RCtYwyGvxAmoT


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
With `no-sources = true` in pyproject's [tool.uv] or a project uv.toml
(or a PEP 723 script's [tool.uv]), uv ignores every [tool.uv.sources]
entry. Hosted scan and vendor still wrote one and reported success, so
`uv sync --locked` then failed and a plain `uv sync` installed the
unpatched registry wheel.

Both modes now refuse before writing anything: hosted reports
redirect_uv_project_unsupported / redirect_uv_script_unsupported and
vendored reports pypi_uv_no_sources. The check follows uv's own
precedence: a uv.toml replaces pyproject's [tool.uv] settings, and a
script's own [tool.uv] wins over uv.toml. Hosted mode now reads uv.toml
as a read-only input.

Fixes #564

Assisted-by: Claude Code:claude-opus-5-5
Add a CLI test that both hosted scan and vendor refuse a uv project
with no-sources = true (in pyproject or uv.toml) and write nothing.
Pin uv.toml in the hosted candidate list and describe the refusal in
the uv compatibility notes.

Refs #564

Assisted-by: Claude Code:claude-opus-5-5
A PEP 723 script without its own no-sources setting takes the
project's: uv.toml if present, else pyproject's [tool.uv]. A script
next to a pyproject with no-sources = true was still wired.

uv.toml is now read only beside a uv lock, as advisory input, so a
symlinked or unreadable uv.toml no longer refuses unrelated hosted
runs. A uv.toml that fails to parse sets nothing here (uv reports it)
instead of surfacing as a no-sources or lock-parse refusal. The
vendored script check reads the project config once per run.

Refs #564

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 12, 2026 16:44
@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.

Comment thread crates/socket-patch-core/src/utils/python_script.rs Outdated
uv 0.10+ also drops the [tool.uv.sources] entries of the packages
named in no-sources-package, so patching a listed package left the
same unpatched install as no-sources = true. Read the list beside the
boolean, with uv's rules: a uv.toml replaces pyproject's [tool.uv], a
script's list combines with the project's, and an explicit
no-sources = false disables it. Names are compared normalized.

The vendored project check moves into check_target_guards, which knows
the package being wired.

Refs #564

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014C6hY72T6RCtYwyGvxAmoT
@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.

✅ 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 b069a70. Configure here.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 12, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Ready for review (burn-down agent)

  • Head: b069a707
  • CI: 35/35 green, 19 path-skipped; no main-wide failures.
  • Mergeable: yes (clean), already approved on this head.
  • Bugbot: reviewed b069a70, no new issues; all review threads resolved.
  • Reviewer focus: how the uv [tool.uv.sources] writers read no-sources = true.

Generated by Claude Code

Merged via the queue into main with commit 74a44b8 Oct 12, 2026
54 checks passed
@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the agent/fix-uv-no-sources branch October 12, 2026 20:00
github-merge-queue Bot pushed a commit that referenced this pull request Oct 13, 2026
* ci(release): publish npm from release.yml, not publish-npm.yml

Every socket-patch npm package's trusted publisher is registered against
release.yml, and npm allows only one publisher per package. The 5.0.0
release dispatched publish-npm.yml, whose OIDC token named that file, so
npm rejected it with E401 before staging anything.

Move the npm leg back inline as release.yml's npm-publish job (same steps
as publish-npm.yml) and delete publish-npm.yml, so the existing
registration keeps working. Add an `npm-retry-version` input that skips
the version gate, build, tag, GitHub release and crates.io jobs and
re-stages npm from an existing release's assets; this retries 5.0.0 now
and replaces the manual Publish npm dispatch. crates.io stays dispatched
through publish-cargo.yml, since crates.io allows several publishers per
crate.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chore(release): fold 22 more PRs into the 5.0.0 notes

Cover the PRs merged to main after #1432's base (1779317..4cb3643):
Cargo source replacement and registry = "crates-io" (#338/#455, #386),
pnpm lockfile=false (#1074), berry removed descriptors (#1435), yarnrc
${VAR} reads (#1100), CRLF .npmrc append (#1196), Composer vendor-dir
cascade and global home (#439/#686, #586), Go +incompatible vex (#484),
gem moved BUNDLE_GEMFILE and commented Gemfile revert (#952, #988),
Poetry source = "pypi" (#1282) and Hatch path characters (#547).

No entries for #1411 (macOS fix to code #1393 added in this release;
the #1393 entry already describes the intended behavior), #1426 (perf
regression introduced and fixed within 5.0.0) or the CI/test/bench PRs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chore(release): cover #1419, #1434, #1440, #1442, #1444 in the 5.0.0 notes

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chore(release): 5.0.1

Cut [Unreleased] into [5.0.1] — 2026-10-13 and stamp 5.0.1 with
scripts/version-sync.sh. The 5.0.1 notes cover main through 501c6d9
(#1449), i.e. every PR after the v5.0.0 tag (6ad6add): the npm
publish fix (5.0.0 never reached npm or crates.io, 5.0.1 is the first
v5 there), #1465, #759, #1460, #1462, #1445, #869, #1447, #1450,
#1467, and #1449 under Maintenance. CI/test-only PRs (#1448, #1452,
#1454, #1457, #1459) have no entries.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chore(release): 5.0.1 notes for PRs merged since the cut

Covers #1469, #1470, #1474, #1475, #1478, #1479, #1480 and #1482; #1484 is
CI-only. The notes now cover main through 285a5e8.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

3 participants