Commit 255a74d8cf
255a74d8cf7ae143317557d5be6eacea9b331dea
parent: eb453997f3
Verified · cmc ci/build: success
cmc <hello@cleberg.net> · 2026-08-30T04:30:05Z
dashboard: report the running build to admins
The startup warning writes the commit to the journal once and never
again, so the running build is invisible to anyone not reading logs at
the moment of a restart. dashboard now carries it.
Admin-only: the exact build a host runs narrows down which known issues
apply to it. omitempty drops the key for everyone else rather than
reporting an empty string.
Ref #28.
e2e/dashboardbuild_test.go
added
+48
| @@ -0,0 +1,48 @@ |
| 1 | package e2e |
| 2 | |
| 3 | import ( |
| 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. |
| 11 | func 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 ( |
| 6 | 6 | "io" |
| 7 | 7 | "strconv" |
| 8 | 8 | |
| 9 | "gitbay.org/gitbay/internal/buildinfo" |
| 9 | 10 | "gitbay.org/gitbay/internal/gitutil" |
| 10 | 11 | "gitbay.org/gitbay/internal/policy" |
| 11 | 12 | "gitbay.org/gitbay/internal/protocol" |
| @@ -52,6 +53,12 @@ func runDashboard(c *Ctx, args []string) int { |
| 52 | 53 | CreatedAt string `json:"created_at"` |
| 53 | 54 | FinishedAt string `json:"finished_at,omitempty"` |
| 54 | 55 | } |
| 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 | } |
| 55 | 62 | type out struct { |
| 56 | 63 | Reviews []dashboardItem `json:"review_queue"` |
| 57 | 64 | Assigned []dashboardItem `json:"assigned_issues"` |
| @@ -60,6 +67,7 @@ func runDashboard(c *Ctx, args []string) int { |
| 60 | 67 | Pinned []pinnedOut `json:"pinned"` |
| 61 | 68 | Activity []feedOut `json:"recent_activity"` |
| 62 | 69 | Builds []buildOut `json:"builds"` |
| 70 | Server *serverOut `json:"server,omitempty"` |
| 63 | 71 | } |
| 64 | 72 | d := out{ |
| 65 | 73 | Reviews: []dashboardItem{}, Assigned: []dashboardItem{}, MRs: []dashboardItem{}, |
| @@ -127,6 +135,9 @@ func runDashboard(c *Ctx, args []string) int { |
| 127 | 135 | for _, b := range builds { |
| 128 | 136 | d.Builds = append(d.Builds, buildOut{b.RepoPath, b.Number, b.Job, b.Status, b.SHA, b.Ref, b.CreatedAt, b.FinishedAt}) |
| 129 | 137 | } |
| 138 | if c.User.IsAdmin { |
| 139 | d.Server = &serverOut{Commit: buildinfo.String()} |
| 140 | } |
| 130 | 141 | |
| 131 | 142 | return c.emit(d, func(w io.Writer) { |
| 132 | 143 | fmt.Fprintln(w, "waiting on your review:") |
| @@ -153,6 +164,9 @@ func runDashboard(c *Ctx, args []string) int { |
| 153 | 164 | for _, b := range d.Builds { |
| 154 | 165 | 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) |
| 155 | 166 | } |
| 167 | if d.Server != nil { |
| 168 | fmt.Fprintf(w, "server:\n build %s\n", d.Server.Commit) |
| 169 | } |
| 156 | 170 | }) |
| 157 | 171 | } |
| 158 | 172 | |