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 ./...).
internal/web/templates.go +10−5
| @@ -303,9 +303,11 @@ select{background:var(--card);color:var(--fg);border:1px solid var(--line);borde | |||
| 303 | 303 | {{else if .Filtered}} | |
| 304 | 304 | <p class="muted">No issues match that filter.</p> | |
| 305 | 305 | {{else}} | |
| 306 | <p class="muted">No issues yet. An issue is one file on the <code>vole</code> branch: | ||
| 306 | <p class="muted">No issues yet. From a clone of this repository: | ||
| 307 | <code>vole issue new --title "..." -m "..."</code></p> | ||
| 308 | <p class="muted">An issue is also just a file on the <code>vole</code> branch: | ||
| 307 | 309 | <code>issues/<ulid>/issue.md</code> — a frontmatter block with the title, state and | |
| 308 | labels, then the body as markdown.</p> | ||
| 310 | labels, then the body as markdown, which you can write with any editor.</p> | ||
| 309 | 311 | {{end}} | |
| 310 | 312 | {{template "footer" .}} | |
| 311 | 313 | {{end}} | |
| @@ -321,12 +323,13 @@ labels, then the body as markdown.</p> | |||
| 321 | 323 | <article class="md">{{.Description}}</article> | |
| 322 | 324 | <h2>{{len .Comments}} comment(s)</h2> | |
| 323 | 325 | {{range .Comments}} | |
| 324 | <div class="card"> | ||
| 326 | <div class="card" id="c-{{.ID}}"> | ||
| 325 | 327 | <p class="muted"><span class="sha">{{short .ID}}</span> · {{.Author}} · {{since .Created.Unix}}</p> | |
| 326 | 328 | <article class="md">{{.Body}}</article> | |
| 327 | 329 | </div> | |
| 328 | 330 | {{else}} | |
| 329 | <p class="muted">No comments yet.</p> | ||
| 331 | <p class="muted">No comments yet. From a clone of this repository: | ||
| 332 | <code>vole issue comment {{.Issue.ID}} -m "..."</code></p> | ||
| 330 | 333 | {{end}} | |
| 331 | 334 | {{if .Backlinks}} | |
| 332 | 335 | <h2>Referenced in commits</h2> | |
| @@ -373,7 +376,9 @@ labels, then the body as markdown.</p> | |||
| 373 | 376 | {{end}} | |
| 374 | 377 | </table> | |
| 375 | 378 | {{else}} | |
| 376 | <p class="muted">No change requests. A request is a directory on the <code>vole</code> branch: | ||
| 379 | <p class="muted">No change requests. From a clone of this repository: | ||
| 380 | <code>vole request new --title "..." --base main</code></p> | ||
| 381 | <p class="muted">A request is also just a directory on the <code>vole</code> branch: | ||
| 377 | 382 | <code>requests/<ulid>/request.md</code> naming the <code>base</code> branch and the <code>head</code> | |
| 378 | 383 | commit to land, plus <code>approvals/<account-id>.md</code> from each approver.</p> | |
| 379 | 384 | {{end}} | |