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/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.

Name branches after the account, and labels after the display

474a812d199a109ba894124cd6f26be12c73285d · Keenan Nemetz · 16d ago · parent ade6107e

Name branches after the account, and labels after the display

A contributor's folder was `refs/heads/contrib/<account-id>/`, which told a
reader nothing without decoding a ULID. It is now the account name,

    refs/heads/contrib/<name>/<branch>

and the name is therefore fixed at creation: renaming an account would strand
every ref under its old folder, so `vole admin user rename` is gone and the
users row itself — kept after retirement — is what stops a name being reissued.

What people see is now a separate, mutable display name, validated by the same
rules wherever it is set (CLI, remote admin, registration, `/account`) and shown
on pages, approvals, listings and refusals. Nothing is authorized by it and
nothing on disk is keyed by it: metadata authors stay account names because the
push boundary resolves them for ownership, and approvals stay account ids.

`user_names`, `Snapshot.Reserved` and `Account.Former` are dropped: with a fixed
name there are no former names to remember, and the UNIQUE column already does
the reserving. Opening an older roster adds the `display` column and drops the
alias table in place; contributor refs written under the old id namespace are
left where they are, readable, and landable by a maintainer.

server/web/templates.go +26−9

@@ -269,7 +269,7 @@ form.inline{display:inline}
269 269 <td class="ln">{{if .NewNo}}{{.NewNo}}{{end}}</td>
270 270 <td class="code">{{.Text}}</td>
271 271 </tr>
272 {{if .Comments}}<tr class="note"><td colspan="3">{{range .Comments}}<a class="label" href="#c-{{.ID}}">{{.Author}} commented</a>{{end}}</td></tr>{{end}}
272 {{if .Comments}}<tr class="note"><td colspan="3">{{range .Comments}}<a class="label" href="#c-{{.ID}}">{{who .Author}} commented</a>{{end}}</td></tr>{{end}}
273 273 {{end}}
274 274 {{end}}
275 275 </table>
@@ -347,7 +347,7 @@ form.inline{display:inline}
347 347 <td class="sha">{{short .ID}}</td>
348 348 <td><a href="/r/{{$.Name}}/issues/{{.ID}}">{{.Title}}</a>{{range .Labels}}<span class="label">{{.}}</span>{{end}}{{range .Assignees}} <a class="label" href="/r/{{$.Name}}/issues?assignee={{.}}">@{{.}}</a>{{end}}</td>
349 349 <td class="muted">{{.State}}</td>
350 <td class="muted">{{.Author}}</td>
350 <td class="muted">{{who .Author}}</td>
351 351 <td class="muted">{{.Comments}}</td>
352 352 <td class="muted">{{since .Created.Unix}}</td>
353 353 </tr>
@@ -369,7 +369,7 @@ labels, then the body as markdown, which you can write with any editor.</p>
369 369 {{template "header" .}}
370 370 <h1>{{.Issue.Title}}</h1>
371 371 <p class="crumbs">
372 <span class="sha">{{.Issue.ID}}</span> · {{.Issue.State}} · opened by {{.Issue.Author}} {{since .Issue.Created.Unix}}
372 <span class="sha">{{.Issue.ID}}</span> · {{.Issue.State}} · opened by {{who .Issue.Author}} {{since .Issue.Created.Unix}}
373 373 {{if .Labels}}<span class="label">{{.Labels}}</span>{{end}}
374 374 {{range .Issue.Assignees}}<a class="label" href="/r/{{$.Name}}/issues?assignee={{.}}">@{{.}}</a>{{end}}
375 375 </p>
@@ -377,7 +377,7 @@ labels, then the body as markdown, which you can write with any editor.</p>
377 377 <h2>{{len .Comments}} comment(s)</h2>
378 378 {{range .Comments}}
379 379 <div class="card" id="c-{{.ID}}">
380 <p class="muted"><span class="sha">{{short .ID}}</span> · {{.Author}} · {{since .Created.Unix}}
380 <p class="muted"><span class="sha">{{short .ID}}</span> · {{who .Author}} · {{since .Created.Unix}}
381 381 {{if .Anchor}}· on <code>{{.Anchor.Path}}:{{.Anchor.Line}}</code> ({{.Anchor.Side}} side)
382 382 {{if not .Current}}· <span class="conflict">the file has changed since this comment</span>{{end}}{{end}}
383 383 </p>
@@ -433,7 +433,7 @@ labels, then the body as markdown, which you can write with any editor.</p>
433 433 </div>
434 434 <label>Description <textarea name="body" placeholder="Why should this land?"></textarea></label>
435 435 <button type="submit">Open change request</button>
436 {{if not .HeadBranches}}<p class="muted">Push a branch first. Contributors can push only under <code>refs/heads/contrib/&lt;account-id&gt;/</code>.</p>{{end}}
436 {{if not .HeadBranches}}<p class="muted">Push a branch first. Contributors can push only under <code>refs/heads/contrib/&lt;account-name&gt;/</code>.</p>{{end}}
437 437 </form>
438 438 {{end}}
439 439 {{if .Rows}}
@@ -466,7 +466,7 @@ commit to land, plus <code>approvals/&lt;account-id&gt;.md</code> from each appr
466 466 <h1>{{.Request.Title}}</h1>
467 467 <p class="muted">
468 468 {{.Request.State}} · <span class="sha">{{short .Request.ID}}</span> ·
469 by {{.Request.Author}} · {{since .Request.Created.Unix}}
469 by {{who .Request.Author}} · {{since .Request.Created.Unix}}
470 470 </p>
471 471 <p>
472 472 <strong>{{.Request.Base}}</strong> ← <span class="sha">{{short .Request.Head}}</span>
@@ -477,7 +477,7 @@ commit to land, plus <code>approvals/&lt;account-id&gt;.md</code> from each appr
477 477 <h2>Discussion</h2>
478 478 {{range .Comments}}
479 479 <div class="card" id="c-{{.ID}}">
480 <p class="muted"><span class="sha">{{short .ID}}</span> · {{.Author}} · {{since .Created.Unix}}
480 <p class="muted"><span class="sha">{{short .ID}}</span> · {{who .Author}} · {{since .Created.Unix}}
481 481 {{if .Anchor}}· on <code>{{.Anchor.Path}}:{{.Anchor.Line}}</code> ({{.Anchor.Side}} side)
482 482 {{if not .Current}}· <span class="conflict">the file has changed since this comment</span>{{end}}{{end}}
483 483 </p>
@@ -693,10 +693,15 @@ release a description; the tarball below works either way.</p>
693 693 <label>Account name
694 694 <input type="text" name="username" pattern="[a-z0-9][a-z0-9_-]{1,31}" maxlength="32" autocomplete="username webauthn" required autofocus>
695 695 </label>
696 <label>Display name <span class="muted">(optional, shown to other people)</span>
697 <input type="text" name="display" maxlength="64" autocomplete="name">
698 </label>
696 699 <button type="submit">Create account and passkey</button>
697 700 <p class="error" data-auth-error></p>
698 701 </form>
699 <p class="muted">Names are 2–32 characters: lowercase letters, digits, <code>-</code> and <code>_</code>.</p>
702 <p class="muted">Names are 2–32 characters: lowercase letters, digits, <code>-</code> and <code>_</code>.
703 The account name is permanent — it is the folder your branches live under and cannot be
704 changed later. The display name is a label and can be changed any time.</p>
700 705 </div>
701 706 {{template "footer" .}}
702 707 {{end}}
@@ -728,7 +733,19 @@ release a description; the tarball below works either way.</p>
728 733 {{define "account"}}
729 734 {{template "header" .}}
730 735 <h1>Account</h1>
731 <p><strong>{{.Account.Name}}</strong> <span class="muted">{{.Account.ID}}</span></p>
736 <p><strong>{{.Account.Label}}</strong> <span class="muted">{{.Account.Name}} · {{.Account.ID}}</span></p>
737
738 <h2>Display name</h2>
739 <p class="muted">Shown next to your account on requests, issues and approvals. Your account
740 name <strong>{{.Account.Name}}</strong> is permanent: your branches live under
741 <code>refs/heads/contrib/{{.Account.Name}}/</code>.</p>
742 <form method="post" action="/account/display" class="card">
743 <input type="hidden" name="csrf" value="{{.CSRF}}">
744 <label>Display name
745 <input type="text" name="display" maxlength="64" value="{{.Account.Display}}" placeholder="{{.Account.Name}}">
746 </label>
747 <button type="submit">Save display name</button>
748 </form>
732 749
733 750 <h2>SSH keys</h2>
734 751 {{if .Account.Keys}}