+ {{ config.version || "unknown" }} +
diff --git a/src/thoughtsync/__init__.py b/src/thoughtsync/__init__.py index 0c2c273..0148171 100644 --- a/src/thoughtsync/__init__.py +++ b/src/thoughtsync/__init__.py @@ -1,10 +1,16 @@ """ThoughtSync — self-hosted personal thought-capture web app (FabledSword family).""" -# The FALLBACK version, used only when APP_VERSION is absent from the environment — -# i.e. running from a checkout rather than from an image. A built image always has -# it, derived from the server's own shipped file set (packaging/version.sh), so this -# string never reaches a deployed instance and bumping it changes nothing a user -# sees. Kept because a package needs a version and "unknown" is not a valid one for -# packaging metadata; the honest "I cannot say" for a running server is APP_VERSION -# being missing, which app.py already handles. +# PACKAGING METADATA, and nothing else. Not the version any running server reports. +# +# A built image carries APP_VERSION in the environment, derived from the server's +# own shipped file set (packaging/version.sh); `app.py` reads that and reports an +# explicit "unknown" when it is absent, so this string never reaches a user and +# bumping it changes nothing anybody sees. +# +# It exists because a Python package needs a version and "unknown" is not a legal +# one here. It used to double as app.py's fallback, which meant a server run from a +# checkout confidently reported `0.2.0` — a real-looking version naming no build +# that exists. Note 3127 §5 is why that matters more than it reads: with version +# tags gone, a build's self-report is the only answer to "which build is this?", +# and there is nothing left to catch it lying. __version__ = "0.2.0" diff --git a/src/thoughtsync/app.py b/src/thoughtsync/app.py index 8c655d9..0809c59 100644 --- a/src/thoughtsync/app.py +++ b/src/thoughtsync/app.py @@ -11,7 +11,6 @@ from datetime import timedelta from quart import Quart, jsonify, send_from_directory from quart.sessions import SecureCookieSessionInterface -from . import __version__ from .auth import bp as auth_bp from .client_dist import advertisement as client_advertisement, bp as client_bp from .config import Config @@ -64,7 +63,20 @@ def create_app() -> Quart: # Ephemeral/env secret so the app (and DB-free unit tests) construct without a # database. before_serving swaps in the real, DB-persisted key before serving. app.secret_key = Config.secret_key_env() or secrets.token_urlsafe(48) - app.config["APP_VERSION"] = os.environ.get("APP_VERSION", __version__) + # The RUNNING build, or an explicit "unknown" — never the packaging fallback. + # + # This read `os.environ.get("APP_VERSION", __version__)`, so a server started + # from a checkout reported `0.2.0`: a real-looking version that names no build + # anybody could get. `__init__.py` already claimed the honest answer was + # "APP_VERSION being missing, which app.py already handles" — it did not, and a + # comment asserting a behaviour two files away from the code is how that stayed + # true-sounding for months. + # + # It matters more than it used to. Note 3127 §5 removed version tags, so this + # string is the only answer to "which build is this?" and nothing exists to + # contradict it when it is wrong. `__version__` stays where it belongs, as + # packaging metadata, which is the one place "unknown" is not a legal value. + app.config["APP_VERSION"] = os.environ.get("APP_VERSION") or "unknown" app.config["SESSION_COOKIE_HTTPONLY"] = True app.config["SESSION_COOKIE_SAMESITE"] = "Lax" # Auto-mark the session cookie Secure on HTTPS requests (see the interface above). diff --git a/tests/test_app.py b/tests/test_app.py index 942563f..e9f95ec 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -27,3 +27,50 @@ async def test_unknown_api_route_404s(app): client = app.test_client() resp = await client.get("/api/does-not-exist") assert resp.status_code == 404 + + +# --- the version a running server reports ------------------------------------ +# +# Note 3127 §5 removed version tags, so this string is the only answer to "which +# build is this?" and nothing exists to contradict it when it is wrong. That makes +# the FALLBACK the interesting case rather than the happy path: it used to be +# `__version__`, so a server run from a checkout reported `0.2.0` — a real-looking +# version naming no build anybody could obtain. + + +async def reported_version() -> str: + """What a freshly built app tells /api/health it is. + + Built per call rather than through the `app` fixture: the value is read from the + environment in `create_app`, so an app constructed before `monkeypatch` ran would + answer about the wrong environment. + """ + client = create_app().test_client() + return (await (await client.get("/api/health")).get_json())["version"] + + +async def test_the_version_is_whatever_the_environment_says(monkeypatch): + monkeypatch.setenv("APP_VERSION", "2026.08.29.0443") + assert await reported_version() == "2026.08.29.0443" + + +async def test_no_version_in_the_environment_reports_unknown(monkeypatch): + """The honest "I cannot say", not a plausible default. + + Also asserted against `__version__` by name rather than against the literal it + happens to hold, so bumping the packaging version cannot make this pass for the + wrong reason. + """ + from thoughtsync import __version__ + + monkeypatch.delenv("APP_VERSION", raising=False) + reported = await reported_version() + assert reported == "unknown" + assert reported != __version__ + + +async def test_an_empty_version_reports_unknown_too(monkeypatch): + """`APP_VERSION=` is what a mis-set build arg looks like, and an empty string + renders as a blank space rather than as a missing value.""" + monkeypatch.setenv("APP_VERSION", "") + assert await reported_version() == "unknown"