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:
@@ -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 = [
|
||||||
|
|||||||
@@ -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`).
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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}`;
|
||||||
@@ -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: {
|
||||||
|
|||||||
Reference in New Issue
Block a user