Add required human review gate to the Agent pipeline #1
@@ -63,6 +63,8 @@ jobs:
|
|||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
build-args: |
|
||||||
|
APP_BUILD=sha-${{ steps.meta.outputs.short_sha }}
|
||||||
|
|
||||||
release:
|
release:
|
||||||
needs: build-and-push
|
needs: build-and-push
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ COPY cli cli
|
|||||||
RUN mkdir -p backend/uploads backend/outputs backend/.llm_cache
|
RUN mkdir -p backend/uploads backend/outputs backend/.llm_cache
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED=1
|
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
|
EXPOSE 8099
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ EXTRACT_CONCURRENCY=4
|
|||||||
REASON_CONCURRENCY=4
|
REASON_CONCURRENCY=4
|
||||||
|
|
||||||
# Public URL users reach this server on (used for the link in result emails)
|
# 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.
|
# Email notifications (optional). Leave SMTP_HOST blank to disable.
|
||||||
# Examples:
|
# Examples:
|
||||||
|
|||||||
+4
-1
@@ -123,7 +123,10 @@ APP_VERSION = "0.1.0"
|
|||||||
# Public base URL used to build the "view results" link in notification
|
# 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
|
# emails. Set to whatever address users reach this server on (e.g. the
|
||||||
# Tailscale/LAN URL) so the link in the email actually resolves.
|
# 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) -------------
|
# -- Email / SMTP (optional notification on completion) -------------
|
||||||
# If unset, the app still works; it just logs "SMTP not configured" and
|
# If unset, the app still works; it just logs "SMTP not configured" and
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ _FRONTEND_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "
|
|||||||
@app.get("/health")
|
@app.get("/health")
|
||||||
def health():
|
def health():
|
||||||
return {"status": "ok", "model": config.MODEL,
|
return {"status": "ok", "model": config.MODEL,
|
||||||
|
"version": config.APP_VERSION,
|
||||||
|
"build": config.APP_BUILD,
|
||||||
"key_configured": bool(config.AI_API_KEY),
|
"key_configured": bool(config.AI_API_KEY),
|
||||||
"email_configured": bool(config.SMTP_HOST and config.SMTP_USER and config.SMTP_PASSWORD)}
|
"email_configured": bool(config.SMTP_HOST and config.SMTP_USER and config.SMTP_PASSWORD)}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ services:
|
|||||||
env_file:
|
env_file:
|
||||||
- backend/.env
|
- backend/.env
|
||||||
environment:
|
environment:
|
||||||
APP_BASE_URL: ${APP_BASE_URL:-http://localhost:8099}
|
APP_BASE_URL: ${APP_BASE_URL:-https://conchecker.scoutitsystems.com}
|
||||||
volumes:
|
volumes:
|
||||||
- uploads:/app/backend/uploads
|
- uploads:/app/backend/uploads
|
||||||
- outputs:/app/backend/outputs
|
- outputs:/app/backend/outputs
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ services:
|
|||||||
- backend/.env
|
- backend/.env
|
||||||
environment:
|
environment:
|
||||||
# Override in backend/.env for production (email links, etc.)
|
# 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:
|
volumes:
|
||||||
- uploads:/app/backend/uploads
|
- uploads:/app/backend/uploads
|
||||||
- outputs:/app/backend/outputs
|
- outputs:/app/backend/outputs
|
||||||
|
|||||||
+5
-1
@@ -87,7 +87,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<h1>Conflict Checker</h1>
|
<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>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<div class="drop" id="drop">
|
<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.
|
// If opened from an email link (/?job=<id>), load that job's results directly.
|
||||||
(function init(){
|
(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');
|
const jobId=new URLSearchParams(location.search).get('job');
|
||||||
if(jobId){ statusEl.innerHTML='<span class="spinner"></span>Loading job '+esc(jobId)+'...'; poll(jobId); }
|
if(jobId){ statusEl.innerHTML='<span class="spinner"></span>Loading job '+esc(jobId)+'...'; poll(jobId); }
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -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"
|
||||||
Reference in New Issue
Block a user