Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Build out the forge web UI

f09ef39efe44de693748053a7b596ef328fcf06b · Keenan Nemetz · 15d ago · parent 70b70264

Build out the forge web UI

Turn the web surface into a compact, server-rendered forge with HTMX as the
progressive dynamic layer.

- Split templates and assets into embedded files; add a tokenised dark theme,
  Lucide icons, and vendored HTMX.
- Add the repository Overview page and a separate chronological Activity feed
  with type filters and HTMX fragment updates.
- Finish the issue and change workflows: create pages, comments, close/reopen,
  labels and assignees, approval, discussion, line-anchored review comments,
  and HTMX list filters.
- Add syntax-highlighted blobs, contributor/statistics derivation, branch-aware
  code browsing, and a repository Runs list.
- Add a loopback-only development login for local write testing, with explicit
  configuration and listener checks.
- Extend tests for the new pages, fragments, labels, assignees, anchors, and
  development login.

server/web/templates/runners.html +77

@@ -0,0 +1,77 @@
1 {{define "runners"}}
2 {{template "header" .}}
3 <h1>Runners</h1>
4 <p class="muted">A runner is an SSH connection that leases jobs. Nothing is registered: a
5 channel that is open and being refreshed is a runner, and it disappears when it closes.</p>
6 {{if .Sessions}}
7 <table>
8 <tr><th>Runner</th><th>Takes</th><th>State</th><th>Last seen</th></tr>
9 {{range .Sessions}}
10 <tr>
11 <td>{{.Account}}</td>
12 <td class="muted">{{if .Labels}}{{.Labels}}{{else}}<span class="muted">any job</span>{{end}}</td>
13 <td>{{if .Idle}}<span class="muted">waiting for work</span>{{else if .RunID}}<a href="/r/{{.Repo}}/runs/{{.RunID}}">{{.Job}}</a> on <a href="/r/{{.Repo}}">{{.Repo}}</a>{{else}}<span class="muted">busy</span>{{end}}</td>
14 <td class="muted">{{since .Seen.Unix}}</td>
15 </tr>
16 {{end}}
17 </table>
18 {{else}}
19 <p class="muted">No runner is connected: none has been seen in the last {{.Window}}. Start one
20 with <code>vole runner --remote ssh://git@&lt;host&gt;</code>. If a job asks for a machine with
21 <code>runs-on</code>, the runner has to carry that label with <code>--label</code>.</p>
22 {{end}}
23
24 <h2>Queue</h2>
25 {{if .Queue}}
26 <table>
27 <tr><th>Repository</th><th>Waiting</th><th>Running</th></tr>
28 {{range .Queue}}
29 <tr>
30 <td><a href="/r/{{.Name}}">{{.Name}}</a></td>
31 <td class="muted">{{.Queued}}</td>
32 <td class="muted">{{.Running}}{{if .Cap}}{{if ge .Running .Cap}} of {{.Cap}} <span class="muted">(at the cap)</span>{{else}} of {{.Cap}}{{end}}{{end}}</td>
33 </tr>
34 {{end}}
35 </table>
36 {{else}}
37 <p class="muted">Nothing is waiting and nothing is running.</p>
38 {{end}}
39 {{template "footer" .}}
40 {{end}}
41
42 {{define "run"}}
43 {{template "header" .}}
44 {{template "repohead" (nav .Name "runs")}}
45 <h1>Run · {{.Run.Job}}</h1>
46 <p class="muted">
47 {{.Run.Workflow}} · {{if .Run.Tag}}tag {{.Run.Tag}}{{else}}{{.Run.Branch}}{{end}} at <a class="sha" href="/r/{{.Name}}/commit/{{.Run.SHA}}">{{short .Run.SHA}}</a>
48 · {{.Run.Status}}{{if eq .Run.Status "failed"}} (exit {{.Run.ExitCode}}){{end}}
49 · attempt {{.Run.Attempts}}
50 · {{since .Run.Created.Unix}}
51 </p>
52 {{if .Run.OS}}<p class="muted">ran on {{.Run.OS}}/{{.Run.Arch}}{{if .Run.Image}} · in {{if .Run.ImageFile}}image-file <code>{{.Run.ImageFile}}</code> ({{short .Run.ImageSHA}}){{else}}<code>{{.Run.Image}}</code>{{end}} via {{.Run.Runtime}}{{end}}</p>{{end}}
53 {{if .Attestation}}<p class="muted">reported by <strong>{{.Signer}}</strong>{{if .Attestation.CommitID}} · <a href="/r/{{.Name}}/commit/{{.Attestation.CommitID}}">the commit that records it</a>{{end}} · the instance accepted this from that account's key at the push boundary, and logged it</p>{{end}}
54 {{if .Counts}}<p class="muted">counts as a check for this repository</p>{{else if .CountsWhy}}<p class="muted">does not count as a check: {{.CountsWhy}}</p>{{end}}
55 {{if .HasLog}}<p><a href="/r/{{.Name}}/runs/{{.Run.ID}}/log">log</a>{{if gt (len .Logs) 1}} · by attempt:
56 {{range .Logs}}<a href="/r/{{$.Name}}/runs/{{$.Run.ID}}/log?attempt={{.Attempt}}">{{.Attempt}}</a> {{end}}{{end}}</p>{{end}}
57
58 <h2>Artifacts</h2>
59 {{if .Artifacts}}
60 <table>
61 <tr><th>File</th><th>Size</th><th>SHA-256</th><th></th></tr>
62 {{range .Artifacts}}
63 <tr>
64 <td><a href="/r/{{$.Name}}/runs/{{$.Run.ID}}/artifacts/{{.Name}}">{{.Name}}</a></td>
65 <td class="muted">{{bytes .Size}}</td>
66 <td class="muted"><code>{{short .OID}}</code></td>
67 <td><a href="/r/{{$.Name}}/runs/{{$.Run.ID}}/artifacts/{{.Name}}">download</a></td>
68 </tr>
69 {{end}}
70 </table>
71 <p class="muted">Built from {{short .Run.SHA}} by this run. The bytes live once in the
72 instance's content-addressed store, and are swept only when nothing names them.</p>
73 {{else}}
74 <p class="muted">No artifacts published by this run.</p>
75 {{end}}
76 {{template "footer" .}}
77 {{end}}