diff --git a/app/api/health.py b/app/api/health.py index 2cc670e..e6734c6 100644 --- a/app/api/health.py +++ b/app/api/health.py @@ -12,6 +12,11 @@ from src.repositories.sync_state import SyncStateRepository router = APIRouter(tags=["health"]) +# Captured at module import (i.e., app process start) — proxy for "deployed at". +# When the cron auto-upgrade pulls a new digest and recreates the container, +# this resets. Accurate enough for a UI "last updated" badge. +_DEPLOYED_AT = datetime.now(timezone.utc).isoformat() + @router.get("/api/health") async def health_check(conn: duckdb.DuckDBPyConnection = Depends(_get_db)): @@ -73,7 +78,23 @@ async def health_check(conn: duckdb.DuckDBPyConnection = Depends(_get_db)): "status": overall, "version": os.environ.get("AGNES_VERSION", "dev"), "channel": os.environ.get("RELEASE_CHANNEL", "dev"), + "image_tag": os.environ.get("AGNES_TAG", "unknown"), + "commit_sha": os.environ.get("AGNES_COMMIT_SHA", "unknown"), "schema_version": SCHEMA_VERSION, + "deployed_at": _DEPLOYED_AT, "timestamp": datetime.now(timezone.utc).isoformat(), "services": checks, } + + +@router.get("/api/version") +async def version_info(): + """Lightweight version info — cacheable, no DB touch. Used by UI footer badge.""" + return { + "version": os.environ.get("AGNES_VERSION", "dev"), + "channel": os.environ.get("RELEASE_CHANNEL", "dev"), + "image_tag": os.environ.get("AGNES_TAG", "unknown"), + "commit_sha": os.environ.get("AGNES_COMMIT_SHA", "unknown"), + "schema_version": SCHEMA_VERSION, + "deployed_at": _DEPLOYED_AT, + } diff --git a/app/web/templates/base.html b/app/web/templates/base.html index 4f9c8ac..6fce72f 100644 --- a/app/web/templates/base.html +++ b/app/web/templates/base.html @@ -46,7 +46,31 @@ + diff --git a/infra/modules/customer-instance/startup-script.sh.tpl b/infra/modules/customer-instance/startup-script.sh.tpl index ef4b1ef..01e0a40 100644 --- a/infra/modules/customer-instance/startup-script.sh.tpl +++ b/infra/modules/customer-instance/startup-script.sh.tpl @@ -68,6 +68,25 @@ if [ "$DATA_SOURCE" = "keboola" ]; then fi JWT_KEY=$(gcloud secrets versions access latest --secret=agnes-$${CUSTOMER_NAME}-jwt-secret) +# Resolve the actual version/commit behind the requested tag so the UI can +# show specific `stable-2026.04.47` + commit SHA instead of just `stable`. +IMAGE_DIGEST=$(docker pull "$IMAGE_REPO:$IMAGE_TAG" 2>/dev/null | grep -o 'sha256:[a-f0-9]*' | head -1 || echo "unknown") +IMAGE_INFO=$(curl -fsSL "https://ghcr.io/v2/keboola/agnes-the-ai-analyst/manifests/$IMAGE_TAG" -H "Accept: application/vnd.oci.image.manifest.v1+json" 2>/dev/null || echo "{}") + +# Channel derived from tag prefix (stable-*/dev-*/release-*) — simple heuristic. +case "$IMAGE_TAG" in + stable*) RELEASE_CHANNEL="stable" ;; + dev*) RELEASE_CHANNEL="dev" ;; + release*) RELEASE_CHANNEL="release" ;; + *) RELEASE_CHANNEL="custom" ;; +esac + +# Version extracted from versioned tags (stable-2026.04.N); floating tags stay "dev". +case "$IMAGE_TAG" in + *-[0-9]*.[0-9]*.[0-9]*) AGNES_VERSION="$${IMAGE_TAG#*-}" ;; + *) AGNES_VERSION="$IMAGE_TAG" ;; +esac + cat > "$APP_DIR/.env" <