mirror of
https://github.com/wahyd4/hey-search.git
synced 2026-08-08 21:05:14 +10:00
Show app version at the bottom
This commit is contained in:
@@ -2,7 +2,8 @@ name: Docker Build & Push
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [master]
|
branches: ["**"]
|
||||||
|
tags: ["v*"]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [master]
|
branches: [master]
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Log in to GitHub Container Registry
|
- name: Log in to GitHub Container Registry
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/'))
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
@@ -40,13 +41,18 @@ jobs:
|
|||||||
tags: |
|
tags: |
|
||||||
type=sha
|
type=sha
|
||||||
type=raw,value=latest,enable={{is_default_branch}}
|
type=raw,value=latest,enable={{is_default_branch}}
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
push: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) }}
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
build-args: |
|
||||||
|
APP_VERSION=${{ steps.meta.outputs.version }}
|
||||||
|
APP_COMMIT=${{ github.sha }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ COPY --from=frontend-build /app/frontend/dist /app/static
|
|||||||
# Persistent data directory for SQLite DB and configuration
|
# Persistent data directory for SQLite DB and configuration
|
||||||
ENV DATA_DIR=/app/data
|
ENV DATA_DIR=/app/data
|
||||||
ENV REDIS_URL=""
|
ENV REDIS_URL=""
|
||||||
|
|
||||||
|
# Version injected at build time via --build-arg
|
||||||
|
ARG APP_VERSION=dev
|
||||||
|
ARG APP_COMMIT=unknown
|
||||||
|
ENV APP_VERSION=${APP_VERSION}
|
||||||
|
ENV APP_COMMIT=${APP_COMMIT}
|
||||||
VOLUME ["/app/data"]
|
VOLUME ["/app/data"]
|
||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from app.settings import get_all_settings, get_setting, set_setting
|
|||||||
from app.cache import is_cache_available, flush_cache, reconnect_redis
|
from app.cache import is_cache_available, flush_cache, reconnect_redis
|
||||||
from app.history import get_history, delete_history_entry, clear_history
|
from app.history import get_history, delete_history_entry, clear_history
|
||||||
from app import stats as _stats
|
from app import stats as _stats
|
||||||
|
from app.version import get_version_info
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@@ -690,3 +691,10 @@ async def api_record_click(body: ClickEventRequest, request: Request):
|
|||||||
)
|
)
|
||||||
async def api_get_stats(days: int = Query(7, ge=1, le=365)):
|
async def api_get_stats(days: int = Query(7, ge=1, le=365)):
|
||||||
return _stats.get_summary(days)
|
return _stats.get_summary(days)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Version ---
|
||||||
|
|
||||||
|
@router.get("/version", summary="App version", tags=["System"])
|
||||||
|
async def api_version():
|
||||||
|
return get_version_info()
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
"""Version detection — reads env vars injected at Docker build time, falls back to git for local dev."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
def _git(cmd: list[str]) -> str:
|
||||||
|
try:
|
||||||
|
return subprocess.check_output(cmd, stderr=subprocess.DEVNULL, cwd=os.path.dirname(__file__)).decode().strip()
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def get_version_info() -> dict[str, str]:
|
||||||
|
"""Return {"version": ..., "commit": ..., "local": bool_as_str}."""
|
||||||
|
version = os.environ.get("APP_VERSION", "")
|
||||||
|
commit = os.environ.get("APP_COMMIT", "")
|
||||||
|
|
||||||
|
if not version and not commit:
|
||||||
|
# Local dev — derive from git
|
||||||
|
version = _git(["git", "describe", "--tags", "--abbrev=0"]) or "local"
|
||||||
|
commit = _git(["git", "rev-parse", "--short", "HEAD"]) or "unknown"
|
||||||
|
return {"version": version, "commit": commit, "local": "true"}
|
||||||
|
|
||||||
|
return {"version": version, "commit": commit[:7] if commit else "", "local": "false"}
|
||||||
+16
-3
@@ -11,7 +11,7 @@ import { Bookmarks } from "@/components/Bookmarks";
|
|||||||
import { AppHeader } from "@/components/AppHeader";
|
import { AppHeader } from "@/components/AppHeader";
|
||||||
import { StatsPage } from "@/components/StatsPage";
|
import { StatsPage } from "@/components/StatsPage";
|
||||||
import { History } from "@/components/History";
|
import { History } from "@/components/History";
|
||||||
import { search as apiSearch, isImageResult, getBackground, refreshBackground, getBookmarkedUrls, addBookmark, removeBookmarkByUrl, type SearchResponse, type WebResult, type ImageResult, type BackgroundInfo } from "@/lib/api";
|
import { search as apiSearch, isImageResult, getBackground, refreshBackground, getBookmarkedUrls, addBookmark, removeBookmarkByUrl, getVersion, type SearchResponse, type WebResult, type ImageResult, type BackgroundInfo, type VersionInfo } from "@/lib/api";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
type Category = "web" | "images";
|
type Category = "web" | "images";
|
||||||
@@ -82,10 +82,14 @@ function App() {
|
|||||||
// Bookmarked URLs for toggle state
|
// Bookmarked URLs for toggle state
|
||||||
const [bookmarkedUrls, setBookmarkedUrls] = useState<Set<string>>(new Set());
|
const [bookmarkedUrls, setBookmarkedUrls] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
// Fetch background and bookmarked URLs on mount
|
// App version
|
||||||
|
const [versionInfo, setVersionInfo] = useState<VersionInfo | null>(null);
|
||||||
|
|
||||||
|
// Fetch background, bookmarked URLs, and version on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getBackground().then(setBgInfo).catch(() => {});
|
getBackground().then(setBgInfo).catch(() => {});
|
||||||
getBookmarkedUrls().then(setBookmarkedUrls).catch(() => {});
|
getBookmarkedUrls().then(setBookmarkedUrls).catch(() => {});
|
||||||
|
getVersion().then(setVersionInfo).catch(() => {});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const bgUrl = bgInfo?.enabled && bgInfo?.url ? bgInfo.url : null;
|
const bgUrl = bgInfo?.enabled && bgInfo?.url ? bgInfo.url : null;
|
||||||
@@ -704,7 +708,16 @@ function App() {
|
|||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<footer className="border-t px-4 py-3 text-xs text-muted-foreground">
|
<footer className="border-t px-4 py-3 text-xs text-muted-foreground">
|
||||||
<div className="mx-auto max-w-6xl text-center">Hey Search</div>
|
<div className="mx-auto max-w-6xl text-center">
|
||||||
|
Hey Search
|
||||||
|
{versionInfo && (
|
||||||
|
<span className="ml-1.5 opacity-60">
|
||||||
|
{versionInfo.local === "true"
|
||||||
|
? `local · ${versionInfo.commit}`
|
||||||
|
: versionInfo.version}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
{/* Error toasts */}
|
{/* Error toasts */}
|
||||||
|
|||||||
@@ -359,3 +359,17 @@ export async function deleteHistoryEntry(id: number): Promise<void> {
|
|||||||
export async function clearHistory(): Promise<void> {
|
export async function clearHistory(): Promise<void> {
|
||||||
await fetch(`${API_BASE}/history`, { method: "DELETE" });
|
await fetch(`${API_BASE}/history`, { method: "DELETE" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Version ---
|
||||||
|
|
||||||
|
export interface VersionInfo {
|
||||||
|
version: string;
|
||||||
|
commit: string;
|
||||||
|
local: string; // "true" | "false"
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getVersion(): Promise<VersionInfo> {
|
||||||
|
const resp = await fetch(`${API_BASE}/version`);
|
||||||
|
if (!resp.ok) throw new Error("Failed to fetch version");
|
||||||
|
return resp.json();
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user