Files
hkuds--deeptutor/scripts/docker_compose.py
T
wehub-resource-sync e4dcfc49aa
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:00:43 +08:00

113 lines
3.5 KiB
Python

#!/usr/bin/env python
"""Run Docker Compose with port mappings rendered from JSON settings.
Docker Compose cannot read ``data/user/settings/system.json`` directly for
host port interpolation. This wrapper renders a tiny compose env file from the
JSON settings and then invokes ``docker compose --env-file``. It intentionally
does not read or migrate the project-root ``.env`` file.
"""
from __future__ import annotations
import json
import os
from pathlib import Path
import shutil
import subprocess
import sys
from typing import Any
PROJECT_ROOT = Path(__file__).resolve().parent.parent
SETTINGS_DIR = PROJECT_ROOT / "data" / "user" / "settings"
DOCKER_ENV_PATH = SETTINGS_DIR / "docker.env"
DEFAULT_BACKEND_PORT = 8001
DEFAULT_FRONTEND_PORT = 3782
DEFAULT_POCKETBASE_PORT = 8090
def _read_json_object(path: Path) -> dict[str, Any]:
try:
loaded = json.loads(path.read_text(encoding="utf-8"))
except Exception:
return {}
return loaded if isinstance(loaded, dict) else {}
def _coerce_port(value: Any, default: int) -> int:
try:
port = int(str(value).strip())
except (TypeError, ValueError):
return default
return port if 1 <= port <= 65535 else default
def render_docker_env(
settings_dir: Path = SETTINGS_DIR,
output_path: Path = DOCKER_ENV_PATH,
) -> dict[str, str]:
"""Render compose interpolation vars from JSON settings only."""
system = _read_json_object(settings_dir / "system.json")
integrations = _read_json_object(settings_dir / "integrations.json")
values = {
"DEEPTUTOR_DOCKER_BACKEND_PORT": str(
_coerce_port(system.get("backend_port"), DEFAULT_BACKEND_PORT)
),
"DEEPTUTOR_DOCKER_FRONTEND_PORT": str(
_coerce_port(system.get("frontend_port"), DEFAULT_FRONTEND_PORT)
),
"DEEPTUTOR_DOCKER_POCKETBASE_PORT": str(
_coerce_port(integrations.get("pocketbase_port"), DEFAULT_POCKETBASE_PORT)
),
}
output_path.parent.mkdir(parents=True, exist_ok=True)
lines = [
"# Auto-generated by scripts/docker_compose.py from data/user/settings/*.json.",
"# Do not edit manually; update system.json/integrations.json instead.",
]
lines.extend(f"{key}={value}" for key, value in values.items())
output_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
return values
def _compose_command(args: list[str]) -> list[str]:
docker = shutil.which("docker")
if not docker:
raise SystemExit("docker was not found on PATH")
return [docker, "compose", "--env-file", str(DOCKER_ENV_PATH), *args]
def main(argv: list[str] | None = None) -> int:
args = list(argv if argv is not None else sys.argv[1:])
if not args:
args = ["up", "-d"]
values = render_docker_env()
print(
"Docker settings: "
f"backend={values['DEEPTUTOR_DOCKER_BACKEND_PORT']} "
f"frontend={values['DEEPTUTOR_DOCKER_FRONTEND_PORT']} "
f"pocketbase={values['DEEPTUTOR_DOCKER_POCKETBASE_PORT']}",
file=sys.stderr,
)
env = os.environ.copy()
# Keep Docker execution detached from host process overrides.
for key in (
"BACKEND_PORT",
"FRONTEND_PORT",
"POCKETBASE_PORT",
"AUTH_ENABLED",
"POCKETBASE_URL",
"NEXT_PUBLIC_API_BASE",
"NEXT_PUBLIC_API_BASE_EXTERNAL",
):
env.pop(key, None)
result = subprocess.run(_compose_command(args), cwd=str(PROJECT_ROOT), env=env, check=False)
return int(result.returncode)
if __name__ == "__main__":
raise SystemExit(main())