show version in UI
This commit is contained in:
11
Dockerfile
11
Dockerfile
@@ -12,9 +12,16 @@ RUN go mod download
|
||||
# Copy the rest of the source
|
||||
COPY . .
|
||||
|
||||
ARG BUILD_TIME=""
|
||||
ARG GIT_COMMIT="unknown"
|
||||
ARG VERSION="dev"
|
||||
|
||||
# Build a static-ish binary (alpine musl)
|
||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
||||
go build -trimpath -ldflags="-s -w" -o /out/ingestd ./cmd/ingestd
|
||||
RUN BUILD_TIME="${BUILD_TIME:-$(date -u +%Y-%m-%dT%H:%M:%SZ)}" && \
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
||||
go build -trimpath \
|
||||
-ldflags="-s -w -X main.buildTime=${BUILD_TIME} -X main.gitCommit=${GIT_COMMIT} -X main.version=${VERSION}" \
|
||||
-o /out/ingestd ./cmd/ingestd
|
||||
|
||||
# ---- runtime stage ----
|
||||
FROM alpine:3.22
|
||||
|
||||
59
cmd/ingestd/buildinfo.go
Normal file
59
cmd/ingestd/buildinfo.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
version = "dev"
|
||||
gitCommit = "unknown"
|
||||
buildTime = "unknown"
|
||||
)
|
||||
|
||||
type binaryBuildInfo struct {
|
||||
Version string `json:"version"`
|
||||
GitCommit string `json:"git_commit"`
|
||||
BuildTime string `json:"build_time"`
|
||||
}
|
||||
|
||||
func currentBuildInfo() binaryBuildInfo {
|
||||
out := binaryBuildInfo{
|
||||
Version: version,
|
||||
GitCommit: gitCommit,
|
||||
BuildTime: buildTime,
|
||||
}
|
||||
|
||||
if bi, ok := debug.ReadBuildInfo(); ok {
|
||||
if out.Version == "" || out.Version == "dev" {
|
||||
if bi.Main.Version != "" && bi.Main.Version != "(devel)" {
|
||||
out.Version = bi.Main.Version
|
||||
}
|
||||
}
|
||||
for _, s := range bi.Settings {
|
||||
if s.Key == "vcs.revision" && (out.GitCommit == "" || out.GitCommit == "unknown") {
|
||||
if len(s.Value) > 12 {
|
||||
out.GitCommit = s.Value[:12]
|
||||
} else {
|
||||
out.GitCommit = s.Value
|
||||
}
|
||||
}
|
||||
if s.Key == "vcs.time" && (out.BuildTime == "" || out.BuildTime == "unknown") {
|
||||
out.BuildTime = s.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.Version = normalizeBuildField(out.Version, "dev")
|
||||
out.GitCommit = normalizeBuildField(out.GitCommit, "unknown")
|
||||
out.BuildTime = normalizeBuildField(out.BuildTime, "unknown")
|
||||
return out
|
||||
}
|
||||
|
||||
func normalizeBuildField(v string, fallback string) string {
|
||||
v = strings.TrimSpace(v)
|
||||
if v == "" {
|
||||
return fallback
|
||||
}
|
||||
return v
|
||||
}
|
||||
@@ -21,12 +21,14 @@ type webServer struct {
|
||||
db *db.DB
|
||||
site providers.Site
|
||||
model string
|
||||
build binaryBuildInfo
|
||||
}
|
||||
|
||||
type dashboardResponse struct {
|
||||
GeneratedAt time.Time `json:"generated_at"`
|
||||
Site string `json:"site"`
|
||||
Model string `json:"model"`
|
||||
Build binaryBuildInfo `json:"build"`
|
||||
RangeStart time.Time `json:"range_start"`
|
||||
RangeEnd time.Time `json:"range_end"`
|
||||
Observations []db.ObservationPoint `json:"observations"`
|
||||
@@ -46,6 +48,7 @@ func runWebServer(ctx context.Context, d *db.DB, site providers.Site, model, add
|
||||
db: d,
|
||||
site: site,
|
||||
model: model,
|
||||
build: currentBuildInfo(),
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
@@ -53,7 +56,10 @@ func runWebServer(ctx context.Context, d *db.DB, site providers.Site, model, add
|
||||
mux.HandleFunc("/api/dashboard", ws.handleDashboard)
|
||||
mux.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"ok":true}`))
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"ok": true,
|
||||
"build": ws.build,
|
||||
})
|
||||
})
|
||||
mux.HandleFunc("/chart", func(w http.ResponseWriter, r *http.Request) {
|
||||
serveIndex(w, sub)
|
||||
@@ -193,6 +199,7 @@ func (s *webServer) handleDashboard(w http.ResponseWriter, r *http.Request) {
|
||||
GeneratedAt: time.Now().UTC(),
|
||||
Site: s.site.Name,
|
||||
Model: s.model,
|
||||
Build: s.build,
|
||||
RangeStart: start,
|
||||
RangeEnd: end,
|
||||
Observations: observations,
|
||||
|
||||
@@ -615,7 +615,35 @@ function extendForecastTo(points, endTime) {
|
||||
return [...points, { ...last, ts: new Date(endTs).toISOString() }];
|
||||
}
|
||||
|
||||
function updateSiteMeta(site, model, tzLabel) {
|
||||
function formatBuildStamp(build) {
|
||||
if (!build) {
|
||||
return "build --";
|
||||
}
|
||||
|
||||
const parts = [];
|
||||
const rawBuildTime = build.build_time;
|
||||
if (rawBuildTime && rawBuildTime !== "unknown") {
|
||||
const dt = new Date(rawBuildTime);
|
||||
if (Number.isNaN(dt.getTime())) {
|
||||
parts.push(rawBuildTime);
|
||||
} else {
|
||||
parts.push(formatDateTime(dt));
|
||||
}
|
||||
} else {
|
||||
parts.push("--");
|
||||
}
|
||||
|
||||
if (build.git_commit && build.git_commit !== "unknown") {
|
||||
parts.push(`commit ${build.git_commit}`);
|
||||
}
|
||||
if (build.version && build.version !== "dev") {
|
||||
parts.push(`version ${build.version}`);
|
||||
}
|
||||
|
||||
return `build ${parts.join(", ")}`;
|
||||
}
|
||||
|
||||
function updateSiteMeta(site, model, tzLabel, build) {
|
||||
const home = document.getElementById("site-home");
|
||||
const suffix = document.getElementById("site-meta-suffix");
|
||||
if (home) {
|
||||
@@ -623,7 +651,7 @@ function updateSiteMeta(site, model, tzLabel) {
|
||||
home.setAttribute("href", "/");
|
||||
}
|
||||
if (suffix) {
|
||||
suffix.textContent = ` | model ${model || "--"} | ${tzLabel}`;
|
||||
suffix.textContent = ` | model ${model || "--"} | ${tzLabel} | ${formatBuildStamp(build)}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -686,7 +714,7 @@ function baseOptions(range) {
|
||||
function renderDashboard(data) {
|
||||
const latest = data.latest;
|
||||
const tzLabel = state.tz === "utc" ? "UTC" : "Local";
|
||||
updateSiteMeta(data.site, data.model, tzLabel);
|
||||
updateSiteMeta(data.site, data.model, tzLabel, data.build);
|
||||
updateText("last-updated", `updated ${formatDateTime(data.generated_at)}`);
|
||||
const forecastMeta = data.forecast && data.forecast.points && data.forecast.points.length
|
||||
? `forecast retrieved ${formatDateTime(data.forecast.retrieved_at)}`
|
||||
|
||||
Reference in New Issue
Block a user