fix(pm): keep a workspace member's own uv.lock in the build snapshot
`_copy_core_inputs` listed `uv.lock` in the names its copytree `ignore`
drops, and copytree applies that callback at every depth, so the member
lock `pm/uv.lock` never reached the generation workspace.
`pm/runtime.py::_inputs()` hashes `<workspace>/pm/{pyproject.toml,uv.lock}`
to key the PM runtime, so every `hermes pm` command run by the managed
environment's `hermes` died at preflight:
FileNotFoundError: .../workspace/pm/uv.lock
Drop `uv.lock` from the exclusion set. The root lock is unaffected: the
root pass copies only the explicit `files` set (never `uv.lock`), and
`lock_and_sync` seeds or resolves the root lock itself.
Refs #124075
This commit is contained in:
@@ -82,7 +82,9 @@ def _copy_core_inputs(source: Path, destination: Path) -> None:
|
||||
files.update(str(p.relative_to(source)) for p in source.glob(pattern))
|
||||
files.update(p.name for p in source.glob("*.py"))
|
||||
|
||||
excluded = {".git", ".venv", "venv", "node_modules", "__pycache__", "build", "dist", "release", "uv.lock"}
|
||||
# uv.lock is not excluded: the root lock is never copied (only ``files`` are; lock_and_sync
|
||||
# seeds or resolves it), and pm/uv.lock is the PM runtime's input (pm/runtime.py::_inputs).
|
||||
excluded = {".git", ".venv", "venv", "node_modules", "__pycache__", "build", "dist", "release"}
|
||||
def ignore(directory, names):
|
||||
return [name for name in names if name in excluded or name.startswith(".")
|
||||
or name.endswith(".egg-info") or (Path(directory) / name).is_symlink()]
|
||||
|
||||
@@ -240,3 +240,24 @@ def test_plugin_can_move_compatible_transitive_but_not_exact_requirement(tmp_pat
|
||||
capture_output=True, text=True, check=True, timeout=30)
|
||||
assert result.stdout.strip() == "1.0 1.3"
|
||||
assert (baseline / "uv.lock").read_bytes() == first_lock
|
||||
|
||||
|
||||
def test_member_uv_lock_travels_with_its_member(tmp_path):
|
||||
"""pm/runtime.py::_inputs reads <workspace>/pm/uv.lock; the snapshot must carry it."""
|
||||
core = tmp_path / "core"
|
||||
member = core / "pm"
|
||||
member.mkdir(parents=True)
|
||||
(core / "pyproject.toml").write_text(
|
||||
'[project]\nname="core"\nversion="1"\nrequires-python=">=3.11"\n'
|
||||
'[tool.setuptools.packages.find]\ninclude=["pm"]\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
(core / "uv.lock").write_text("version = 1\n# root lock\n", encoding="utf-8")
|
||||
(member / "pyproject.toml").write_text('[project]\nname="pm"\nversion="1"\n', encoding="utf-8")
|
||||
(member / "uv.lock").write_text("version = 1\n# member lock\n", encoding="utf-8")
|
||||
|
||||
destination = tmp_path / "stage"
|
||||
workspace._copy_core_inputs(core, destination)
|
||||
|
||||
assert (destination / "pm/uv.lock").read_text(encoding="utf-8") == "version = 1\n# member lock\n"
|
||||
assert not (destination / "uv.lock").exists(), "the root lock is seeded by lock_and_sync, not copied"
|
||||
|
||||
Reference in New Issue
Block a user