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 (
6 "io" 6 "io"
7 "strconv" 7 "strconv"
8 8
9 "gitbay.org/gitbay/internal/buildinfo"
9 "gitbay.org/gitbay/internal/gitutil" 10 "gitbay.org/gitbay/internal/gitutil"
10 "gitbay.org/gitbay/internal/policy" 11 "gitbay.org/gitbay/internal/policy"
11 "gitbay.org/gitbay/internal/protocol" 12 "gitbay.org/gitbay/internal/protocol"
@@ -52,6 +53,12 @@ func runDashboard(c *Ctx, args []string) int {
52 CreatedAt string `json:"created_at"` 53 CreatedAt string `json:"created_at"`
53 FinishedAt string `json:"finished_at,omitempty"` 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 type out struct { 62 type out struct {
56 Reviews []dashboardItem `json:"review_queue"` 63 Reviews []dashboardItem `json:"review_queue"`
57 Assigned []dashboardItem `json:"assigned_issues"` 64 Assigned []dashboardItem `json:"assigned_issues"`
@@ -60,6 +67,7 @@ func runDashboard(c *Ctx, args []string) int {
60 Pinned []pinnedOut `json:"pinned"` 67 Pinned []pinnedOut `json:"pinned"`
61 Activity []feedOut `json:"recent_activity"` 68 Activity []feedOut `json:"recent_activity"`
62 Builds []buildOut `json:"builds"` 69 Builds []buildOut `json:"builds"`
70 Server *serverOut `json:"server,omitempty"`
63 } 71 }
64 d := out{ 72 d := out{
65 Reviews: []dashboardItem{}, Assigned: []dashboardItem{}, MRs: []dashboardItem{}, 73 Reviews: []dashboardItem{}, Assigned: []dashboardItem{}, MRs: []dashboardItem{},
@@ -127,6 +135,9 @@ func runDashboard(c *Ctx, args []string) int {
127 for _, b := range builds { 135 for _, b := range builds {
128 d.Builds = append(d.Builds, buildOut{b.RepoPath, b.Number, b.Job, b.Status, b.SHA, b.Ref, b.CreatedAt, b.FinishedAt}) 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 return c.emit(d, func(w io.Writer) { 142 return c.emit(d, func(w io.Writer) {
132 fmt.Fprintln(w, "waiting on your review:") 143 fmt.Fprintln(w, "waiting on your review:")
@@ -153,6 +164,9 @@ func runDashboard(c *Ctx, args []string) int {
153 for _, b := range d.Builds { 164 for _, b := range d.Builds {
154 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) 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