From f066810f86605cc64df063ed3499ca72f72c19bc Mon Sep 17 00:00:00 2001 From: Charles Harries Date: Mon, 10 Aug 2026 22:26:22 +0100 Subject: [PATCH] homepage: Add build time --- cmd/buildinfo.go | 30 +++++++++++++++++++++++++ cmd/handlers.go | 8 ++++--- cmd/main.go | 2 ++ cmd/routes.go | 2 +- pages/navbar_test.go | 44 +++++++++++++++++++++++++++++++++++-- pages/static/main.css | 6 +++++ pages/templates/home.gohtml | 4 ++++ 7 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 cmd/buildinfo.go diff --git a/cmd/buildinfo.go b/cmd/buildinfo.go new file mode 100644 index 0000000..1318f3f --- /dev/null +++ b/cmd/buildinfo.go @@ -0,0 +1,30 @@ +package main + +import ( + "os" + "time" +) + +// buildTime can be stamped at link time with +// -ldflags "-X main.buildTime=2006-01-02T15:04:05Z". +var buildTime string + +// resolveBuildTime falls back to the binary's own mtime, which is what go +// build, air and Docker leave behind. Returns the zero time if neither is +// available, which the template renders as "N/A". +func resolveBuildTime() time.Time { + if t, err := time.Parse(time.RFC3339, buildTime); err == nil { + return t.UTC() + } + + exe, err := os.Executable() + if err != nil { + return time.Time{} + } + info, err := os.Stat(exe) + if err != nil { + return time.Time{} + } + + return info.ModTime().UTC() +} diff --git a/cmd/handlers.go b/cmd/handlers.go index 0a262b4..761d9f4 100644 --- a/cmd/handlers.go +++ b/cmd/handlers.go @@ -25,10 +25,11 @@ import ( ) type HomeParams struct { - NavBar pages.NavBar + NavBar pages.NavBar + BuildTime time.Time } -func home(database *db.DB, pg *pages.Pages, lastfmService *lastfm.Service) http.HandlerFunc { +func home(database *db.DB, pg *pages.Pages, lastfmService *lastfm.Service, buildTime time.Time) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") @@ -47,7 +48,8 @@ func home(database *db.DB, pg *pages.Pages, lastfmService *lastfm.Service) http. } params := HomeParams{ - NavBar: pages.NewNavBar(user, isLoggedIn), + NavBar: pages.NewNavBar(user, isLoggedIn), + BuildTime: buildTime, } err := pg.Execute("home", w, params) if err != nil { diff --git a/cmd/main.go b/cmd/main.go index ef020f3..1cb3c5a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -39,6 +39,7 @@ type application struct { playingNowService *playingnow.Service appleMusicService *applemusic.Service pages *pages.Pages + buildTime time.Time } // JSON API handlers @@ -218,6 +219,7 @@ func main() { playingNowService: playingNowService, appleMusicService: appleMusicService, pages: pages.NewPages(), + buildTime: resolveBuildTime(), } trackerInterval := time.Duration(viper.GetInt("tracker.interval")) * time.Second diff --git a/cmd/routes.go b/cmd/routes.go index 71f67d8..0457fe7 100644 --- a/cmd/routes.go +++ b/cmd/routes.go @@ -14,7 +14,7 @@ func (app *application) routes() http.Handler { //Handles static file routes mux.Handle("/static/{file_name}", app.pages.Static()) - mux.HandleFunc("/", session.WithPossibleAuth(home(app.database, app.pages, app.lastfmService), app.sessionManager)) + mux.HandleFunc("/", session.WithPossibleAuth(home(app.database, app.pages, app.lastfmService, app.buildTime), app.sessionManager)) // OAuth Routes mux.HandleFunc("/login/atproto", app.oauthManager.HandleLogin("atproto")) diff --git a/pages/navbar_test.go b/pages/navbar_test.go index 43be8a3..27bb4c1 100644 --- a/pages/navbar_test.go +++ b/pages/navbar_test.go @@ -4,12 +4,19 @@ import ( "bytes" "strings" "testing" + "time" "github.com/teal-fm/piper/models" ) func ptr(s string) *string { return &s } +// homeParams mirrors cmd.HomeParams, which this package can't import. +type homeParams struct { + NavBar NavBar + BuildTime time.Time +} + func TestNewNavBar(t *testing.T) { t.Run("nil user", func(t *testing.T) { nav := NewNavBar(nil, false) @@ -89,7 +96,7 @@ func TestHomeServiceBullets(t *testing.T) { render := func(t *testing.T, nav NavBar) string { t.Helper() var buf bytes.Buffer - params := struct{ NavBar NavBar }{NavBar: nav} + params := homeParams{NavBar: nav} if err := pages.Execute("home", &buf, params); err != nil { t.Fatalf("failed to render home: %v", err) } @@ -224,7 +231,7 @@ func TestNavBarChipRendering(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var buf bytes.Buffer - params := struct{ NavBar NavBar }{NavBar: tt.nav} + params := homeParams{NavBar: tt.nav} if err := pages.Execute("home", &buf, params); err != nil { t.Fatalf("failed to render home: %v", err) } @@ -243,3 +250,36 @@ func TestNavBarChipRendering(t *testing.T) { }) } } + +func TestHomeBuildTime(t *testing.T) { + pages := NewPages() + + render := func(t *testing.T, params homeParams) string { + t.Helper() + var buf bytes.Buffer + if err := pages.Execute("home", &buf, params); err != nil { + t.Fatalf("failed to render home: %v", err) + } + return buf.String() + } + + t.Run("shows the build time", func(t *testing.T) { + built := time.Date(2026, time.August, 10, 21, 25, 0, 0, time.UTC) + out := render(t, homeParams{BuildTime: built}) + if !strings.Contains(out, "Built Aug 10, 2026 21:25 UTC") { + t.Error("expected the formatted build time in the footer") + } + }) + + // os.Executable and stat both have to fail for this, but "unknown" beats + // the "N/A UTC" that formatTime would produce on its own. + t.Run("unknown build time", func(t *testing.T) { + out := render(t, homeParams{}) + if !strings.Contains(out, "Built unknown") { + t.Error("expected the footer to degrade to 'unknown'") + } + if strings.Contains(out, "UTC") { + t.Error("did not expect a timezone with no build time") + } + }) +} diff --git a/pages/static/main.css b/pages/static/main.css index a4162e6..42be74b 100644 --- a/pages/static/main.css +++ b/pages/static/main.css @@ -16,6 +16,8 @@ --spacing: 0.25rem; --text-xs: 0.75rem; --text-xs--line-height: calc(1 / 0.75); + --text-sm: 0.875rem; + --text-sm--line-height: calc(1.25 / 0.875); --text-lg: 1.125rem; --text-lg--line-height: calc(1.75 / 1.125); --text-xl: 1.25rem; @@ -432,6 +434,10 @@ font-size: var(--text-lg); line-height: var(--tw-leading, var(--text-lg--line-height)); } + .text-sm { + font-size: var(--text-sm); + line-height: var(--tw-leading, var(--text-sm--line-height)); + } .text-xl { font-size: var(--text-xl); line-height: var(--tw-leading, var(--text-xl--line-height)); diff --git a/pages/templates/home.gohtml b/pages/templates/home.gohtml index bacb7fa..7cd1f87 100644 --- a/pages/templates/home.gohtml +++ b/pages/templates/home.gohtml @@ -136,4 +136,8 @@ + + {{ end }} -- 2.51.2