diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..1d0ba9e --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.4.0 diff --git a/backend/app/main.py b/backend/app/main.py index 84a43aa..46b2ceb 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -11,6 +11,7 @@ from __future__ import annotations import logging import sys from contextlib import asynccontextmanager +from pathlib import Path from typing import Any import psycopg @@ -26,6 +27,15 @@ from app.platform.mailer import Mailer from app.platform import session as session_mod +# The repo-root VERSION file is the single version source: the deploy pin checks out its +# tag, and /healthz must report it back (flotilla-core's verify gate compares them). +_REPO_ROOT = Path(__file__).resolve().parents[2] +try: + _APP_VERSION = (_REPO_ROOT / "VERSION").read_text().strip() +except OSError: + _APP_VERSION = "0.0.0" + + class RequestCodeBody(BaseModel): email: str @@ -94,7 +104,7 @@ def create_app(database_url: str | None = None) -> FastAPI: finally: app.state.pool.close() - app = FastAPI(title="ecomm", version="0.3", lifespan=lifespan) + app = FastAPI(title="ecomm", version=_APP_VERSION, lifespan=lifespan) @app.get("/healthz") def healthz(response: Response, conn: psycopg.Connection = Depends(get_conn)): @@ -108,7 +118,7 @@ def create_app(database_url: str | None = None) -> FastAPI: if pending: response.status_code = 503 return {"status": "unavailable", "reason": "migrations_pending"} - return {"status": "ok"} + return {"status": "ok", "version": _APP_VERSION} @app.post("/api/auth/request-code") def request_code( diff --git a/backend/tests/test_bootstrap.py b/backend/tests/test_bootstrap.py index 19ffac6..ec1db53 100644 --- a/backend/tests/test_bootstrap.py +++ b/backend/tests/test_bootstrap.py @@ -12,7 +12,7 @@ def test_inv_1_bootstrap_whole_flow_from_empty(fresh_db_url): # fresh_db_url is a brand-new empty database; create_app() self-migrates (INV-7). with TestClient(create_app(database_url=fresh_db_url)) as client: # a fresh deployment serves healthz green before any row exists - assert client.get("/healthz").json() == {"status": "ok"} + assert client.get("/healthz").json()["status"] == "ok" # PUC-2: first visitor requests a code; it reaches them via the dev channel assert client.post( diff --git a/backend/tests/test_healthz.py b/backend/tests/test_healthz.py index 0dc58ba..c0a8871 100644 --- a/backend/tests/test_healthz.py +++ b/backend/tests/test_healthz.py @@ -1,14 +1,20 @@ +from pathlib import Path + from fastapi.testclient import TestClient from app.main import create_app +# /healthz reports the VERSION file's value — the flotilla deploy gate compares +# body.version against the pinned target (flotilla-core SPEC §8.1 phase 8). +_VERSION = (Path(__file__).resolve().parents[2] / "VERSION").read_text().strip() + def test_healthz_ok_on_migrated_empty_db(fresh_db_url): app = create_app(database_url=fresh_db_url) with TestClient(app) as client: resp = client.get("/healthz") assert resp.status_code == 200 - assert resp.json() == {"status": "ok"} + assert resp.json() == {"status": "ok", "version": _VERSION} def test_startup_migrates_from_empty(fresh_db_url):