feat(settings): the instance reports which build it is (318 step 6)
CI / lint (push) Successful in 4s
Build images / sign-extension (push) Successful in 4s
CI / extension-version (push) Successful in 4s
Build images / build-agent (push) Successful in 4s
CI / frontend-build (push) Successful in 21s
extension / lint (push) Successful in 25s
CI / backend-lint-and-test (push) Successful in 32s
Build images / build-ml (push) Successful in 2m50s
Build images / build-web (push) Successful in 2m49s
CI / integration (push) Successful in 3m52s
CI / lint (push) Successful in 4s
Build images / sign-extension (push) Successful in 4s
CI / extension-version (push) Successful in 4s
Build images / build-agent (push) Successful in 4s
CI / frontend-build (push) Successful in 21s
extension / lint (push) Successful in 25s
CI / backend-lint-and-test (push) Successful in 32s
Build images / build-ml (push) Successful in 2m50s
Build images / build-web (push) Successful in 2m49s
CI / integration (push) Successful in 3m52s
A dim line at the foot of Settings: `FabledCurator 2026.08.28.1249 · dev`. This is no longer a convenience. Milestone 318 stopped publishing version image tags, so an instance's own report is the ONLY answer to "which build is this?" — there is no registry name left to check it against. Note #3127 §5 says it directly: a wrong answer here has no second source to contradict it. Three states, kept distinct because collapsing any two of them lies: not asked yet render nothing asked, no version render "unknown" asked, has a version render it A blank footer reads as "no version", which is a different claim from "I cannot say". And a failed health call deliberately does NOT mark the build loaded — a network blip says nothing about the image, and presenting it as "unknown" would look like a defective build. Carried on /api/health rather than a new route: it answers at the same cost (two module constants, no I/O) and TopNav already fetches it app-wide, so a separate endpoint would mean a second request for two strings. Both fields are OMITTED when unset rather than sent empty. Absence already means "cannot say" — an image predating the field says exactly that by not having the key — so a second spelling would make every reader special-case it. The pre-existing test asserting the body is EXACTLY {"status": "ok"} is what keeps a well-meaning `or ""` default from creeping in. FC_CHANNEL now has one definition. It was read from the environment in extension.py and would have been read again here; the new build_info module holds both, and extension.py binds it as a module-level name so existing tests monkeypatch it exactly as before. Separate from config.py on purpose: those are operator settings meant to be changed, these describe the artifact. Channel sits beside the version, never inside it (rule 149), asserted from both ends. A `-dev` suffix would read as a 0 segment to the extension's parseInt comparator and make every dev build compare equal — #2993 exactly. Not hidden, per the operator and §7: the JS bundle and asset hashes fingerprint the build anyway, and "I'm on 2026.08.28.1249" is the single most useful line in a bug report.
This commit is contained in:
@@ -7,13 +7,13 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import hashlib
|
||||
import hmac
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from quart import Blueprint, jsonify, request
|
||||
from sqlalchemy import select
|
||||
|
||||
from ..build_info import FC_CHANNEL as _FC_CHANNEL
|
||||
from ..extensions import get_session
|
||||
from ..models import AppSetting
|
||||
from ..services.extension_service import (
|
||||
@@ -33,10 +33,13 @@ XPI_DIR = Path("/app/frontend/dist/extension")
|
||||
_XPI_VERSION_RE = re.compile(r"fabledcurator-(?P<version>[\w.-]+)\.xpi$")
|
||||
|
||||
# Which channel this image belongs to — "dev" or "main" — baked in at build
|
||||
# time from the FC_CHANNEL build arg (milestone 271 step 7). Empty for a local
|
||||
# build, or for any image predating the field. Tests override by monkeypatching
|
||||
# this constant, same as XPI_DIR above.
|
||||
FC_CHANNEL = os.environ.get("FC_CHANNEL", "").strip()
|
||||
# time (milestone 271 step 7). Read from build_info rather than the environment
|
||||
# a second time: /api/health reports the same value, and two independent
|
||||
# `os.environ.get` calls are two things that can drift.
|
||||
#
|
||||
# Still bound as a module-level name here, so tests monkeypatch
|
||||
# `extension.FC_CHANNEL` exactly as they did before, same as XPI_DIR above.
|
||||
FC_CHANNEL = _FC_CHANNEL
|
||||
|
||||
|
||||
async def _ext_key_required(session) -> bool:
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
"""Health endpoint — no DB or Redis touch; just liveness."""
|
||||
"""Health endpoint — no DB or Redis touch; liveness, plus the build's identity.
|
||||
|
||||
The identity rides here rather than on a route of its own because it answers
|
||||
at the same cost: two module constants, no I/O, nothing that can be slow or
|
||||
fail. It is also already fetched app-wide — TopNav calls `refreshHealth` on
|
||||
mount — so a separate endpoint would mean a second request for two strings.
|
||||
|
||||
Both fields are OMITTED when unset rather than sent empty. See build_info.
|
||||
"""
|
||||
|
||||
from ..build_info import FC_CHANNEL, FC_VERSION
|
||||
|
||||
|
||||
async def get_health():
|
||||
return {"status": "ok"}, 200
|
||||
body = {"status": "ok"}
|
||||
if FC_VERSION:
|
||||
body["version"] = FC_VERSION
|
||||
if FC_CHANNEL:
|
||||
body["channel"] = FC_CHANNEL
|
||||
return body, 200
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
"""What this build IS — stamped at image build time, not configurable.
|
||||
|
||||
Deliberately separate from `config.py`. Those are operator settings, read from
|
||||
the environment and meant to be changed. These describe the artifact itself and
|
||||
are baked in by CI (the `FC_VERSION` / `FC_CHANNEL` build args); an operator
|
||||
setting them by hand is not a supported thing to do, it is just how a value
|
||||
gets from the build into the running process.
|
||||
|
||||
**Absent rather than empty when unknown.** A locally-built image has no version,
|
||||
and neither did any image predating the field — one spelling of "cannot say",
|
||||
which every reader already has to handle, instead of a second one to
|
||||
special-case (note #3127 §7).
|
||||
|
||||
**Why this matters more than it used to.** Milestone 318 stopped publishing
|
||||
version image tags, so a running instance's self-report is now the *only*
|
||||
answer to "which build is this?" — there is no registry name left to check it
|
||||
against. A wrong value here has nothing to contradict it. That is why the UI
|
||||
renders `unknown` rather than a blank or a plausible default: an empty footer
|
||||
reads as "no version", which is a different and false claim.
|
||||
|
||||
The channel lives BESIDE the version and is never folded into it (rule 149).
|
||||
A `-dev` suffix would be parsed by the extension's comparator as a segment
|
||||
worth 0, making every dev build compare equal to every other — issue #2993's
|
||||
exact failure.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
FC_VERSION = os.environ.get("FC_VERSION", "").strip()
|
||||
FC_CHANNEL = os.environ.get("FC_CHANNEL", "").strip()
|
||||
Reference in New Issue
Block a user