From 3d5e27bdfb15440c3e3a9a496ca4d7e3d0bdea35 Mon Sep 17 00:00:00 2001 From: Mitchell R Date: Thu, 21 May 2026 08:51:41 +0200 Subject: [PATCH] fix(release): surface build versions --- .github/workflows/build.yml | 1 + deploy/docker/Dockerfile.server | 9 ++++++--- docker-compose.coolify.yml | 5 +++++ docker-compose.yml | 5 +++++ kiosk/src/server.rs | 16 ++++++++++++---- .../src/plugins/service-admin-http/index.ts | 3 ++- server/src/shared/version.ts | 9 +++++++++ server/src/web-templates/layout.tsx | 19 +++++++++++++++++++ 8 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 server/src/shared/version.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2afb1e4..55b6ea4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -81,6 +81,7 @@ jobs: working-directory: kiosk env: BF_BUILD_ARCH: ${{ matrix.target }} + BF_BUILD_VERSION: ${{ inputs.version }} run: cargo build --release --target ${{ matrix.target }} - name: Strip + rename diff --git a/deploy/docker/Dockerfile.server b/deploy/docker/Dockerfile.server index 0bebcaf..bae9db8 100644 --- a/deploy/docker/Dockerfile.server +++ b/deploy/docker/Dockerfile.server @@ -1,7 +1,7 @@ -# BetterFrame server image — Node 23 + native deps for argon2/sqlite. +# BetterFrame server image — Node 24 + native deps for argon2/sqlite. # Trixie base (Debian 13, latest stable) — matches the host distro we # recommend in deploy/README.md. -FROM node:23-trixie-slim AS builder +FROM node:24-trixie-slim AS builder WORKDIR /app @@ -22,7 +22,9 @@ WORKDIR /app/server RUN npm run build # ---- Runtime image ---- -FROM node:23-trixie-slim +FROM node:24-trixie-slim + +ARG BF_SERVER_VERSION= # ffmpeg for camera snapshot capture (optional but needed for /admin/entities/:id/snapshot) RUN apt-get update && apt-get install -y --no-install-recommends \ @@ -51,5 +53,6 @@ USER betterframe WORKDIR /app/server ENV NODE_OPTIONS=--import=tsx +ENV BF_SERVER_VERSION=${BF_SERVER_VERSION} CMD ["node", "--import", "tsx", "/app/node_modules/@bsb/base/lib/scripts/bsb-plugin-cli.js", "start"] diff --git a/docker-compose.coolify.yml b/docker-compose.coolify.yml index 152e2b9..dce08aa 100644 --- a/docker-compose.coolify.yml +++ b/docker-compose.coolify.yml @@ -17,6 +17,8 @@ services: build: context: . dockerfile: deploy/docker/Dockerfile.server + args: + BF_SERVER_VERSION: ${BF_SERVER_VERSION:-} container_name: betterframe-server restart: unless-stopped environment: @@ -25,6 +27,9 @@ services: - BF_SQLITE_PATH=/var/lib/betterframe/betterframe.db - BF_NODERED_URL=http://nodered:1880 - BF_SELF_URL=http://server:18080 + - BF_SERVER_VERSION=${BF_SERVER_VERSION:-} + - COOLIFY_GIT_COMMIT=${COOLIFY_GIT_COMMIT:-} + - SOURCE_COMMIT=${SOURCE_COMMIT:-} volumes: - betterframe-data:/var/lib/betterframe expose: diff --git a/docker-compose.yml b/docker-compose.yml index e0eafcd..df00cc7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,6 +27,8 @@ services: build: context: . dockerfile: deploy/docker/Dockerfile.server + args: + BF_SERVER_VERSION: ${BF_SERVER_VERSION:-} container_name: betterframe-server restart: unless-stopped # Env overrides win over sec-config.yaml — Coolify / k8s inject these. @@ -36,6 +38,9 @@ services: - BF_SQLITE_PATH=/var/lib/betterframe/betterframe.db - BF_NODERED_URL=http://nodered:1880 - BF_SELF_URL=http://server:18080 + - BF_SERVER_VERSION=${BF_SERVER_VERSION:-} + - COOLIFY_GIT_COMMIT=${COOLIFY_GIT_COMMIT:-} + - SOURCE_COMMIT=${SOURCE_COMMIT:-} # Optional: paste Ed25519 PEM private key here for firmware signing. # - BF_FIRMWARE_SIGNING_KEY= # Optional: single-purpose Bearer token for GitHub Actions firmware import. diff --git a/kiosk/src/server.rs b/kiosk/src/server.rs index e4a3af7..72f34be 100644 --- a/kiosk/src/server.rs +++ b/kiosk/src/server.rs @@ -7,6 +7,10 @@ use tracing::info; use crate::bundle::KioskBundle; +fn kiosk_app_version() -> &'static str { + option_env!("BF_BUILD_VERSION").unwrap_or(env!("CARGO_PKG_VERSION")) +} + fn state_dir() -> PathBuf { let home = dirs::home_dir().expect("no home directory"); let dir = home.join(".betterframe-kiosk"); @@ -258,9 +262,13 @@ pub fn heartbeat( hw: &crate::hwmon::HwInfo, ) -> bool { let client = reqwest::blocking::Client::new(); - let display_info: Vec<_> = displays.iter().enumerate().map(|(index, (name, w, h))| { - serde_json::json!({ "index": index, "name": name, "width_px": w, "height_px": h }) - }).collect(); + let display_info: Vec<_> = displays + .iter() + .enumerate() + .map(|(index, (name, w, h))| { + serde_json::json!({ "index": index, "name": name, "width_px": w, "height_px": h }) + }) + .collect(); // Surface the LAN-side local key + port to admin so the UI can show a // copy-paste URL for bookmark-style layout switches. let local_key = load_or_create_local_key(); @@ -272,7 +280,7 @@ pub fn heartbeat( .post(format!("{server}/api/kiosk/heartbeat")) .header("Authorization", format!("Bearer {key}")) .json(&serde_json::json!({ - "kiosk_app_version": env!("CARGO_PKG_VERSION"), + "kiosk_app_version": kiosk_app_version(), "displays": display_info, "cpu_temp_c": hw.cpu_temp_c, "cpu_load_percent": hw.cpu_load_percent, diff --git a/server/src/plugins/service-admin-http/index.ts b/server/src/plugins/service-admin-http/index.ts index 4cb0cd2..b343002 100644 --- a/server/src/plugins/service-admin-http/index.ts +++ b/server/src/plugins/service-admin-http/index.ts @@ -22,6 +22,7 @@ import { initNoderedBridge, type NoderedBridge } from "../../shared/nodered-brid import { initFirmware, type FirmwareApi } from "../../shared/firmware.js"; import { initOsUpdates, type OsUpdateApi } from "../../shared/os-updates.js"; import { envStr } from "../../shared/env-overrides.js"; +import { serverVersion } from "../../shared/version.js"; import type { Repository } from "../service-store/repository.js"; import { registerMiddleware } from "./middleware.js"; @@ -207,7 +208,7 @@ export class Plugin extends BSBService, typeof Event }); app.get("/version", () => ({ name: "betterframe", - version: "0.1.0", + version: serverVersion(), now: new Date().toISOString(), })); app.get("/", () => { diff --git a/server/src/shared/version.ts b/server/src/shared/version.ts new file mode 100644 index 0000000..7fa3fc6 --- /dev/null +++ b/server/src/shared/version.ts @@ -0,0 +1,9 @@ +export function serverVersion(): string { + return ( + process.env.BF_SERVER_VERSION + || process.env.BF_BUILD_VERSION + || process.env.COOLIFY_GIT_COMMIT + || process.env.SOURCE_COMMIT + || "dev" + ); +} diff --git a/server/src/web-templates/layout.tsx b/server/src/web-templates/layout.tsx index edb8d8d..8d44f11 100644 --- a/server/src/web-templates/layout.tsx +++ b/server/src/web-templates/layout.tsx @@ -3,6 +3,7 @@ * Server-side rendered via jsx-htmx — returns string. */ import { css, js } from "jsx-htmx"; +import { serverVersion } from "../shared/version.js"; // ---- Shared types ----------------------------------------------------------- @@ -64,6 +65,7 @@ function Sidebar(props: { activeNav?: string }) { // ---- Layout ----------------------------------------------------------------- export function Layout(props: PageProps) { + const version = serverVersion(); return ( @@ -92,6 +94,12 @@ export function Layout(props: PageProps) {
{props.flash.message}
)}
{props.children}
+ {!props.minimal && ( +
+ Copyright BetterCorp (PTY) Ltd 2016 - 2026 - All Rights Reserved + Server: {version} +
+ )} @@ -176,6 +184,17 @@ const baseStyles = { ".topbar-user": { color: "#666", fontSize: "0.85rem" }, ".main-wrap": { display: "flex", flexDirection: "column", minHeight: "100vh" }, ".content": { flex: "1", padding: "1.5rem" }, + ".app-footer": { + display: "flex", + justifyContent: "space-between", + gap: "1rem", + padding: "0.75rem 1.5rem", + color: "#666", + fontSize: "0.8rem", + borderTop: "1px solid #e0e0e0", + backgroundColor: "#fff", + }, + ".app-footer code": { color: "#374151", fontSize: "0.8rem" }, ".minimal .content": { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "100vh" }, ".center-card": { width: "100%", maxWidth: "420px" }, ".auth-logo": { display: "block", width: "220px", maxWidth: "100%", height: "auto", margin: "0 0 1.25rem" },