feat: inject build version/commit into scraper and UI at docker build time
Some checks failed
CI / Scraper / Lint (push) Failing after 6s
CI / Scraper / Lint (pull_request) Failing after 6s
CI / Scraper / Test (push) Successful in 16s
CI / Scraper / Test (pull_request) Successful in 9s
CI / Scraper / Docker Push (push) Has been skipped
CI / Scraper / Docker Push (pull_request) Has been skipped
CI / UI / Build (push) Successful in 28s
CI / UI / Build (pull_request) Successful in 22s
CI / UI / Docker Push (pull_request) Has been skipped
CI / UI / Docker Push (push) Successful in 36s
iOS CI / Build (pull_request) Successful in 1m41s
iOS CI / Test (pull_request) Successful in 4m7s
Some checks failed
CI / Scraper / Lint (push) Failing after 6s
CI / Scraper / Lint (pull_request) Failing after 6s
CI / Scraper / Test (push) Successful in 16s
CI / Scraper / Test (pull_request) Successful in 9s
CI / Scraper / Docker Push (push) Has been skipped
CI / Scraper / Docker Push (pull_request) Has been skipped
CI / UI / Build (push) Successful in 28s
CI / UI / Build (pull_request) Successful in 22s
CI / UI / Docker Push (pull_request) Has been skipped
CI / UI / Docker Push (push) Successful in 36s
iOS CI / Build (pull_request) Successful in 1m41s
iOS CI / Test (pull_request) Successful in 4m7s
- Go scraper: Version/Commit vars in main.go, injected via -ldflags; Server struct + New() updated; GET /health and new GET /api/version expose them - UI Dockerfile: ARG BUILD_VERSION/BUILD_COMMIT → ENV PUBLIC_BUILD_VERSION/PUBLIC_BUILD_COMMIT for SvelteKit - Footer: shows version+short commit when not 'dev' (text-zinc-800, subtle) - docker-compose: args blocks for scraper and ui build sections pass $GIT_TAG/$GIT_COMMIT
This commit is contained in:
@@ -82,6 +82,9 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: ./scraper
|
context: ./scraper
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
VERSION: "${GIT_TAG:-dev}"
|
||||||
|
COMMIT: "${GIT_COMMIT:-unknown}"
|
||||||
#container_name: libnovel-scraper
|
#container_name: libnovel-scraper
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
@@ -131,6 +134,9 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: ./ui
|
context: ./ui
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
BUILD_VERSION: "${GIT_TAG:-dev}"
|
||||||
|
BUILD_COMMIT: "${GIT_COMMIT:-unknown}"
|
||||||
# container_name: libnovel-ui
|
# container_name: libnovel-ui
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
@@ -9,8 +9,14 @@ RUN go mod download
|
|||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
# Build-time version info — injected by docker-compose or CI via --build-arg.
|
||||||
|
ARG VERSION=dev
|
||||||
|
ARG COMMIT=unknown
|
||||||
|
|
||||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
||||||
go build -ldflags="-s -w" -o /scraper ./cmd/scraper
|
go build \
|
||||||
|
-ldflags="-s -w -X main.Version=${VERSION} -X main.Commit=${COMMIT}" \
|
||||||
|
-o /scraper ./cmd/scraper
|
||||||
|
|
||||||
# ── Runtime stage ──────────────────────────────────────────────────────────────
|
# ── Runtime stage ──────────────────────────────────────────────────────────────
|
||||||
FROM alpine:3.21
|
FROM alpine:3.21
|
||||||
|
|||||||
@@ -47,6 +47,13 @@ import (
|
|||||||
"github.com/libnovel/scraper/internal/storage"
|
"github.com/libnovel/scraper/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Build-time version info — injected via -ldflags during docker build.
|
||||||
|
// Falls back to "dev" / "unknown" when built without -ldflags (local dev).
|
||||||
|
var (
|
||||||
|
Version = "dev"
|
||||||
|
Commit = "unknown"
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logLevel := slog.LevelInfo
|
logLevel := slog.LevelInfo
|
||||||
if v := os.Getenv("LOG_LEVEL"); v != "" {
|
if v := os.Getenv("LOG_LEVEL"); v != "" {
|
||||||
@@ -183,7 +190,7 @@ func run(log *slog.Logger) error {
|
|||||||
"pocketbase_url", pbCfg.BaseURL,
|
"pocketbase_url", pbCfg.BaseURL,
|
||||||
"pocketbase_email", pbCfg.AdminEmail,
|
"pocketbase_email", pbCfg.AdminEmail,
|
||||||
)
|
)
|
||||||
srv := server.New(addr, oCfg, nf, log, store, kokoroURL, kokoroVoice)
|
srv := server.New(addr, oCfg, nf, log, store, kokoroURL, kokoroVoice, Version, Commit)
|
||||||
return srv.ListenAndServe(ctx)
|
return srv.ListenAndServe(ctx)
|
||||||
|
|
||||||
case "save-browse":
|
case "save-browse":
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ type Server struct {
|
|||||||
running bool
|
running bool
|
||||||
kokoroURL string // Kokoro-FastAPI base URL, e.g. http://kokoro:8880
|
kokoroURL string // Kokoro-FastAPI base URL, e.g. http://kokoro:8880
|
||||||
kokoroVoice string // default voice, e.g. af_bella
|
kokoroVoice string // default voice, e.g. af_bella
|
||||||
|
version string // semver tag, e.g. "v1.2.3" (set via ldflags)
|
||||||
|
commit string // short git SHA (set via ldflags)
|
||||||
|
|
||||||
// voiceMu guards cachedVoices.
|
// voiceMu guards cachedVoices.
|
||||||
voiceMu sync.RWMutex
|
voiceMu sync.RWMutex
|
||||||
@@ -74,7 +76,7 @@ type browseCacheEntry struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Server.
|
// New creates a new Server.
|
||||||
func New(addr string, oCfg orchestrator.Config, novel scraper.NovelScraper, log *slog.Logger, store storage.Store, kokoroURL, kokoroVoice string) *Server {
|
func New(addr string, oCfg orchestrator.Config, novel scraper.NovelScraper, log *slog.Logger, store storage.Store, kokoroURL, kokoroVoice, version, commit string) *Server {
|
||||||
return &Server{
|
return &Server{
|
||||||
addr: addr,
|
addr: addr,
|
||||||
oCfg: oCfg,
|
oCfg: oCfg,
|
||||||
@@ -83,6 +85,8 @@ func New(addr string, oCfg orchestrator.Config, novel scraper.NovelScraper, log
|
|||||||
store: store,
|
store: store,
|
||||||
kokoroURL: kokoroURL,
|
kokoroURL: kokoroURL,
|
||||||
kokoroVoice: kokoroVoice,
|
kokoroVoice: kokoroVoice,
|
||||||
|
version: version,
|
||||||
|
commit: commit,
|
||||||
audioJobIDs: make(map[string]string),
|
audioJobIDs: make(map[string]string),
|
||||||
browseInFlight: make(map[string]struct{}),
|
browseInFlight: make(map[string]struct{}),
|
||||||
browseMemCache: make(map[string]browseCacheEntry),
|
browseMemCache: make(map[string]browseCacheEntry),
|
||||||
@@ -138,6 +142,7 @@ func (s *Server) voices() []string {
|
|||||||
func (s *Server) ListenAndServe(ctx context.Context) error {
|
func (s *Server) ListenAndServe(ctx context.Context) error {
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
mux.HandleFunc("GET /health", s.handleHealth)
|
mux.HandleFunc("GET /health", s.handleHealth)
|
||||||
|
mux.HandleFunc("GET /api/version", s.handleVersion)
|
||||||
mux.HandleFunc("POST /scrape", s.handleScrapeCatalogue)
|
mux.HandleFunc("POST /scrape", s.handleScrapeCatalogue)
|
||||||
mux.HandleFunc("POST /scrape/book", s.handleScrapeBook)
|
mux.HandleFunc("POST /scrape/book", s.handleScrapeBook)
|
||||||
mux.HandleFunc("POST /scrape/book/range", s.handleScrapeBookRange)
|
mux.HandleFunc("POST /scrape/book/range", s.handleScrapeBookRange)
|
||||||
@@ -222,7 +227,19 @@ func (s *Server) ListenAndServe(ctx context.Context) error {
|
|||||||
|
|
||||||
func (s *Server) handleHealth(w http.ResponseWriter, _ *http.Request) {
|
func (s *Server) handleHealth(w http.ResponseWriter, _ *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
_ = json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
|
_ = json.NewEncoder(w).Encode(map[string]string{
|
||||||
|
"status": "ok",
|
||||||
|
"version": s.version,
|
||||||
|
"commit": s.commit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleVersion(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]string{
|
||||||
|
"version": s.version,
|
||||||
|
"commit": s.commit,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Session cookie helpers ───────────────────────────────────────────────────
|
// ─── Session cookie helpers ───────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -5,6 +5,15 @@ COPY package.json package-lock.json ./
|
|||||||
RUN npm ci
|
RUN npm ci
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
# Build-time version info — injected by docker-compose or CI via --build-arg.
|
||||||
|
ARG BUILD_VERSION=dev
|
||||||
|
ARG BUILD_COMMIT=unknown
|
||||||
|
|
||||||
|
# Expose as PUBLIC_ env vars so SvelteKit's $env/dynamic/public can read them.
|
||||||
|
ENV PUBLIC_BUILD_VERSION=$BUILD_VERSION
|
||||||
|
ENV PUBLIC_BUILD_COMMIT=$BUILD_COMMIT
|
||||||
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# ── Runtime image ──────────────────────────────────────────────────────────────
|
# ── Runtime image ──────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import type { Snippet } from 'svelte';
|
import type { Snippet } from 'svelte';
|
||||||
import type { LayoutData } from './$types';
|
import type { LayoutData } from './$types';
|
||||||
import { audioStore } from '$lib/audio.svelte';
|
import { audioStore } from '$lib/audio.svelte';
|
||||||
|
import { env } from '$env/dynamic/public';
|
||||||
|
|
||||||
let { children, data }: { children: Snippet; data: LayoutData } = $props();
|
let { children, data }: { children: Snippet; data: LayoutData } = $props();
|
||||||
|
|
||||||
@@ -395,6 +396,9 @@
|
|||||||
<a href="/privacy" class="hover:text-zinc-500 transition-colors">Privacy</a>
|
<a href="/privacy" class="hover:text-zinc-500 transition-colors">Privacy</a>
|
||||||
<a href="/dmca" class="hover:text-zinc-500 transition-colors">DMCA</a>
|
<a href="/dmca" class="hover:text-zinc-500 transition-colors">DMCA</a>
|
||||||
<span>© {new Date().getFullYear()} libnovel</span>
|
<span>© {new Date().getFullYear()} libnovel</span>
|
||||||
|
{#if env.PUBLIC_BUILD_VERSION && env.PUBLIC_BUILD_VERSION !== 'dev'}
|
||||||
|
<span class="text-zinc-800">{env.PUBLIC_BUILD_VERSION}+{env.PUBLIC_BUILD_COMMIT?.slice(0, 7)}</span>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
Reference in New Issue
Block a user