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.
Phase 7 slice 1: review on the web, so approving does not start with a clone
78912785b00227cffe3f8fb26b057cd60159a6b4 · Keenan Nemetz · 18d ago · parent 9bea7f11
Phase 7 slice 1: review on the web, so approving does not start with a clone Phase 7 is re-scoped: signing is deferred (the SSH handshake already establishes who pushed, and there is no third-party verifier here), and the phase is now about the experience of using the forge. - GET /r/<repo>/commit/<sha>: a commit's metadata and the patch it introduced, per-file hunks with line numbers; the commit list links to it - change requests show the same diff, computed three-dot: a review is what the branch adds since it diverged, not every commit main gained meanwhile - diffs are capped at 512 KiB and labelled when cut, binary files are named rather than faked, and every line is escaped (a <script> line is a test) - the repository README renders on its home page and markdown blobs render as markdown, through the sanitiser issues already use
internal/web/templates.go +56−7
| @@ -46,6 +46,18 @@ select{background:var(--card);color:var(--fg);border:1px solid var(--line);borde | |||
| 46 | 46 | .md blockquote{margin:.6rem 0;padding-left:.8rem;border-left:3px solid var(--line);color:var(--muted)} | |
| 47 | 47 | .md ul,.md ol{padding-left:1.4rem} | |
| 48 | 48 | .md table{width:auto} | |
| 49 | .diff{border:1px solid var(--line);border-radius:8px;margin:1rem 0;overflow:hidden} | ||
| 50 | .diff h3{margin:0;padding:.55rem .8rem;background:var(--card);border-bottom:1px solid var(--line);font-size:.9rem;font-weight:600} | ||
| 51 | .diff table{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.82rem;border-collapse:collapse} | ||
| 52 | .diff td{border:0;padding:0 .5rem;white-space:pre-wrap;word-break:break-word;vertical-align:top} | ||
| 53 | .diff td.ln{width:3.5rem;text-align:right;color:var(--muted);user-select:none;border-right:1px solid var(--line)} | ||
| 54 | .diff td.code{padding-left:.75rem} | ||
| 55 | .diff tr.add{background:rgba(46,160,67,.12)} | ||
| 56 | .diff tr.add td.ln{color:#3fb950} | ||
| 57 | .diff tr.del{background:rgba(248,81,73,.10)} | ||
| 58 | .diff tr.del td.ln{color:var(--bad)} | ||
| 59 | .diff tr.hunk td{color:var(--muted);background:var(--card);padding:.25rem .5rem;font-size:.78rem} | ||
| 60 | .diff .binary{padding:.7rem .8rem;color:var(--muted);font-size:.85rem} | ||
| 49 | 61 | </style> | |
| 50 | 62 | </head> | |
| 51 | 63 | <body> | |
| @@ -107,6 +119,7 @@ select{background:var(--card);color:var(--fg);border:1px solid var(--line);borde | |||
| 107 | 119 | <h1>{{.Name}}</h1> | |
| 108 | 120 | {{if .Description}}<p class="muted">{{.Description}}</p>{{end}} | |
| 109 | 121 | {{if .Site}}<p class="muted">Site: <a href="{{.Site}}">{{.Site}}</a></p>{{end}} | |
| 122 | {{if .Readme}}<div class="md readme">{{.Readme}}</div>{{end}} | ||
| 110 | 123 | ||
| 111 | 124 | <div class="row"> | |
| 112 | 125 | <div> | |
| @@ -159,8 +172,8 @@ select{background:var(--card);color:var(--fg);border:1px solid var(--line);borde | |||
| 159 | 172 | <table> | |
| 160 | 173 | {{range .Commits}} | |
| 161 | 174 | <tr> | |
| 162 | <td><span class="sha">{{short .SHA}}</span></td> | ||
| 163 | <td>{{.Subject}}</td> | ||
| 175 | <td><a class="sha" href="/r/{{$.Name}}/commit/{{.SHA}}">{{short .SHA}}</a></td> | ||
| 176 | <td><a href="/r/{{$.Name}}/commit/{{.SHA}}">{{.Subject}}</a></td> | ||
| 164 | 177 | <td class="muted">{{.Author}}</td> | |
| 165 | 178 | <td class="muted">{{since .When}}</td> | |
| 166 | 179 | </tr> | |
| @@ -193,6 +206,41 @@ select{background:var(--card);color:var(--fg);border:1px solid var(--line);borde | |||
| 193 | 206 | {{template "footer" .}} | |
| 194 | 207 | {{end}} | |
| 195 | 208 | ||
| 209 | {{define "diff"}} | ||
| 210 | <p class="muted">{{len .Files}} file(s) changed{{if .Added}} · <span class="adds">+{{.Added}}</span>{{end}}{{if .Removed}} · <span class="dels">−{{.Removed}}</span>{{end}}{{if .Truncated}} · showing the first 512 KiB{{end}}</p> | ||
| 211 | {{range .Files}} | ||
| 212 | <div class="diff"> | ||
| 213 | <h3>{{if and .OldPath (ne .OldPath .NewPath)}}{{.OldPath}} → {{end}}{{.Label}}{{if .Binary}} <span class="muted">binary</span>{{end}}</h3> | ||
| 214 | {{if .Binary}} | ||
| 215 | <p class="binary">Binary file: no line diff to show. Use <code>git show</code> to inspect it.</p> | ||
| 216 | {{end}} | ||
| 217 | {{if .Hunks}} | ||
| 218 | <table> | ||
| 219 | {{range .Hunks}} | ||
| 220 | <tr class="hunk"><td colspan="3">{{.Header}}</td></tr> | ||
| 221 | {{range .Lines}} | ||
| 222 | <tr class="{{.Kind}}"> | ||
| 223 | <td class="ln">{{if .OldNo}}{{.OldNo}}{{end}}</td> | ||
| 224 | <td class="ln">{{if .NewNo}}{{.NewNo}}{{end}}</td> | ||
| 225 | <td class="code">{{.Text}}</td> | ||
| 226 | </tr> | ||
| 227 | {{end}} | ||
| 228 | {{end}} | ||
| 229 | </table> | ||
| 230 | {{end}} | ||
| 231 | </div> | ||
| 232 | {{end}} | ||
| 233 | {{end}} | ||
| 234 | |||
| 235 | {{define "commit"}} | ||
| 236 | {{template "header" .}} | ||
| 237 | <h1>{{.Commit.Subject}}</h1> | ||
| 238 | <p class="muted"><span class="sha">{{.Commit.SHA}}</span> · {{.Commit.Author}} · {{since .Commit.When}}</p> | ||
| 239 | {{if .Body}}<pre>{{.Body}}</pre>{{end}} | ||
| 240 | {{template "diff" .}} | ||
| 241 | {{template "footer" .}} | ||
| 242 | {{end}} | ||
| 243 | |||
| 196 | 244 | {{define "blob"}} | |
| 197 | 245 | {{template "header" .}} | |
| 198 | 246 | <h1><a href="/r/{{.Name}}">{{.Name}}</a></h1> | |
| @@ -205,7 +253,8 @@ select{background:var(--card);color:var(--fg);border:1px solid var(--line);borde | |||
| 205 | 253 | <p class="muted">Binary file, {{.Size}} bytes. <a href="/r/{{.Name}}/raw/{{esc .Ref}}/{{.Path}}">Download it</a>.</p> | |
| 206 | 254 | {{else}} | |
| 207 | 255 | {{if .Truncated}}<p class="muted">Showing the first 1 MiB.</p>{{end}} | |
| 208 | <pre>{{.Content}}</pre> | ||
| 256 | {{if .Markdown}}<div class="md">{{.Markdown}}</div> | ||
| 257 | {{else}}<pre>{{.Content}}</pre>{{end}} | ||
| 209 | 258 | {{end}} | |
| 210 | 259 | {{template "footer" .}} | |
| 211 | 260 | {{end}} | |
| @@ -275,8 +324,8 @@ select{background:var(--card);color:var(--fg);border:1px solid var(--line);borde | |||
| 275 | 324 | <table> | |
| 276 | 325 | {{range .Backlinks}} | |
| 277 | 326 | <tr> | |
| 278 | <td><span class="sha">{{short .SHA}}</span></td> | ||
| 279 | <td>{{.Subject}}</td> | ||
| 327 | <td><a class="sha" href="/r/{{$.Name}}/commit/{{.SHA}}">{{short .SHA}}</a></td> | ||
| 328 | <td><a href="/r/{{$.Name}}/commit/{{.SHA}}">{{.Subject}}</a></td> | ||
| 280 | 329 | <td class="muted">{{.Author}}</td> | |
| 281 | 330 | <td class="muted">{{since .When}}</td> | |
| 282 | 331 | </tr> | |
| @@ -371,8 +420,8 @@ commit to land, plus <code>approvals/<user>.json</code> from each approver | |||
| 371 | 420 | <h2>What would land</h2> | |
| 372 | 421 | {{if .AlreadyMerged}} | |
| 373 | 422 | <p class="muted">Already merged into {{.Request.Base}}.</p> | |
| 374 | {{else if .Changed}} | ||
| 375 | <ul class="files">{{range .Changed}}<li class="sha">{{.}}</li>{{end}}</ul> | ||
| 423 | {{else if .Files}} | ||
| 424 | {{template "diff" .}} | ||
| 376 | 425 | {{else}} | |
| 377 | 426 | <p class="muted">No changes, or the head is not reachable from this repository yet.</p> | |
| 378 | 427 | {{end}} | |