Use the git commit hash as the app version

- Spec header, status pill and footer now document '<env> @ <hash>'
  instead of invented release numbers
- Backend settings expose GIT_COMMIT_HASH / APP_ENV / APP_VERSION,
  mirroring the original app
- Frontend bakes the short hash in at build time and shows it in the
  top bar
This commit is contained in:
2026-09-17 08:12:54 -05:00
parent 3f353307cf
commit 3c66629ed2
5 changed files with 51 additions and 6 deletions
+19
View File
@@ -3,6 +3,7 @@ Django settings for the J621 backend.
""" """
import os import os
import subprocess
from pathlib import Path from pathlib import Path
from dotenv import load_dotenv from dotenv import load_dotenv
@@ -19,6 +20,24 @@ ALLOWED_HOSTS = [
if host.strip() if host.strip()
] ]
def _git_commit_hash():
"""Short git hash of the running build, mirroring the original app."""
try:
return subprocess.check_output(
["git", "rev-parse", "--short", "HEAD"],
cwd=BASE_DIR,
text=True,
stderr=subprocess.DEVNULL,
).strip()
except (subprocess.SubprocessError, OSError):
return "unknown"
GIT_COMMIT_HASH = _git_commit_hash()
APP_ENV = "dev" if DEBUG else "prod"
APP_VERSION = f"{APP_ENV} @ {GIT_COMMIT_HASH}"
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
+3 -3
View File
@@ -1,6 +1,6 @@
# DESIGN.md — J621 Frontend Design System & Architecture Specification # DESIGN.md — J621 Frontend Design System & Architecture Specification
> **Version:** 1.4.2-stable > **Version:** the git commit hash of the build (e.g. `3f35330`)
> **Theme Foundation:** Catppuccin Mocha + e621 Authentic Taxonomy Architecture > **Theme Foundation:** Catppuccin Mocha + e621 Authentic Taxonomy Architecture
> **Target Platform:** Desktop-first, fully responsive self-hosted home-server media web application (the library is a watched folder on disk, not a NAS appliance) > **Target Platform:** Desktop-first, fully responsive self-hosted home-server media web application (the library is a watched folder on disk, not a NAS appliance)
> **Reference Design System:** `Mocha Archive Terminal` (`{{DATA:DESIGN_SYSTEM:DESIGN_SYSTEM_3}}`) > **Reference Design System:** `Mocha Archive Terminal` (`{{DATA:DESIGN_SYSTEM:DESIGN_SYSTEM_3}}`)
@@ -79,12 +79,12 @@ All tag chips, counters, and sidebar taxonomy badges strictly adhere to e621's e
- **Top App Bar (`#11111b`, height: 56px):** - **Top App Bar (`#11111b`, height: 56px):**
- Left: Brand mark `J621` badge (`#cba6f7`) followed by the **storage line** — the path of the watched folder that J621 indexes (e.g. `/mnt/media/library`). J621 is folder-based: this is just a path on disk, not a NAS/appliance indicator. - Left: Brand mark `J621` badge (`#cba6f7`) followed by the **storage line** — the path of the watched folder that J621 indexes (e.g. `/mnt/media/library`). J621 is folder-based: this is just a path on disk, not a NAS/appliance indicator.
- Center: Nav pills: `Library`, `Online`, `Pools`, `Followed`, `Upload`, `Duplicates`, and `Stats [STAFF]`. - Center: Nav pills: `Library`, `Online`, `Pools`, `Followed`, `Upload`, `Duplicates`, and `Stats [STAFF]`.
- Right: System status pill with real backend values, never hardcoded: environment (`DEV`/`PROD`), the actual host OS, page generation time, and the total e621 API request time for that page load — e.g. `ENV: PROD · LINUX · 18ms (e621: 4ms)`. Followed by the user avatar. - Right: System status pill with real backend values, never hardcoded: environment (`DEV`/`PROD`), git commit hash, the actual host OS, page generation time, and the total e621 API request time for that page load — e.g. `ENV: PROD · a1b2c3d · LINUX · 18ms (e621: 4ms)`. Followed by the user avatar.
- **Global Footer Status Strip (`#11111b`, height: 32px):** - **Global Footer Status Strip (`#11111b`, height: 32px):**
- The strip placement is a proposal; its values are real and drawn from the existing stats data. - The strip placement is a proposal; its values are real and drawn from the existing stats data.
- Left: the watched folder path — `📁 Local Storage: /mnt/media/library`. - Left: the watched folder path — `📁 Local Storage: /mnt/media/library`.
- Center: `⚡ Active Workers: N running | Queue: M pending` — the count of active optimization/ingest jobs and queued downloads (the original app surfaced this on its stats page; the footer simply keeps it visible). - Center: `⚡ Active Workers: N running | Queue: M pending` — the count of active optimization/ingest jobs and queued downloads (the original app surfaced this on its stats page; the footer simply keeps it visible).
- Right: the build actually running — `J621 <version>` / commit hash. - Right: the running build — `<env> @ <git commit hash>` (e.g. `dev @ 3f35330`), matching the original app.
- **Collapsible Sidebar (Behavior):** - **Collapsible Sidebar (Behavior):**
- **Closed by default** across all gallery and viewer views to prioritize screen real estate. - **Closed by default** across all gallery and viewer views to prioritize screen real estate.
- Toggled via `☰` button or quick keyboard shortcut (`Ctrl+B` or `⌘B`). - Toggled via `☰` button or quick keyboard shortcut (`Ctrl+B` or `⌘B`).
+7 -3
View File
@@ -2,6 +2,7 @@ import { LogOut } from "lucide-react";
import { Link, NavLink, Outlet } from "react-router-dom"; import { Link, NavLink, Outlet } from "react-router-dom";
import { cn } from "@/lib/cn"; import { cn } from "@/lib/cn";
import { APP_VERSION } from "@/lib/version";
import { useAuth } from "@/store/auth"; import { useAuth } from "@/store/auth";
const navItems = [ const navItems = [
@@ -21,9 +22,6 @@ export function AppShell() {
<span className="rounded bg-ctp-mauve px-2 py-1 font-mono text-xs font-bold tracking-wide text-ctp-crust"> <span className="rounded bg-ctp-mauve px-2 py-1 font-mono text-xs font-bold tracking-wide text-ctp-crust">
J621 J621
</span> </span>
<span className="hidden text-[11px] font-medium uppercase tracking-[0.14em] text-ctp-overlay0 md:block">
Mocha Archive Terminal
</span>
</Link> </Link>
<nav className="flex items-center gap-1"> <nav className="flex items-center gap-1">
@@ -47,6 +45,12 @@ export function AppShell() {
</nav> </nav>
<div className="ml-auto flex items-center gap-3"> <div className="ml-auto flex items-center gap-3">
<span
className="hidden font-mono text-[11px] text-ctp-overlay0 lg:block"
title="Running build"
>
{APP_VERSION}
</span>
{user ? ( {user ? (
<> <>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
+5
View File
@@ -0,0 +1,5 @@
declare const __GIT_COMMIT_HASH__: string;
export const GIT_COMMIT_HASH = __GIT_COMMIT_HASH__;
export const APP_ENV = import.meta.env.DEV ? "dev" : "prod";
export const APP_VERSION = `${APP_ENV} @ ${GIT_COMMIT_HASH}`;
+17
View File
@@ -1,13 +1,30 @@
import { execSync } from "node:child_process";
import { fileURLToPath, URL } from "node:url"; import { fileURLToPath, URL } from "node:url";
import tailwindcss from "@tailwindcss/vite"; import tailwindcss from "@tailwindcss/vite";
import react from "@vitejs/plugin-react"; import react from "@vitejs/plugin-react";
import { defineConfig } from "vite"; import { defineConfig } from "vite";
function gitCommitHash(): string {
try {
return execSync("git rev-parse --short HEAD", {
cwd: fileURLToPath(new URL(".", import.meta.url)),
stdio: ["ignore", "pipe", "ignore"],
})
.toString()
.trim();
} catch {
return "unknown";
}
}
const backend = process.env.VITE_BACKEND ?? "http://127.0.0.1:8000"; const backend = process.env.VITE_BACKEND ?? "http://127.0.0.1:8000";
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
define: {
__GIT_COMMIT_HASH__: JSON.stringify(gitCommitHash()),
},
plugins: [react(), tailwindcss()], plugins: [react(), tailwindcss()],
resolve: { resolve: {
alias: { alias: {