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.
93 lines
3.8 KiB
Python
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)
|