diff --git a/cmd/proxy/main.go b/cmd/proxy/main.go index 9054eee..c5549ad 100644 --- a/cmd/proxy/main.go +++ b/cmd/proxy/main.go @@ -275,7 +275,10 @@ func runServe() { logger := setupLogger(cfg.Log.Level, cfg.Log.Format) // Create and start server - srv, err := server.New(cfg, logger) + srv, err := server.New(cfg, logger, server.BuildInfo{ + Version: Version, + Commit: Commit, + }) if err != nil { logger.Error("failed to create server", "error", err) os.Exit(1) diff --git a/internal/server/browse_test.go b/internal/server/browse_test.go index 3cc37c8..f4f2f9a 100644 --- a/internal/server/browse_test.go +++ b/internal/server/browse_test.go @@ -430,6 +430,10 @@ func TestHandleBrowseSourcePage(t *testing.T) { } } + if !strings.Contains(body, "proxy test-version (test-commit)") { + t.Error("browse source footer should contain proxy build information, not the package version") + } + // Check that the escapeHTML function is present for XSS protection if !strings.Contains(body, "function escapeHTML(str)") { t.Error("browse source page missing escapeHTML function for XSS protection") diff --git a/internal/server/layout.go b/internal/server/layout.go index ef39858..2da9469 100644 --- a/internal/server/layout.go +++ b/internal/server/layout.go @@ -2,17 +2,24 @@ package server import "net/http" -// Layout carries per-request fields consumed by the shared base template -// (canonical URL, og:url). It is embedded in every page data struct so that -// templates can reference {{.UIBaseURL}} and {{.CanonicalPath}} alongside the -// page's own fields. +// BuildInfo identifies the running proxy binary. +type BuildInfo struct { + Version string + Commit string +} + +// Layout carries shared fields consumed by the base template. It is embedded +// in every page data struct so templates can access canonical URL and build +// information alongside the page's own fields. type Layout struct { + BuildInfo BuildInfo UIBaseURL string CanonicalPath string } func (s *Server) layoutFor(r *http.Request) Layout { return Layout{ + BuildInfo: s.buildInfo, UIBaseURL: s.cfg.UIBaseURL, CanonicalPath: r.URL.Path, } diff --git a/internal/server/server.go b/internal/server/server.go index 71af1af..bb964e8 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -92,6 +92,7 @@ type Server struct { db *database.DB storage storage.Storage logger *slog.Logger + buildInfo BuildInfo http *http.Server templates *Templates cancel context.CancelFunc @@ -100,7 +101,7 @@ type Server struct { } // New creates a new Server with the given configuration. -func New(cfg *config.Config, logger *slog.Logger) (*Server, error) { +func New(cfg *config.Config, logger *slog.Logger, buildInfo BuildInfo) (*Server, error) { var activityLog *accesslog.Logger if cfg.AccessLog.Path != "" { var err error @@ -169,6 +170,7 @@ func New(cfg *config.Config, logger *slog.Logger) (*Server, error) { db: db, storage: store, logger: logger, + buildInfo: buildInfo, templates: &Templates{}, healthCache: hc, accessLog: activityLog, diff --git a/internal/server/server_test.go b/internal/server/server_test.go index a870106..03b253f 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -104,6 +104,7 @@ func newTestServer(t *testing.T) *testServer { db: db, storage: store, logger: logger, + buildInfo: BuildInfo{Version: "test-version", Commit: "test-commit"}, templates: &Templates{}, healthCache: hc, } @@ -313,6 +314,9 @@ func TestDashboard(t *testing.T) { if !strings.Contains(body, "Cached Artifacts") { t.Error("dashboard should contain stats") } + if !strings.Contains(body, "proxy test-version (test-commit)") { + t.Error("dashboard footer should contain build information") + } if !strings.Contains(body, "Popular Packages") { t.Error("dashboard should contain popular packages section") } @@ -598,6 +602,9 @@ func TestVersionShowWithHitCount(t *testing.T) { if !strings.Contains(body, "42 cache hits") { t.Error("expected page to show hit count") } + if !strings.Contains(body, "proxy test-version (test-commit)") { + t.Error("version show footer should contain proxy build information, not the package version") + } } func TestSearchWithNullValues(t *testing.T) { @@ -1327,10 +1334,14 @@ func TestNewServer_StorageConnectivityCheck(t *testing.T) { logger := slog.New(slog.NewTextHandler(io.Discard, nil)) - srv, err := New(cfg, logger) + buildInfo := BuildInfo{Version: "test-version", Commit: "test-commit"} + srv, err := New(cfg, logger, buildInfo) if err != nil { t.Fatalf("New() failed: %v", err) } + if srv.buildInfo != buildInfo { + t.Errorf("build info = %#v, want %#v", srv.buildInfo, buildInfo) + } // On Windows, OpenBucket normalises to file:///C:/path; on Unix the // absolute path already starts with /, so file:// + /path == file:///path. @@ -1354,7 +1365,7 @@ func TestNewServer_InvalidAccessLogFailsBeforeDatabaseInit(t *testing.T) { } logger := slog.New(slog.NewTextHandler(io.Discard, nil)) - if _, err := New(cfg, logger); err == nil { + if _, err := New(cfg, logger, BuildInfo{}); err == nil { t.Fatal("New() succeeded with invalid access log path") } else if !strings.Contains(err.Error(), "initializing access log") { t.Fatalf("New() error = %v, want access log initialization error", err) diff --git a/internal/server/templates/layout/footer.html b/internal/server/templates/layout/footer.html index 5aa970d..33daddf 100644 --- a/internal/server/templates/layout/footer.html +++ b/internal/server/templates/layout/footer.html @@ -12,6 +12,11 @@
+ proxy {{.BuildInfo.Version}}{{if .BuildInfo.Commit}} ({{.BuildInfo.Commit}}){{end}} +
+ {{end}}