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.
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 ./...).
internal/sshserver/sshserver.go +16−1
| @@ -57,6 +57,9 @@ type Config struct { | |||
| 57 | 57 | // PagesRef is the branch a site is served from. The runner needs it because the | |
| 58 | 58 | // workflow cannot know instance configuration. | |
| 59 | 59 | PagesRef string | |
| 60 | // PublicURL is where a browser reads this instance, handed to `vole-whoami` so | ||
| 61 | // the CLI can print a link to what it just created. Empty is fine. | ||
| 62 | PublicURL string | ||
| 60 | 63 | } | |
| 61 | 64 | ||
| 62 | 65 | type Server struct { | |
| @@ -229,8 +232,20 @@ func (s *Server) exec(ch ssh.Channel, req *ssh.Request, command, user, accountID | |||
| 229 | 232 | exit(ch, 0) | |
| 230 | 233 | return | |
| 231 | 234 | } | |
| 235 | // `vole whoami` asks the instance which account the presented key is. That | ||
| 236 | // mapping exists only here: a client's git config holds a name somebody typed, | ||
| 237 | // and guessing from it is how an issue ends up filed by an account that does | ||
| 238 | // not exist. The third field is where a browser reads this instance, empty | ||
| 239 | // when the operator has not set public_url — the caller that asks "who am I" | ||
| 240 | // is usually the same one that wants to print a link to what it just wrote. | ||
| 241 | if len(argv) == 1 && argv[0] == "vole-whoami" { | ||
| 242 | req.Reply(true, nil) | ||
| 243 | fmt.Fprintf(ch, "%s %s %s\n", accountID, s.cfg.Identity.Name(accountID), s.cfg.PublicURL) | ||
| 244 | exit(ch, 0) | ||
| 245 | return | ||
| 246 | } | ||
| 232 | 247 | if len(argv) != 2 { | |
| 233 | refuse("only git-upload-pack, git-receive-pack and vole-roster are available") | ||
| 248 | refuse("only git-upload-pack, git-receive-pack, vole-roster, vole-whoami and vole-runner are available") | ||
| 234 | 249 | return | |
| 235 | 250 | } | |
| 236 | 251 | verb := argv[0] | |