From 8a8745fad26af0a0827cf8f438d969b7425fa607 Mon Sep 17 00:00:00 2001 From: RaymondVerhoef Date: Wed, 20 May 2026 15:20:46 +0200 Subject: [PATCH] feat: show build SHA + timestamp in a small footer for deploy verification Adds a BuildStamp component pinned to the bottom-right of every page (desktop only, dim monospace text) so it's obvious at a glance which build is currently running. The git short SHA and an ISO build timestamp are injected at build time via Vite's `define`, falling back to 'unknown' if git is not available. ESLint config declares the two compile-time constants as readonly globals so no per-file disable comments are needed. Co-Authored-By: Claude Opus 4.7 (1M context) --- eslint.config.js | 6 +++++- src/App.jsx | 20 ++++++++++++-------- src/components/ui/BuildStamp.jsx | 19 +++++++++++++++++++ vite.config.js | 15 +++++++++++++++ 4 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 src/components/ui/BuildStamp.jsx diff --git a/eslint.config.js b/eslint.config.js index 15ece5f..9baab19 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -14,7 +14,11 @@ export default defineConfig([ reactRefresh.configs.vite, ], languageOptions: { - globals: globals.browser, + globals: { + ...globals.browser, + __BUILD_SHA__: 'readonly', + __BUILD_TIME__: 'readonly', + }, parserOptions: { ecmaFeatures: { jsx: true } }, }, rules: { diff --git a/src/App.jsx b/src/App.jsx index 19df4c9..2c263d3 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,6 +2,7 @@ import { Routes, Route, Navigate, Link } from 'react-router-dom' import { BookOpen, CheckSquare, LayoutDashboard, Trophy, Settings, LogOut } from 'lucide-react' import { useApp } from './store/AppContext' import Mark from './components/ui/Mark' +import BuildStamp from './components/ui/BuildStamp' import ChatLauncher from './components/chat/ChatLauncher' import Login from './pages/Login' @@ -88,14 +89,17 @@ function App() { if (state.isLoading) return
Loading application...
return ( - - } /> - } /> - } /> - } /> - } /> - } /> - + <> + + } /> + } /> + } /> + } /> + } /> + } /> + + + ) } diff --git a/src/components/ui/BuildStamp.jsx b/src/components/ui/BuildStamp.jsx new file mode 100644 index 0000000..3437d4c --- /dev/null +++ b/src/components/ui/BuildStamp.jsx @@ -0,0 +1,19 @@ +const sha = typeof __BUILD_SHA__ === 'string' ? __BUILD_SHA__ : 'dev'; +const time = typeof __BUILD_TIME__ === 'string' ? __BUILD_TIME__ : new Date().toISOString(); + +const formatted = (() => { + const d = new Date(time); + if (Number.isNaN(d.getTime())) return time; + return d.toLocaleString('nl-NL', { dateStyle: 'short', timeStyle: 'short' }); +})(); + +const BuildStamp = () => ( +
+ v{sha} ยท {formatted} +
+); + +export default BuildStamp; diff --git a/vite.config.js b/vite.config.js index 7a446ee..5537728 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,9 +1,24 @@ import { defineConfig } from 'vite' +import { execSync } from 'node:child_process' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' +function getBuildSha() { + try { + return execSync('git rev-parse --short HEAD', { stdio: ['ignore', 'pipe', 'ignore'] }) + .toString() + .trim() + } catch { + return 'unknown' + } +} + // https://vite.dev/config/ export default defineConfig({ + define: { + __BUILD_SHA__: JSON.stringify(getBuildSha()), + __BUILD_TIME__: JSON.stringify(new Date().toISOString()), + }, plugins: [react(), tailwindcss()], server: { proxy: {