mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-18 06:13:54 +00:00
fix: make a native install take effect on systemd and stay out of Docker's way
From review of #2800: - systemd `enable --now` starts nothing when the unit is already active, so a second `up --native` kept the old ExecStart and left the API on its previous port. start enables and then restarts, as the launchd path already did by booting the job out first. - An explicit `home` now wins over XDG_CONFIG_HOME, which is what callers pass it for. - The ExecStart program must be a real executable: when `docsgpt` is not on PATH, sys.argv[0] is accepted only if it can be run, and otherwise the failure is raised before any unit is written. - `up --native` over a directory holding a Docker install now refuses and says how to proceed, instead of starting native services beside containers that down, status and uninstall would no longer see. - Docs: without a terminal only --postgres-uri is required, and the Windows fallback names `docsgpt beat`, which the worker cannot embed there. SystemdServices was the least covered part of the module and cannot be run on this machine, so it now has tests for install, start, stop, remove, is_running and a failing systemctl.
This commit is contained in:
1 parent
e2c40f7a78
commit
f9f52e99f0
4 files changed
+130
-6
No files matched your search
@@ -218,6 +218,17 @@ def _redis_urls(base: str) -> dict[str, str]:
|
||||
}
|
||||
|
||||
|
||||
def _own_executable() -> str:
|
||||
"""The docsgpt command to put in the service files, or a DeployError naming what to install."""
|
||||
candidate = Path(sys.argv[0]).resolve()
|
||||
if candidate.is_file() and os.access(candidate, os.X_OK):
|
||||
return str(candidate)
|
||||
raise DeployError(
|
||||
"the docsgpt command is not on PATH, so the services would have nothing to run. "
|
||||
"Install it with `uv tool install docsgpt` (or `pip install docsgpt`) and run this again."
|
||||
)
|
||||
|
||||
|
||||
def _native_up(args, context: Context, directory: Path) -> int:
|
||||
"""Run the API and the worker as services on this machine, against an existing Postgres and Redis."""
|
||||
services = context.service_manager()
|
||||
@@ -263,7 +274,7 @@ def _native_up(args, context: Context, directory: Path) -> int:
|
||||
(directory / "logs").mkdir(exist_ok=True)
|
||||
envfile.update(env_path, updates)
|
||||
|
||||
executable = shutil.which("docsgpt") or sys.argv[0]
|
||||
executable = shutil.which("docsgpt") or _own_executable()
|
||||
print("Applying database migrations ...")
|
||||
# The child reads the stack's settings, not a .env in whatever directory this was run from.
|
||||
stack_env = {"DOCSGPT_HOME": str(directory), "DOCSGPT_ENV_FILE": str(env_path)}
|
||||
@@ -303,6 +314,13 @@ def up(args, context: Optional[Context] = None) -> int:
|
||||
context = context or Context.default(args)
|
||||
directory = stack.stack_dir(args.dir)
|
||||
if getattr(args, "native", False) or _mode(directory) == "native":
|
||||
if _mode(directory) != "native" and (directory / stack.COMPOSE_FILE).is_file():
|
||||
raise DeployError(
|
||||
f"{directory} holds a Docker install. Native services would run beside its containers "
|
||||
"on the same port, and down, status and uninstall would stop seeing them. Stop it "
|
||||
"first with `docsgpt down` (and `docsgpt uninstall` to remove it), or pass --dir to "
|
||||
"put the native install somewhere else."
|
||||
)
|
||||
return _native_up(args, context, directory)
|
||||
env_path = directory / ".env"
|
||||
record_path = directory / stack.RECORD_FILE
|
||||
|
||||
@@ -152,8 +152,12 @@ class SystemdServices:
|
||||
|
||||
def __init__(self, runner=subprocess.run, home: Optional[Path] = None) -> None:
|
||||
self._run = runner
|
||||
base = os.environ.get("XDG_CONFIG_HOME")
|
||||
root = Path(base) if base else (home or Path.home()) / ".config"
|
||||
# An explicit home wins: it is what a caller passes to redirect the unit directory.
|
||||
if home is not None:
|
||||
root = home / ".config"
|
||||
else:
|
||||
base = os.environ.get("XDG_CONFIG_HOME")
|
||||
root = Path(base) if base else Path.home() / ".config"
|
||||
self.directory = root / "systemd" / "user"
|
||||
|
||||
def _unit_file(self, name: str) -> Path:
|
||||
@@ -171,7 +175,10 @@ class SystemdServices:
|
||||
self._systemctl("daemon-reload")
|
||||
|
||||
def start(self, name: str) -> None:
|
||||
self._systemctl("enable", "--now", f"{name}.service", check=True)
|
||||
# restart, not `enable --now`: --now starts nothing when the unit is already active, so a
|
||||
# reinstalled unit would keep running with the ExecStart and environment it started with.
|
||||
self._systemctl("enable", f"{name}.service", check=True)
|
||||
self._systemctl("restart", f"{name}.service", check=True)
|
||||
|
||||
def stop(self, name: str) -> None:
|
||||
self._systemctl("stop", f"{name}.service")
|
||||
|
||||
Reference in new issue
Block a user