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.

Name branches after the account, and labels after the display

474a812d199a109ba894124cd6f26be12c73285d · Keenan Nemetz · 15d 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/requests.go +10−4

@@ -61,10 +61,16 @@ func (w *Web) Requests(rw http.ResponseWriter, r *http.Request) {
61 61 acl, _ := m.ACL()
62 62 canOpen := acl != nil && accountID != "" && acl.CanOpenRequest(accountID)
63 63 if acl != nil && acl.RoleFor(accountID) == access.Contributor {
64 // The namespace is named after the account name, which is fixed at
65 // creation, so this filter can never disagree with what the hook allows.
66 name := accountID
67 if w.Ident != nil {
68 name = w.Ident.Name(accountID)
69 }
64 70 headBranches = nil
65 for _, name := range branches {
66 if access.IsContributorRef(accountID, "refs/heads/"+name) {
67 headBranches = append(headBranches, name)
71 for _, branch := range branches {
72 if access.IsContributorRef(name, "refs/heads/"+branch) {
73 headBranches = append(headBranches, branch)
68 74 }
69 75 }
70 76 }
@@ -124,7 +130,7 @@ func (w *Web) RequestView(rw http.ResponseWriter, r *http.Request) {
124 130 for _, a := range req.Approvals {
125 131 name := a.Principal
126 132 if w.Ident != nil {
127 name = w.Ident.Name(a.Principal)
133 name = w.Ident.Label(a.Principal)
128 134 }
129 135 approvals = append(approvals, requestApproval{
130 136 Approval: a,