feat: add build info (commit SHA + deploy timestamp) to health endpoint and admin sidebar
Some checks failed
Some checks failed
- deploy.sh writes .build-info with commit SHA and timestamp after git pull - /health endpoint now returns version, commit, and deployed_at fields - Admin sidebar footer shows version and commit SHA - Hetzner docs updated: runner --config flag, swap, and runner timeout Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -161,6 +161,7 @@ docker-compose.override.yml
|
||||
*.override.yml
|
||||
|
||||
# Deployment & Security
|
||||
.build-info
|
||||
deployment-local/
|
||||
*.pem
|
||||
*.key
|
||||
|
||||
60
app/core/build_info.py
Normal file
60
app/core/build_info.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# app/core/build_info.py
|
||||
"""
|
||||
Build information utilities.
|
||||
|
||||
Reads commit SHA and deploy timestamp from .build-info file
|
||||
(written by scripts/deploy.sh at deploy time), or falls back
|
||||
to git for local development.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import subprocess
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_BUILD_INFO_FILE = Path(__file__).resolve().parent.parent.parent / ".build-info"
|
||||
_cached_info: dict | None = None
|
||||
|
||||
|
||||
def get_build_info() -> dict:
|
||||
"""Return build info: commit, deployed_at, environment."""
|
||||
global _cached_info
|
||||
if _cached_info is not None:
|
||||
return _cached_info
|
||||
|
||||
info = {
|
||||
"commit": None,
|
||||
"deployed_at": None,
|
||||
}
|
||||
|
||||
# Try .build-info file first (written by deploy.sh)
|
||||
if _BUILD_INFO_FILE.is_file():
|
||||
try:
|
||||
data = json.loads(_BUILD_INFO_FILE.read_text())
|
||||
info["commit"] = data.get("commit")
|
||||
info["deployed_at"] = data.get("deployed_at")
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to read .build-info: {e}")
|
||||
|
||||
# Fall back to git for local development
|
||||
if not info["commit"]:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", "rev-parse", "--short=8", "HEAD"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
info["commit"] = result.stdout.strip()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not info["deployed_at"]:
|
||||
info["deployed_at"] = datetime.now(UTC).isoformat()
|
||||
|
||||
_cached_info = info
|
||||
return info
|
||||
@@ -101,8 +101,18 @@
|
||||
DESKTOP SIDEBAR
|
||||
============================================================================ #}
|
||||
|
||||
<aside class="z-20 hidden w-64 overflow-y-auto bg-white dark:bg-gray-800 md:block flex-shrink-0">
|
||||
{{ sidebar_content() }}
|
||||
<aside class="z-20 hidden w-64 overflow-y-auto bg-white dark:bg-gray-800 md:block flex-shrink-0 flex flex-col">
|
||||
<div class="flex-1">
|
||||
{{ sidebar_content() }}
|
||||
</div>
|
||||
<div class="px-6 py-3 border-t border-gray-200 dark:border-gray-700">
|
||||
<p class="text-[10px] font-mono text-gray-400 dark:text-gray-600">
|
||||
v{{ config.version }}
|
||||
{% if config.commit %}
|
||||
· <span title="Deployed {{ config.deployed_at or '' }}">{{ config.commit }}</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{# ============================================================================
|
||||
|
||||
@@ -94,3 +94,12 @@ templates.env.globals["DEFAULT_LANGUAGE"] = DEFAULT_LANGUAGE
|
||||
templates.env.globals["LANGUAGE_NAMES"] = LANGUAGE_NAMES
|
||||
templates.env.globals["LANGUAGE_FLAGS"] = LANGUAGE_FLAGS
|
||||
templates.env.globals["current_year"] = datetime.now().year
|
||||
|
||||
# Add build info (version, commit, deployed_at) for sidebar footer
|
||||
from app.core.build_info import get_build_info
|
||||
from app.core.config import settings as _settings
|
||||
|
||||
templates.env.globals["config"] = {
|
||||
"version": _settings.version,
|
||||
**get_build_info(),
|
||||
}
|
||||
|
||||
@@ -1056,7 +1056,7 @@ After=network.target
|
||||
Type=simple
|
||||
User=samir
|
||||
WorkingDirectory=/home/samir/gitea-runner
|
||||
ExecStart=/home/samir/gitea-runner/act_runner daemon
|
||||
ExecStart=/home/samir/gitea-runner/act_runner daemon --config /home/samir/gitea-runner/config.yaml
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
|
||||
5
main.py
5
main.py
@@ -306,10 +306,15 @@ def health_check(db: Session = Depends(get_db)):
|
||||
"""Health check endpoint"""
|
||||
try:
|
||||
# Test database connection
|
||||
from app.core.build_info import get_build_info
|
||||
build = get_build_info()
|
||||
db.execute(text("SELECT 1"))
|
||||
return {
|
||||
"status": "healthy",
|
||||
"timestamp": datetime.now(UTC),
|
||||
"version": settings.version,
|
||||
"commit": build["commit"],
|
||||
"deployed_at": build["deployed_at"],
|
||||
"message": f"{settings.project_name} v{settings.version}",
|
||||
"docs": {
|
||||
"swagger": "/docs",
|
||||
|
||||
@@ -37,6 +37,13 @@ fi
|
||||
log "Restoring local changes …"
|
||||
git stash pop --quiet 2>/dev/null || true
|
||||
|
||||
# ── 1b. Write build info ─────────────────────────────────────────────────────
|
||||
log "Writing build info …"
|
||||
printf '{"commit":"%s","deployed_at":"%s"}\n' \
|
||||
"$(git rev-parse --short=8 HEAD)" \
|
||||
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||
> .build-info
|
||||
|
||||
# ── 2. Rebuild and restart containers ────────────────────────────────────────
|
||||
log "Rebuilding containers …"
|
||||
if ! $COMPOSE up -d --build; then
|
||||
|
||||
Reference in New Issue
Block a user