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/hook/names_test.go +5−5

@@ -7,7 +7,7 @@ import (
7 7 "github.com/rodent-software/vole/forge/identity"
8 8 )
9 9
10 func TestOpenNamesResolves(t *testing.T) {
10 func TestOpenLabelsResolves(t *testing.T) {
11 11 t.Parallel()
12 12 path := filepath.Join(t.TempDir(), "users.db")
13 13 if err := identity.Init(path); err != nil {
@@ -19,12 +19,12 @@ func TestOpenNamesResolves(t *testing.T) {
19 19 }
20 20 defer s.Close()
21 21 _, pub := writeKey(t, filepath.Join(t.TempDir(), "key"))
22 a, err := s.Create("keenan", pub, "test")
22 a, err := s.Create("keenan", "Keenan O'Brien", pub, "test")
23 23 if err != nil {
24 24 t.Fatal(err)
25 25 }
26 got := openNames(path)(a.ID)
27 if got != "keenan" {
28 t.Fatalf("openNames = %q, want keenan (id %s)", got, a.ID)
26 got := openLabels(path)(a.ID)
27 if got != "Keenan O'Brien" {
28 t.Fatalf("openLabels = %q, want the display name (id %s)", got, a.ID)
29 29 }
30 30 }