From e79877a99d9776c2711516e0ff4bad945a483136 Mon Sep 17 00:00:00 2001 From: pcvantol Date: Mon, 7 Sep 2026 07:50:40 +0200 Subject: [PATCH] Project LaunchAgent start settings in component details --- src/engineering_platform/server.py | 23 +++++++++++++++++++++ tests/engineering/test_server_foundation.py | 22 ++++++++++++++++---- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/engineering_platform/server.py b/src/engineering_platform/server.py index da8ceadd..ad32ce39 100644 --- a/src/engineering_platform/server.py +++ b/src/engineering_platform/server.py @@ -15,6 +15,7 @@ import logging import os from pathlib import Path +import plistlib import re import select import shlex @@ -1289,6 +1290,24 @@ def _dashboard_relay_component(*, server_running: bool) -> dict[str, object]: } +def _launch_agent_configuration(plist_path: Path) -> dict[str, object]: + """Project the safe start policy from one EP-owned LaunchAgent plist.""" + try: + payload = plistlib.loads(plist_path.read_bytes()) + except (OSError, plistlib.InvalidFileException): + return {} + if not isinstance(payload, dict): + return {} + configuration: dict[str, object] = {} + run_at_load = payload.get("RunAtLoad") + if isinstance(run_at_load, bool): + configuration["run_at_load"] = run_at_load + keep_alive = payload.get("KeepAlive") + if isinstance(keep_alive, (bool, dict)): + configuration["keep_alive"] = bool(keep_alive) + return configuration + + def _platform_component_detail(data_root: Path, component_id: str) -> dict[str, object] | None: """Expose one secret-free detail view from the same platform projection.""" component = status(data_root)["components"].get(component_id) # type: ignore[index] @@ -1336,6 +1355,10 @@ def _platform_component_detail(data_root: Path, component_id: str) -> dict[str, "active": observed.active, "pid": observed.pid, "last_exit_code": observed.last_exit_code, + **( + _launch_agent_configuration(Path(installation["launch_agent_path"])) + if isinstance(installation.get("launch_agent_path"), str) else {} + ), } detail["process_state"] = "OWNED_PROCESS" detail["processes"] = ([{ diff --git a/tests/engineering/test_server_foundation.py b/tests/engineering/test_server_foundation.py index 0a2584c1..f20ac66a 100644 --- a/tests/engineering/test_server_foundation.py +++ b/tests/engineering/test_server_foundation.py @@ -4,6 +4,7 @@ import inspect import io import os +import plistlib import re import subprocess import sys @@ -83,13 +84,16 @@ def test_service_components_expose_their_launchagent_as_host_detail(self, launch LaunchdRuntimeDetails("com.engineeringplatform.server", True, True, 321, "(never exited)", 2048, 61), ) - ep_server = server._platform_component_detail(self.root, "ep_server") - relay = server._platform_component_detail(self.root, "dashboard_relay") - worker = server._platform_component_detail(self.root, "lifecycle_worker") + with patch("engineering_platform.server._launch_agent_configuration", return_value={ + "run_at_load": True, "keep_alive": True, + }): + ep_server = server._platform_component_detail(self.root, "ep_server") + relay = server._platform_component_detail(self.root, "dashboard_relay") + worker = server._platform_component_detail(self.root, "lifecycle_worker") self.assertEqual(ep_server["launchd"], { "label": "com.engineeringplatform.server", "loaded": True, "active": True, - "pid": 321, "last_exit_code": "(never exited)", + "pid": 321, "last_exit_code": "(never exited)", "run_at_load": True, "keep_alive": True, }) self.assertEqual(relay["launchd"]["pid"], 654) self.assertEqual(relay["launchd"]["last_exit_code"], "1") @@ -100,6 +104,16 @@ def test_service_components_expose_their_launchagent_as_host_detail(self, launch self.assertEqual(worker["process_state"], "IN_PROCESS") self.assertEqual(worker["process_host"], {"component": "ep_server", "pid": 321}) + def test_launchagent_configuration_projects_boolean_and_dictionary_policies(self) -> None: + plist_path = self.root / "agent.plist" + plist_path.parent.mkdir(parents=True) + plist_path.write_bytes(plistlib.dumps({"RunAtLoad": True, "KeepAlive": {"SuccessfulExit": False}})) + + self.assertEqual(server._launch_agent_configuration(plist_path), { + "run_at_load": True, + "keep_alive": True, + }) + def test_register_topology_cli_reads_the_versioned_json_declaration(self) -> None: declaration_path = Path(self.temporary.name) / "repository.json" declaration_path.write_text(json.dumps({