chore: import upstream snapshot with attribution
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
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
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user