diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..1edfae5 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.01-alpha diff --git a/api/app/main.py b/api/app/main.py index 9503394..4d37612 100644 --- a/api/app/main.py +++ b/api/app/main.py @@ -17,8 +17,9 @@ from .canon_log import validate_canon_log from .narrate import run as narrate_run from .npc import UnknownNpc, run as npc_run from .ollama_client import ModelError +from .version import VERSION -app = FastAPI(title="coc-rpg proxy", version="0.0.1") +app = FastAPI(title="coc-rpg proxy", version=VERSION) @app.exception_handler(RequestValidationError) @@ -65,6 +66,11 @@ def health() -> dict: return {"status": "ok"} +@app.get("/version") +def version() -> dict: + return {"version": VERSION} + + # ── Role endpoints (charter §4) ────────────────────────────────────────────── # The client knows these paths and nothing about which model or prompt serves # them. Bodies are validated against the canon log contract. /dm/narrate is diff --git a/api/app/version.py b/api/app/version.py new file mode 100644 index 0000000..7cf8c5b --- /dev/null +++ b/api/app/version.py @@ -0,0 +1,33 @@ +"""Single source of truth for the app version (charter §4: client and API share +one version). Reads the repo-root /VERSION file — the ONLY place a human edits the +version. Mirrors canon_log._schema_dir's env-override + walk-up so it resolves in +a local checkout and (once /VERSION is bundled) an image alike. +""" +import os +from functools import lru_cache +from pathlib import Path + +LAST_RESORT = "0.0.0-dev" + + +def _version_file() -> Path | None: + env = os.environ.get("COC_VERSION_FILE") + if env: + return Path(env) + here = Path(__file__).resolve() + for parent in here.parents: + candidate = parent / "VERSION" + if candidate.is_file(): + return candidate + return None + + +@lru_cache(maxsize=1) +def get_version() -> str: + path = _version_file() + if path is None or not path.is_file(): + return LAST_RESORT + return path.read_text(encoding="utf-8").strip() or LAST_RESORT + + +VERSION = get_version() diff --git a/api/tests/test_version.py b/api/tests/test_version.py new file mode 100644 index 0000000..88eb121 --- /dev/null +++ b/api/tests/test_version.py @@ -0,0 +1,22 @@ +from pathlib import Path + +from fastapi.testclient import TestClient + +from app.main import app +from app.version import VERSION, get_version + +client = TestClient(app) +ROOT_VERSION = Path(__file__).resolve().parents[2] / "VERSION" + + +def test_get_version_matches_file(): + assert ROOT_VERSION.is_file() + assert get_version() == ROOT_VERSION.read_text(encoding="utf-8").strip() + + +def test_app_version_matches(): + assert app.version == VERSION == get_version() + + +def test_version_endpoint(): + assert client.get("/version").json() == {"version": VERSION}