From 4ecc7c5cef3c9e9bfbef1b182ab10e8e98727145 Mon Sep 17 00:00:00 2001 From: John Wilganowski Date: Tue, 28 Jul 2026 20:34:33 +0000 Subject: [PATCH] Point email links at conchecker.scoutitsystems.com and show build SHA in header. 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'. --- .gitea/workflows/docker-release.yml | 2 ++ Dockerfile | 4 ++++ backend/.env.example | 3 ++- backend/config.py | 5 ++++- backend/main.py | 2 ++ docker-compose.prod.yml | 2 +- docker-compose.yml | 2 +- frontend/index.html | 6 +++++- tests/api/test_build_info.py | 21 +++++++++++++++++++++ 9 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 tests/api/test_build_info.py diff --git a/.gitea/workflows/docker-release.yml b/.gitea/workflows/docker-release.yml index 8225774..73eca50 100644 --- a/.gitea/workflows/docker-release.yml +++ b/.gitea/workflows/docker-release.yml @@ -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 diff --git a/Dockerfile b/Dockerfile index 8df508e..37ec1e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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-, 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 \ diff --git a/backend/.env.example b/backend/.env.example index eeb82dc..ff845fc 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -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-) - do not set manually. # Email notifications (optional). Leave SMTP_HOST blank to disable. # Examples: diff --git a/backend/config.py b/backend/config.py index 70cfa77..169b5a8 100644 --- a/backend/config.py +++ b/backend/config.py @@ -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-, 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 diff --git a/backend/main.py b/backend/main.py index 6f12b3e..3fd9881 100644 --- a/backend/main.py +++ b/backend/main.py @@ -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)} diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 5cca7a8..0495a75 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -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 diff --git a/docker-compose.yml b/docker-compose.yml index 3bba993..3cdbc2e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 diff --git a/frontend/index.html b/frontend/index.html index 7ae8826..262734e 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -87,7 +87,7 @@

Conflict Checker

-
Cross-discipline design contradiction review for construction drawing sets
+
Cross-discipline design contradiction review for construction drawing sets
@@ -504,6 +504,10 @@ async function finalizeReview(){ // If opened from an email link (/?job=), 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='Loading job '+esc(jobId)+'...'; poll(jobId); } })(); diff --git a/tests/api/test_build_info.py b/tests/api/test_build_info.py new file mode 100644 index 0000000..8899cb5 --- /dev/null +++ b/tests/api/test_build_info.py @@ -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"