vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Add the status page and language-aware file icons
09b15120717116d07d0227444bdb2d2a499164b1 · Keenan Nemetz · 15d ago · parent b355db72
Add the status page and language-aware file icons GET /status is the human version of /healthz: store checks, account/repository counts, queue depth, runners seen recently, LFS availability, and uptime, with a footer link. /healthz stays a plain-text probe for scripts. The code tree, directory list, and file search now choose an icon and language colour from the file extension, with build files and go.mod handled by name. Both are presentation only; unknown files keep the generic icon.
server/web/status.go +95
| @@ -0,0 +1,95 @@ | |||
| 1 | package web | ||
| 2 | |||
| 3 | // The status page is the human view of what /healthz answers for a probe: the | ||
| 4 | // stores are open, the queue is readable, and a runner has been seen recently. | ||
| 5 | // It exposes counts, never repository contents. | ||
| 6 | |||
| 7 | import ( | ||
| 8 | "fmt" | ||
| 9 | "net/http" | ||
| 10 | "time" | ||
| 11 | ) | ||
| 12 | |||
| 13 | type statusCheck struct { | ||
| 14 | Name string | ||
| 15 | State string // ok, warn, fail | ||
| 16 | Detail string | ||
| 17 | } | ||
| 18 | |||
| 19 | func (w *Web) StatusPage(rw http.ResponseWriter, r *http.Request) { | ||
| 20 | view := struct { | ||
| 21 | Page | ||
| 22 | Checks []statusCheck | ||
| 23 | Healthy bool | ||
| 24 | Users int | ||
| 25 | Repos int | ||
| 26 | Queued int | ||
| 27 | Running int | ||
| 28 | Runners int | ||
| 29 | Started time.Time | ||
| 30 | }{Page: w.page(r, "Status"), Started: w.Started} | ||
| 31 | healthy := true | ||
| 32 | add := func(name, state, detail string) { | ||
| 33 | if state == "fail" { | ||
| 34 | healthy = false | ||
| 35 | } | ||
| 36 | view.Checks = append(view.Checks, statusCheck{Name: name, State: state, Detail: detail}) | ||
| 37 | } | ||
| 38 | |||
| 39 | if w.Ident != nil { | ||
| 40 | view.Users = w.Ident.Snapshot().Count | ||
| 41 | add("identity", "ok", fmt.Sprintf("%d active account(s)", view.Users)) | ||
| 42 | } else { | ||
| 43 | add("identity", "fail", "identity store is not wired") | ||
| 44 | } | ||
| 45 | if w.Repos != nil { | ||
| 46 | if repos, err := w.Repos.List(); err == nil { | ||
| 47 | view.Repos = len(repos) | ||
| 48 | add("repositories", "ok", fmt.Sprintf("%d repository(s)", view.Repos)) | ||
| 49 | } else { | ||
| 50 | add("repositories", "fail", err.Error()) | ||
| 51 | } | ||
| 52 | } else { | ||
| 53 | add("repositories", "fail", "repository store is not wired") | ||
| 54 | } | ||
| 55 | if w.Index != nil { | ||
| 56 | if repos, err := w.Index.List(); err == nil { | ||
| 57 | add("index", "ok", fmt.Sprintf("%d indexed repository(s)", len(repos))) | ||
| 58 | } else { | ||
| 59 | add("index", "fail", err.Error()) | ||
| 60 | } | ||
| 61 | } else { | ||
| 62 | add("index", "fail", "index store is not wired") | ||
| 63 | } | ||
| 64 | if w.Runs != nil { | ||
| 65 | if queue, err := w.Runs.QueueByRepo(); err == nil { | ||
| 66 | for _, depth := range queue { | ||
| 67 | view.Queued += depth.Queued | ||
| 68 | view.Running += depth.Running | ||
| 69 | } | ||
| 70 | add("queue", "ok", fmt.Sprintf("%d queued · %d running", view.Queued, view.Running)) | ||
| 71 | } else { | ||
| 72 | add("queue", "fail", err.Error()) | ||
| 73 | } | ||
| 74 | if sessions, err := w.Runs.Sessions(time.Now().UTC().Add(-presenceWindow)); err == nil { | ||
| 75 | view.Runners = len(sessions) | ||
| 76 | state := "ok" | ||
| 77 | if view.Runners == 0 { | ||
| 78 | state = "warn" | ||
| 79 | } | ||
| 80 | add("runners", state, fmt.Sprintf("%d seen in the last %s", view.Runners, presenceWindow)) | ||
| 81 | } else { | ||
| 82 | add("runners", "fail", err.Error()) | ||
| 83 | } | ||
| 84 | } else { | ||
| 85 | add("queue", "fail", "run queue is not wired") | ||
| 86 | add("runners", "fail", "run queue is not wired") | ||
| 87 | } | ||
| 88 | if w.LFS != nil { | ||
| 89 | add("objects", "ok", "LFS object store is available") | ||
| 90 | } else { | ||
| 91 | add("objects", "warn", "LFS object store is disabled") | ||
| 92 | } | ||
| 93 | view.Healthy = healthy | ||
| 94 | w.render(rw, "status", view, 0) | ||
| 95 | } | ||