fix(update): stop retired installers and finish on fresh launch

Old updaters keep running after the checkout changes. Returning None
from their removed uv helpers enables a pip fallback against the new tree.
Keep the historical imports as inert shims and stop dependency entrypoints
with a relaunch message instead. Do not call PM or write recovery markers
from that mixed-version process.

Self-managed source launches use PM's successful input stamp to decide
when dependencies need a sync. Restart on the managed interpreter before
activating the selected generation. Preserve launcher forms and options,
and do not sync while a live updater owns the installation.

Targeted runtime batch: 225 passed, 8 platform skips. Real PM worker tests
build and publish disposable dependency generations, retain prior state on
failure, and exercise fresh-process relaunch before dependency activation.
The final launch guard test also passes. Full suite and native Windows
execution were not run locally.
This commit is contained in:
ethernet
2026-09-11 19:45:30 -04:00
parent 528a9414df
commit 529050eab7
28 changed files with 1116 additions and 30 deletions

View File

@@ -114,6 +114,24 @@ _root = Path(__file__).resolve().parent
# Repair needs only stdlib. Do not activate the damaged tree to reach it.
_pm_repair = command_argv(sys.argv[1:])[:2] == ["pm", "repair"]
if not _pm_repair:
from hermes_cli.venv_sync import prepare_launch, relaunch_command
try:
_launch_python = prepare_launch(_root, sys.argv[1:])
if _launch_python is not None:
_main_spec = getattr(sys.modules.get("__main__"), "__spec__", None)
_command = relaunch_command(
_launch_python, _root, sys.argv, sys.orig_argv,
getattr(_main_spec, "name", None),
)
if os.name == "nt":
import subprocess
raise SystemExit(subprocess.call(_command))
os.execv(str(_launch_python), _command)
except Exception as exc:
print(f"hermes: source-update completion failed: {exc}", file=sys.stderr)
raise SystemExit(1) from None
recover_if_needed(_root)
try:
activate_dependencies(_root)