Contributors had to run `python -m pm.build_env --source . --lock-only`, then re-source activate, then call sync_venv for opt-in extras, while `hermes pm lock` only pinned tool artifacts in pm/lock.json. Now `hermes pm lock` with no arguments is the one step: it runs the same PM check_project_lock / lock_project operations as build_env's --check-lock / --lock-only (same exclude-newer quarantine), writes nothing when the lock is current, changes no environment, and prints the activate command to run next. Activation syncs [all] plus recorded extras only, and --test-extras replaces the default, so a newly added extra outside [all] gets `source ./activate --test-extras all,NAME` (`-TestExtras` on PowerShell). `hermes pm lock --bump NAME VERSION` keeps writing pm/lock.json; NAME and VERSION are now only accepted together. build_env --lock-only is unchanged for scripts. Contributor docs, AGENTS.md, CONTRIBUTING.es.md, the pyproject comment, and the uv.lock CI remediation text point at `hermes pm lock`.
63 lines
1.9 KiB
Python
63 lines
1.9 KiB
Python
"""`hermes pm lock` with no arguments relocks uv.lock through PM and nothing else."""
|
|
from __future__ import annotations
|
|
|
|
from argparse import Namespace
|
|
|
|
import pm
|
|
from pm import cli
|
|
from pm.package import InstallError
|
|
|
|
PYPROJECT = """\
|
|
[project]
|
|
name = "demo"
|
|
version = "0"
|
|
|
|
[project.optional-dependencies]
|
|
alpha = ["six"]
|
|
beta = ["idna"]
|
|
all = ["demo[alpha]"]
|
|
"""
|
|
LOCK = """\
|
|
version = 1
|
|
|
|
[[package]]
|
|
name = "demo"
|
|
version = "0"
|
|
source = { editable = "." }
|
|
|
|
[package.metadata]
|
|
provides-extras = ["alpha", "all"]
|
|
"""
|
|
|
|
|
|
def test_relock_writes_nothing_when_current_and_names_new_opt_in_extras(tmp_path, monkeypatch, capsys):
|
|
(tmp_path / "pyproject.toml").write_text(PYPROJECT, encoding="utf-8")
|
|
(tmp_path / "uv.lock").write_text(LOCK, encoding="utf-8")
|
|
monkeypatch.setattr(cli, "repo_root", lambda: tmp_path)
|
|
stale = []
|
|
relocks = []
|
|
|
|
def check(source, **kw):
|
|
assert (source, kw.get("explicit")) == (tmp_path, True)
|
|
if stale:
|
|
raise InstallError("venv", "The lockfile at `uv.lock` needs to be updated")
|
|
|
|
monkeypatch.setattr(pm, "check_project_lock", check)
|
|
monkeypatch.setattr(pm, "lock_project", lambda source, **kw: relocks.append((source, kw)))
|
|
monkeypatch.setattr(pm, "sync_venv", lambda *a, **kw: relocks.append("sync"))
|
|
|
|
before = (tmp_path / "uv.lock").read_bytes()
|
|
assert cli.cmd_lock(Namespace(name=None, version=None)) == 0
|
|
assert relocks == []
|
|
assert (tmp_path / "uv.lock").read_bytes() == before
|
|
assert "already current" in capsys.readouterr().out
|
|
|
|
stale.append(True)
|
|
assert cli.cmd_lock(Namespace(name=None, version=None)) == 0
|
|
# Relocking never syncs an environment; activation owns that.
|
|
assert relocks == [(tmp_path, {"explicit": True})]
|
|
out = capsys.readouterr().out
|
|
assert "activate" in out
|
|
# beta is new and outside [all]; alpha was already locked and is in [all].
|
|
assert "all,beta" in out and "alpha" not in out
|