dashboard: report the running build to admins !119

merged merged by cmc on 2026-08-30 04:30 UTC · krz/gitbay:dashboard-build into main

2 files changed, +62 −0

Layout: unified · split

e2e/dashboardbuild_test.go added +48
@@ -0,0 +1,48 @@
1package e2e
2
3import (
4 "strings"
5 "testing"
6)
7
8// The dashboard reports which build is serving, so the running commit is
9// visible without reading the journal. It is admin-only: the exact build a
10// host runs narrows down which known issues apply to it.
11func TestDashboardReportsTheServerBuild(t *testing.T) {
12 inst := startInstance(t)
13
14 adminKey := inst.newKey(t, "root")
15 userKey := inst.newKey(t, "plain")
16 inst.admin(t, "admin", "user", "create", "root", "--key", adminKey+".pub", "--admin")
17 inst.admin(t, "admin", "user", "create", "plain", "--key", userKey+".pub")
18
19 out, errOut, code := inst.ssh(t, adminKey, "", "dashboard", "--json")
20 if code != 0 {
21 t.Fatalf("admin dashboard: %s", errOut)
22 }
23 if !strings.Contains(out, `"server":{"commit":"`) {
24 t.Fatalf("admin dashboard did not report the build:\n%s", out)
25 }
26
27 // A non-admin gets the same dashboard without it. omitempty drops the key
28 // entirely rather than reporting an empty string.
29 out, errOut, code = inst.ssh(t, userKey, "", "dashboard", "--json")
30 if code != 0 {
31 t.Fatalf("user dashboard: %s", errOut)
32 }
33 if strings.Contains(out, `"server"`) {
34 t.Fatalf("a non-admin was told the server build:\n%s", out)
35 }
36 if !strings.Contains(out, `"review_queue"`) {
37 t.Fatalf("user dashboard is missing its usual contents:\n%s", out)
38 }
39
40 // Human output carries it too, for the operator who did not ask for JSON.
41 out, errOut, code = inst.ssh(t, adminKey, "", "dashboard")
42 if code != 0 {
43 t.Fatalf("admin dashboard (human): %s", errOut)
44 }
45 if !strings.Contains(out, "server:") || !strings.Contains(out, "build ") {
46 t.Fatalf("human dashboard did not report the build:\n%s", out)
47 }
48}
internal/control/dashboard.go +14
@@ -6,6 +6,7 @@ import (
66 "io"
77 "strconv"
88
9 "gitbay.org/gitbay/internal/buildinfo"
910 "gitbay.org/gitbay/internal/gitutil"
1011 "gitbay.org/gitbay/internal/policy"
1112 "gitbay.org/gitbay/internal/protocol"
@@ -52,6 +53,12 @@ func runDashboard(c *Ctx, args []string) int {
5253 CreatedAt string `json:"created_at"`
5354 FinishedAt string `json:"finished_at,omitempty"`
5455 }
56 // serverOut is admin-only. The exact build a host is running narrows down
57 // which known issues apply to it, so it is not everyone's to read; the
58 // person who needs it is the operator.
59 type serverOut struct {
60 Commit string `json:"commit"`
61 }
5562 type out struct {
5663 Reviews []dashboardItem `json:"review_queue"`
5764 Assigned []dashboardItem `json:"assigned_issues"`
@@ -60,6 +67,7 @@ func runDashboard(c *Ctx, args []string) int {
6067 Pinned []pinnedOut `json:"pinned"`
6168 Activity []feedOut `json:"recent_activity"`
6269 Builds []buildOut `json:"builds"`
70 Server *serverOut `json:"server,omitempty"`
6371 }
6472 d := out{
6573 Reviews: []dashboardItem{}, Assigned: []dashboardItem{}, MRs: []dashboardItem{},
@@ -127,6 +135,9 @@ func runDashboard(c *Ctx, args []string) int {
127135 for _, b := range builds {
128136 d.Builds = append(d.Builds, buildOut{b.RepoPath, b.Number, b.Job, b.Status, b.SHA, b.Ref, b.CreatedAt, b.FinishedAt})
129137 }
138 if c.User.IsAdmin {
139 d.Server = &serverOut{Commit: buildinfo.String()}
140 }
130141
131142 return c.emit(d, func(w io.Writer) {
132143 fmt.Fprintln(w, "waiting on your review:")
@@ -153,6 +164,9 @@ func runDashboard(c *Ctx, args []string) int {
153164 for _, b := range d.Builds {
154165 fmt.Fprintf(w, " %s\t%d\t%s\t%s\t%.10s\t%s\n", b.Repo, b.Number, b.Job, b.Status, b.SHA, b.Ref)
155166 }
167 if d.Server != nil {
168 fmt.Fprintf(w, "server:\n build %s\n", d.Server.Commit)
169 }
156170 })
157171}
158172