Point email links at conchecker.scoutitsystems.com and show build SHA in header.
Docker Release / build-and-push (push) Successful in 58s
Docker Release / release (push) Skipped

APP_BASE_URL default (config, .env.example, both compose files) is now
https://conchecker.scoutitsystems.com with no port, so review-required
and final-report email links use the public site. CI bakes the short
commit SHA into the image as APP_BUILD via a Docker build-arg; /health
returns version+build and the site header shows the build so it's easy
to confirm which image is deployed. Local runs default to 'dev'.
This commit is contained in:
John Wilganowski
2026-07-28 20:34:33 +00:00
parent 1c1d2ff21b
commit 4ecc7c5cef
9 changed files with 42 additions and 5 deletions
+2
View File
@@ -63,6 +63,8 @@ jobs:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
build-args: |
APP_BUILD=sha-${{ steps.meta.outputs.short_sha }}
release:
needs: build-and-push
+4
View File
@@ -19,6 +19,10 @@ COPY cli cli
RUN mkdir -p backend/uploads backend/outputs backend/.llm_cache
ENV PYTHONUNBUFFERED=1
# Build identifier baked in by CI (sha-<short_sha>, matches the image tag);
# defaults to "dev" for local builds. Surfaced in /health and the site header.
ARG APP_BUILD=dev
ENV APP_BUILD=${APP_BUILD}
EXPOSE 8099
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
+2 -1
View File
@@ -45,7 +45,8 @@ EXTRACT_CONCURRENCY=4
REASON_CONCURRENCY=4
# Public URL users reach this server on (used for the link in result emails)
APP_BASE_URL=http://localhost:8099
APP_BASE_URL=https://conchecker.scoutitsystems.com
# APP_BUILD is set by CI at image build time (sha-<short_sha>) - do not set manually.
# Email notifications (optional). Leave SMTP_HOST blank to disable.
# Examples:
+4 -1
View File
@@ -123,7 +123,10 @@ APP_VERSION = "0.1.0"
# Public base URL used to build the "view results" link in notification
# emails. Set to whatever address users reach this server on (e.g. the
# Tailscale/LAN URL) so the link in the email actually resolves.
APP_BASE_URL = os.getenv("APP_BASE_URL", "http://localhost:8099")
APP_BASE_URL = os.getenv("APP_BASE_URL", "https://conchecker.scoutitsystems.com")
# Build identifier baked into the Docker image by CI (sha-<short_sha>, matching
# the image tag). Shown in the site header and /health. "dev" for local runs.
APP_BUILD = os.getenv("APP_BUILD", "dev")
# -- Email / SMTP (optional notification on completion) -------------
# If unset, the app still works; it just logs "SMTP not configured" and
+2
View File
@@ -35,6 +35,8 @@ _FRONTEND_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "
@app.get("/health")
def health():
return {"status": "ok", "model": config.MODEL,
"version": config.APP_VERSION,
"build": config.APP_BUILD,
"key_configured": bool(config.AI_API_KEY),
"email_configured": bool(config.SMTP_HOST and config.SMTP_USER and config.SMTP_PASSWORD)}
+1 -1
View File
@@ -13,7 +13,7 @@ services:
env_file:
- backend/.env
environment:
APP_BASE_URL: ${APP_BASE_URL:-http://localhost:8099}
APP_BASE_URL: ${APP_BASE_URL:-https://conchecker.scoutitsystems.com}
volumes:
- uploads:/app/backend/uploads
- outputs:/app/backend/outputs
+1 -1
View File
@@ -7,7 +7,7 @@ services:
- backend/.env
environment:
# Override in backend/.env for production (email links, etc.)
APP_BASE_URL: ${APP_BASE_URL:-http://localhost:8099}
APP_BASE_URL: ${APP_BASE_URL:-https://conchecker.scoutitsystems.com}
volumes:
- uploads:/app/backend/uploads
- outputs:/app/backend/outputs
+5 -1
View File
@@ -87,7 +87,7 @@
<body>
<header>
<h1>Conflict Checker</h1>
<div class="sub">Cross-discipline design contradiction review for construction drawing sets</div>
<div class="sub">Cross-discipline design contradiction review for construction drawing sets<span id="buildTag"></span></div>
</header>
<main>
<div class="drop" id="drop">
@@ -504,6 +504,10 @@ async function finalizeReview(){
// If opened from an email link (/?job=<id>), load that job's results directly.
(function init(){
// Show the deployed build in the header so it's obvious which version is up.
fetch('/health').then(r=>r.ok?r.json():null).then(h=>{
if(h&&h.build) document.getElementById('buildTag').textContent=' · build '+h.build;
}).catch(()=>{});
const jobId=new URLSearchParams(location.search).get('job');
if(jobId){ statusEl.innerHTML='<span class="spinner"></span>Loading job '+esc(jobId)+'...'; poll(jobId); }
})();
+21
View File
@@ -0,0 +1,21 @@
from fastapi.testclient import TestClient
from backend import config
from backend.main import app
def test_health_includes_version_and_build():
client = TestClient(app)
response = client.get("/health")
assert response.status_code == 200
body = response.json()
assert body["version"] == config.APP_VERSION
assert body["build"] == config.APP_BUILD
def test_app_base_url_default_is_public_site():
assert config.APP_BASE_URL == "https://conchecker.scoutitsystems.com"
def test_app_build_defaults_to_dev():
assert config.APP_BUILD == "dev"