diff --git a/docker-compose.yml b/docker-compose.yml index 2bd9d77e..699fa943 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -181,6 +181,8 @@ services: build: context: . dockerfile: frontend_multi_user/Dockerfile + args: + GIT_COMMIT_HASH: ${GIT_COMMIT_HASH:-unknown} container_name: frontend_multi_user depends_on: database_postgres: diff --git a/frontend_multi_user/Dockerfile b/frontend_multi_user/Dockerfile index fa9c44cc..1d80ec08 100644 --- a/frontend_multi_user/Dockerfile +++ b/frontend_multi_user/Dockerfile @@ -1,5 +1,10 @@ FROM python:3.13-slim +# Accept commit hash from docker-compose build arg or Railway's built-in variable. +ARG GIT_COMMIT_HASH=unknown +ARG RAILWAY_GIT_COMMIT_SHA +ENV PLANEXE_GIT_COMMIT_HASH=${RAILWAY_GIT_COMMIT_SHA:-${GIT_COMMIT_HASH}} + ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PLANEXE_CONFIG_PATH=/app \ diff --git a/frontend_multi_user/src/app.py b/frontend_multi_user/src/app.py index 2828c1b1..f1c24519 100644 --- a/frontend_multi_user/src/app.py +++ b/frontend_multi_user/src/app.py @@ -16,6 +16,7 @@ import zipfile import secrets import hashlib +import subprocess import tempfile from urllib.parse import quote_plus, urlparse from typing import ClassVar, Dict, Optional, Tuple, Any, cast @@ -606,14 +607,36 @@ def load_user(user_id): self._track_flask_app_started() + @staticmethod + def _get_git_commit_hash() -> str: + """Return the short git commit hash, or 'unknown' if unavailable. + + Checks the PLANEXE_GIT_COMMIT_HASH env var first (set at Docker + build time), then falls back to running git directly. + """ + from_env = os.environ.get("PLANEXE_GIT_COMMIT_HASH", "").strip() + if from_env and from_env != "unknown": + return from_env + try: + return subprocess.check_output( + ["git", "rev-parse", "--short", "HEAD"], + stderr=subprocess.DEVNULL, + text=True, + ).strip() + except Exception: + return "unknown" + def _track_flask_app_started(self): logger.info(f"MyFlaskApp._track_flask_app_started. Starting...") - + # Determine if this is the main process or reloader process is_reloader = os.environ.get('WERKZEUG_RUN_MAIN') == 'true' is_debug_mode = self.app.debug if hasattr(self, 'app') else True - + + commit_hash = self._get_git_commit_hash() + event_context = { + "commit_hash": commit_hash, "pid": str(os.getpid()), "parent_pid": str(os.getppid()), "is_reloader_process": is_reloader, @@ -624,18 +647,18 @@ def _track_flask_app_started(self): "FLASK_ENV": os.environ.get('FLASK_ENV', 'not_set'), "FLASK_DEBUG": os.environ.get('FLASK_DEBUG', 'not_set') } - + with self.app.app_context(): event = _new_model( EventItem, event_type=EventType.GENERIC_EVENT, - message="Flask app started", + message=f"Flask app started (commit {commit_hash})", context=event_context, ) self.db.session.add(event) self.db.session.commit() - - logger.info(f"MyFlaskApp._track_flask_app_started. Logged {event_context!r}") + + logger.info(f"MyFlaskApp._track_flask_app_started. commit={commit_hash} context={event_context!r}") def _start_check(self): # When the Flask app launches in debug mode it runs __init__ twice, so that the app can hot reload.