Files
hermes-agent/scripts/_activation.py
ethernet 1ec55baea8 refactor(dev): drop the activation demo; keep only the invariant tests
The demo was scaffolding, not repo content: nothing referenced it but a
docstring.

The tests were 12 against the repo's 1-2 invariant bar. Keep the two that fail
silently when inverted -- the sentinel reaching an exec'd child (the unexported
variable this change fixes) and the prologue's staleness gate, which inverted
either way costs a re-sync per run or a stale environment that looks fine. The
per-OS command pair stays because neither host can observe the other's string.
Dropped the exit-code/message restatement, the no-sentinel cold path, and the
demo-driven harness.
2026-09-16 23:45:47 -04:00

93 lines
3.8 KiB
Python

"""Activation guard for repository scripts.
Every script in this checkout assumes it runs under the PM-activated
environment (``source ./activate`` on POSIX, ``. .\\activate.ps1`` on Windows),
which is what puts the pinned interpreter and its dependency tree on
``PATH``/``PYTHONPATH``. A script run with a bare system Python instead fails
much later with a confusing ``ModuleNotFoundError``. Call
:func:`require_activation` first and it fails immediately, naming the exact
command for the caller's shell.
Import it by its bare name: ``scripts/`` is the running script's own directory,
so it is on ``sys.path`` whether or not the shell is activated::
from _activation import require_activation
require_activation()
This module must stay stdlib-only and side-effect free at import — it has to
load before the environment it checks for exists.
Activating from the shebang
---------------------------
A POSIX script can activate for itself, so ``./scripts/foo.py`` works from any
cwd with no manual ``source``. The shebang points at ``scripts/_hermes-python``,
a real repo script that sources ``activate`` and execs the interpreter on the
same file::
#!/usr/bin/env -S bash -c 'exec "$BASH" "$(dirname "$0")/_hermes-python" "$0" "$@"'
The kernel appends the invoking script as the last argument, and ``bash -c``
binds it to ``$0`` — so the prologue finds the target relative to itself and
the shebang stays independent of the cwd. ``$BASH`` for the second hop keeps it
free of both the exec bit and a ``PATH`` lookup.
Activation is not re-run when the inherited environment is still current: the
sentinel's value is the installed-state file the environment was composed
against (see ``hermes_cli.runtime_paths.activation_environment``), so the
prologue compares it against the inputs that decide the dependency set. Any of
``uv.lock``, ``pyproject.toml`` or ``pm/lock.json`` being newer than that file
means the inherited environment predates its inputs, and the prologue
re-activates once. ``[ -nt ]`` is a bash builtin, so the check costs no process
spawn; pm rewrites that file on every real sync and no-ops otherwise, so it
settles back to current rather than re-syncing on every run.
Two constraints worth knowing: the line is 83 bytes (a shebang must stay under
127), and ``/usr/bin/env -S`` is GNU and newer-BSD — verify it if an older
macOS or BSD is a target. Windows keeps using ``python scripts\\foo.py`` with
the guard.
"""
from __future__ import annotations
import os
import sys
ACTIVATION_ENV_VAR = "__HERMES_ACTIVATED"
POSIX_COMMAND = "source ./activate"
WINDOWS_COMMAND = ". .\\activate.ps1"
def activation_command() -> str:
"""The exact command that activates this checkout in the caller's shell."""
if os.name != "nt":
return POSIX_COMMAND
# A bash-family shell on Windows (Git Bash, MSYS, WSL interop) sources the
# POSIX script; only a native host gets the PowerShell one.
if os.environ.get("MSYSTEM") or os.path.basename(os.environ.get("SHELL", "")) in {"bash", "sh", "zsh"}:
return POSIX_COMMAND
return WINDOWS_COMMAND
def require_activation() -> None:
"""Exit the process unless the launching shell sourced the activate script.
Only tests the sentinel for non-emptiness: its value is the installed-state
path the environment was built from (and earlier revisions wrote a bare
``1``), so any value means "an ancestor shell activated".
"""
if os.environ.get(ACTIVATION_ENV_VAR):
return
script = os.path.basename(sys.argv[0]) or "this script"
print(
f"{script}: the Hermes environment is not activated.\n"
"From the repository root, run:\n"
"\n"
f" {activation_command()}\n"
"\n"
"then re-run this script.",
file=sys.stderr,
)
raise SystemExit(1)