fix(pm): union sync must install member deps — add --all-packages
Probed live 2026-09-03: plain `uv sync --frozen` installs only the
ROOT project's dependencies — workspace-member deps are locked by
`uv lock` but silently never reach site-packages (a member's
pyfiglet stayed absent under plain --frozen, present under
--all-packages). The union's whole contract is that plugin deps ride
the venv; without the flag every member install is a green-looking
lock over an empty site-packages.
- lock_and_sync now passes --all-packages, with the probe rationale
in place so the flag can never be 'simplified' away.
- _uv_binary() falls back to shutil.which('uv') when the pm store has
no provisioned uv — store-first, PATH-second (the activate()
precedence), fixing 'uv is not installed' lies on dev machines and
test envs that have uv on PATH but no store.
- tests/pm/test_union_installs_members.py: two REAL end-to-end tests
(mini workspace, real uv): member deps land in site-packages after
lock_and_sync, and SURVIVE a second sync (the update-rebuild prune
contract). These fail under the old flagless command by
construction — the probe scenario can never silently return.
Found while porting mnemosyne to the union (the manifest-less pip
route prunes on resync; the union route needs member deps to
actually install). tests/pm: 190 passed, 0 failed.
This commit is contained in:
@@ -297,7 +297,14 @@ def lock_and_sync(
|
||||
if lock.returncode != 0:
|
||||
raise InstallError("venv", f"uv lock exited {lock.returncode}: {lock.stderr[-600:]}")
|
||||
|
||||
cmd = [uv_bin, "sync", "--frozen"]
|
||||
# --all-packages is REQUIRED: plain `uv sync --frozen` installs only the
|
||||
# ROOT project's deps — workspace-member deps are locked by `uv lock`
|
||||
# but silently never reach site-packages (probed live 2026-09-03: a
|
||||
# member's pyfiglet stayed absent from site-packages under plain
|
||||
# --frozen, present under --all-packages). The union's whole contract
|
||||
# is that plugin deps ride the venv; without this flag every member
|
||||
# install is a silent no-op.
|
||||
cmd = [uv_bin, "sync", "--frozen", "--all-packages"]
|
||||
for extra in sorted(set(extras or [])):
|
||||
cmd += ["--extra", extra]
|
||||
sync = subprocess.run(cmd, cwd=str(root), env=env, capture_output=True, text=True)
|
||||
@@ -372,7 +379,14 @@ def _default_venv_dir() -> Path:
|
||||
|
||||
|
||||
def _uv_binary() -> Optional[str]:
|
||||
"""Store uv first (pinned), PATH uv second (dev machines, test envs).
|
||||
A dev machine with uv on PATH but no provisioned store is the normal
|
||||
pre-`pm install` state — 'uv is not installed' there is a lie."""
|
||||
from pm.ensure import uv as pm_uv
|
||||
|
||||
uv_bin, _env = pm_uv(realize=False)
|
||||
return uv_bin
|
||||
if uv_bin:
|
||||
return uv_bin
|
||||
import shutil
|
||||
|
||||
return shutil.which("uv")
|
||||
|
||||
Reference in New Issue
Block a user