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.

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/&lt;ulid&gt;/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/&lt;ulid&gt;/request.md</code> naming the <code>base</code> branch and the <code>head</code>
378 383 commit to land, plus <code>approvals/&lt;account-id&gt;.md</code> from each approver.</p>
379 384 {{end}}