fix(docker): the image's gateway run keeps the root profile, and a leading -p reaches hermes
Inside the s6 image a bare `gateway run` (the image's CMD) redirects to the supervised slot of the current profile. The profile pre-parse applied the sticky active_profile first, so after `hermes profile use <name>` (or a dashboard profile switch) every container boot started that named slot: the one the boot reconciler had just registered down, because a started named slot is a second gateway beside the multiplexer. The redirected run now keeps the root identity like any supervised slot (#74872); --no-supervise keeps the foreground behaviour that follows active_profile. The obvious workaround, pinning the CMD to `-p default gateway run`, restart-looped the container: main-wrapper.sh probes `command -v "$1"`, and `command -v -p` parses -p as an option to `command` and succeeds, so the wrapper exec'd "-p". A leading flag now always goes to hermes.
This commit is contained in:
committed by
Teknium
parent
1769024ca3
commit
bd112bc8b3
@@ -82,10 +82,18 @@ if [ $# -eq 0 ]; then
|
||||
drop hermes
|
||||
fi
|
||||
|
||||
if command -v "$1" >/dev/null 2>&1; then
|
||||
# Bare executable — pass through directly.
|
||||
drop "$@"
|
||||
fi
|
||||
# A leading flag is a hermes global option (`-p <profile> gateway run`), never an executable:
|
||||
# `command -v -p` parses -p as an option to `command` itself and succeeds, so the wrapper exec'd
|
||||
# "-p" and the container restart-looped.
|
||||
case "$1" in
|
||||
-*) ;;
|
||||
*)
|
||||
if command -v "$1" >/dev/null 2>&1; then
|
||||
# Bare executable — pass through directly.
|
||||
drop "$@"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# Hermes subcommand pass-through.
|
||||
drop hermes "$@"
|
||||
|
||||
@@ -577,6 +577,24 @@ def _desktop_ssh_backend(argv: list) -> bool:
|
||||
return "--ssh-session-token-file" in argv
|
||||
|
||||
|
||||
def _s6_supervised_gateway_run(argv: list) -> bool:
|
||||
"""A bare ``gateway run`` inside the s6 image names the ``gateway-default`` slot too.
|
||||
|
||||
``_maybe_redirect_run_to_s6_supervision`` turns it into a start of the supervised slot for the
|
||||
current profile, and it is the image's own CMD. Following the sticky ``active_profile`` there
|
||||
started that profile's named slot on every container boot: the one the boot reconciler just
|
||||
registered down, because a started named slot is a second gateway beside the multiplexer.
|
||||
``--no-supervise`` keeps the foreground run, which follows ``active_profile`` as before (#22502).
|
||||
"""
|
||||
words = [a for a in argv if not a.startswith("-")]
|
||||
if words[:2] != ["gateway", "run"] or "--no-supervise" in argv:
|
||||
return False
|
||||
if os.environ.get("HERMES_GATEWAY_NO_SUPERVISE", "").lower() in ("1", "true", "yes"):
|
||||
return False
|
||||
from hermes_cli.service_manager import _s6_running
|
||||
return _s6_running()
|
||||
|
||||
|
||||
def _apply_profile_override() -> None:
|
||||
"""Pre-parse --profile/-p and set HERMES_HOME before imports."""
|
||||
argv = sys.argv[1:]
|
||||
@@ -596,7 +614,8 @@ def _apply_profile_override() -> None:
|
||||
if profile_name is None and hermes_home_env and os.environ.get("HERMES_UPDATE_POST_SWAP") == "1":
|
||||
return
|
||||
|
||||
if profile_name is None and not _under_gateway_supervisor(argv) and not _desktop_ssh_backend(argv):
|
||||
if (profile_name is None and not _under_gateway_supervisor(argv) and not _desktop_ssh_backend(argv)
|
||||
and not _s6_supervised_gateway_run(argv)):
|
||||
try:
|
||||
from hermes_constants import get_default_hermes_root
|
||||
|
||||
|
||||
@@ -307,3 +307,31 @@ class TestGeneralizedSupervisorMarkers:
|
||||
|
||||
plist = generate_launchd_plist()
|
||||
assert "<key>HERMES_SUPERVISED_CHILD</key>" in plist
|
||||
|
||||
|
||||
class TestS6ContainerGatewayRun:
|
||||
"""Inside the s6 image a bare ``gateway run`` (the image's CMD) redirects to the supervised
|
||||
``gateway-default`` slot. It must keep that root identity whatever ``active_profile`` says;
|
||||
otherwise every container boot starts the named slot the reconciler registered down."""
|
||||
|
||||
def test_the_redirected_run_keeps_the_root_home_despite_the_active_profile(
|
||||
self, tmp_path, monkeypatch
|
||||
):
|
||||
monkeypatch.setattr("hermes_cli.service_manager._s6_running", lambda: True)
|
||||
root = tmp_path / ".hermes"
|
||||
result = _run_apply_profile_override(
|
||||
tmp_path, monkeypatch, hermes_home=str(root), active_profile="coder",
|
||||
argv=["hermes", "gateway", "run"],
|
||||
)
|
||||
assert result == str(root)
|
||||
|
||||
def test_a_foreground_run_and_other_verbs_still_follow_the_active_profile(
|
||||
self, tmp_path, monkeypatch
|
||||
):
|
||||
monkeypatch.setattr("hermes_cli.service_manager._s6_running", lambda: True)
|
||||
root = tmp_path / ".hermes"
|
||||
for argv in (["hermes", "gateway", "run", "--no-supervise"], ["hermes", "chat"]):
|
||||
result = _run_apply_profile_override(
|
||||
tmp_path, monkeypatch, hermes_home=str(root), active_profile="coder", argv=argv,
|
||||
)
|
||||
assert result == str(root / "profiles" / "coder"), argv
|
||||
|
||||
Reference in New Issue
Block a user