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: the CLI knows who you are and where you wrote it
a63475ce8a689107f6572150d7c58be1d623f6f0 · Keenan Nemetz · 17d ago · parent 79c88d72
Phase 7: the CLI knows who you are and where you wrote it Three things a first-time user hit in a row, all measured against a live instance before they were fixed. 1. Identity was guessed. Every issue and request was stamped with a name from `git config user.name`, sanitised: "Keenan Nemetz" became `keenan-nemetz`, an account that does not exist, and nothing complained — the issue page showed it, and the assignee rule accepted it because that rule only checks the shape. `vole-whoami` is a new SSH verb, and `vole whoami` is the CLI command that reads it: the instance is the only place a key maps to an account, and it is one call on the connection already open. `vole issue` and `vole request` stamp the account's current name and id, and `--author` is gone from both, because a name you can type is a name you can get wrong. Read commands still need no identity — `issue list` and `issue show` keep working with the instance unreachable — and the git-config guess survives only as a warned fallback for `--no-push`. 2. `--remote ssh://…` did not fetch. `fetch` checked for a configured remote name, so a URL was skipped: the first write in a fresh repository succeeded (nothing to fast-forward from) and every later one pushed from a tip that had never been updated, was rejected, and then retried as a "lost race" that never happened, reporting `already exists`. A URL is now fetched like a named remote, so `git init` plus a URL is the real no-clone filing path the roadmap described. 3. A command answered with an id and stopped. `new` and `comment` now print the page they wrote, and a comment link lands on that comment: the instance reports its own `public_url` in the same whoami reply (new optional config key), and the web puts an `id="c-<ulid>"` anchor on every comment. Empty states on the issues and requests pages print the command that fills them, because four of the five things a reader wants to do are writes the web cannot take, and "no comments yet" is a dead end. Verified live, not only in tests: `vole whoami` in a clone whose `user.name` is "Keenan Nemetz" printed `keenan` while the issue it then filed carried `author: keenan` on the branch; two repositories that never clone both wrote through the URL (the exact case that failed before); and a comment printed a `#c-01M5N3M0…` permalink whose anchor is on the served page. Tests: TestWhoAmIAnswersFromTheInstance (including `--author` now being an unknown option), TestReadCommandsNeedNoIdentity (closes the SSH listener, then lists), TestRequestAuthorComesFromTheKey, TestAURLRemoteIsFetchedBeforeWriting, TestEmptyStatesPrintAWorkingCommand, and the comment-empty-state assertion added to TestIssuePushedThenRendered. scripts/ci.sh green (gofmt, vet, build, markdown links, go test -race ./...).
cmd/vole/whoami.go +55
| @@ -0,0 +1,55 @@ | |||
| 1 | package main | ||
| 2 | |||
| 3 | // `vole whoami` is the one command that answers "which account am I", and the | ||
| 4 | // answer comes from the instance rather than from git config. Before it existed, | ||
| 5 | // every metadata write stamped a name guessed from `user.name`, which on an | ||
| 6 | // ordinary developer machine is "Keenan Nemetz" and sanitises to `keenan-nemetz` | ||
| 7 | // — a name no account holds, shown on the web as the author of the issue and | ||
| 8 | // accepted by the assignee rule because that rule only checks the shape. | ||
| 9 | |||
| 10 | import ( | ||
| 11 | "fmt" | ||
| 12 | "os" | ||
| 13 | ) | ||
| 14 | |||
| 15 | func whoamiUsage() string { | ||
| 16 | return `usage: vole whoami [--id] [--remote <name>] | ||
| 17 | |||
| 18 | Asks the instance which account the SSH key you push with belongs to. That | ||
| 19 | mapping exists only on the instance; git config holds a name somebody typed. | ||
| 20 | |||
| 21 | --id print the stable account id instead of the name | ||
| 22 | --remote the remote naming the instance (name or ssh:// URL); default origin` | ||
| 23 | } | ||
| 24 | |||
| 25 | func cmdWhoami(args []string) error { | ||
| 26 | f, err := parseFlags(args, flagSpec{long: "id", boolean: true}, flagSpec{long: "remote"}) | ||
| 27 | if err != nil { | ||
| 28 | return err | ||
| 29 | } | ||
| 30 | if f.help { | ||
| 31 | fmt.Fprintln(os.Stderr, whoamiUsage()) | ||
| 32 | return nil | ||
| 33 | } | ||
| 34 | if len(f.rest) > 0 { | ||
| 35 | return usagef("unexpected argument %q", f.rest[0]) | ||
| 36 | } | ||
| 37 | dir, err := currentGitDir() | ||
| 38 | if err != nil { | ||
| 39 | return err | ||
| 40 | } | ||
| 41 | remote := f.one("remote") | ||
| 42 | if remote == "" { | ||
| 43 | remote = "origin" | ||
| 44 | } | ||
| 45 | a, err := askIdentity(dir, remote) | ||
| 46 | if err != nil { | ||
| 47 | return err | ||
| 48 | } | ||
| 49 | if f.has("id") { | ||
| 50 | fmt.Println(a.ID) | ||
| 51 | return nil | ||
| 52 | } | ||
| 53 | fmt.Println(a.Name) | ||
| 54 | return nil | ||
| 55 | } | ||