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 2: an assignee is a person you can click
67dd8854450865e9c5d2a8643b99c9ef7f71c284 · Keenan Nemetz · 17d ago · parent 307cbc2a
Phase 2: an assignee is a person you can click `assignees:` was in the frontmatter, written by the tool and read by nothing: not validated, not set by any command, not in the index, not on a page. It now goes the whole way — the field is checked at the push boundary, the index holds it in an `issue_assignees` table mirroring `issue_labels`, and the web renders it as a filter link on both the list and the detail page. The field is held to the *roster's* account-name rule rather than the label rule. It names a person, so the label rule (which allows dots) would accept a value no account could ever have; a capital or a `@` prefix is a typo that would render as a link to nobody. It is deliberately NOT resolved against the roster at push time: a clone may be offline, an assignment may name someone who has not been created yet, and a name in an issue file is a claim on the same footing as `author` — the hook's job is the format, not the org chart. Stored as written rather than as an account id, for the same reason `author` is: the file stays readable in a plain clone, and because names are never reissued an assignment keeps meaning the same person after a rename. The index needed a schema version to go with the new table, because `EnsureIssues` only re-reads when the ref moves: an existing instance would have served rows derived from the old shape until someone happened to push. A bump now empties the derived issue rows and leaves every repo's `issues_sha` empty, so the next read rebuilds from git. The index is disposable; a stale row is worse than a rebuild. `vole issue new -a`, `assign`, `unassign`, `list --assignee` and `%(assignees)` are the CLI half. An assignment that changes nothing is not a commit — `Update` reports `ErrNoChange` and the branch does not move — so a script that reruns stays idempotent without growing a linear log. Verified: TestAssigneeRuleMatchesAccountNames compares this package's regex with identity.ValidateName over the edge cases (a name the roster accepts and the format rejects would only ever show up as a rejected push); TestAssigneesAreAccountNames drives valid, capitalised, dotted, `@`-prefixed, too-short, too-long and space-bearing values through the reader; TestAssigneeRoundTrip proves the writer emits a sorted one-per-line list that the reader reads back; TestSchemaVersionDropsRowsFromAnOlderShape writes rows an older build would have left, reopens, and checks they are gone, `issues_sha` is empty and a second open does not re-wipe; and TestIssueAssigneesRenderedFilteredAndValidated plus the CLI end-to-end test push an assignment over SSH, click it, filter by it and get a push rejected for `Wilbur` with the path and value named and the branch unmoved. scripts/ci.sh green (gofmt, vet, build, markdown links, go test -race ./...).
README.md +11−2
| @@ -145,14 +145,23 @@ The id must be a ULID (26 characters, Crockford base32 — uppercase, no `I`, `L | |||
| 145 | 145 | Or use the CLI, which is the same thing with the paperwork done: | |
| 146 | 146 | ||
| 147 | 147 | ```sh | |
| 148 | vole issue new --title "Nothing renders on the docs page" -m "Steps..." -l bug | ||
| 148 | vole issue new --title "Nothing renders on the docs page" -m "Steps..." -l bug -a wilbur | ||
| 149 | 149 | vole issue comment -m "Also affects search" 01M5JR2JK29KKMAA1T9MDNTPBP | |
| 150 | vole issue assign 01M5JR2JK29KKMAA1T9MDNTPBP wilbur | ||
| 151 | vole issue unassign 01M5JR2JK29KKMAA1T9MDNTPBP wilbur | ||
| 150 | 152 | vole issue close 01M5JR2JK29KKMAA1T9MDNTPBP | |
| 151 | vole issue list --state open -l bug --format '%(shortid) %(title)' | ||
| 153 | vole issue list --state open -l bug --assignee wilbur --format '%(shortid) %(title)' | ||
| 152 | 154 | ||
| 153 | 155 | vole issue show 01M5JR2JK29KKMAA1T9MDNTPBP # a prefix is enough | |
| 154 | 156 | ``` | |
| 155 | 157 | ||
| 158 | An assignee is a roster account name, so it is held to the same rule an account | ||
| 159 | name is (lowercase letters, digits, `-` and `_`) and a value that could never be | ||
| 160 | one fails the push that introduced it. The name is stored as written rather than | ||
| 161 | resolved to an account id: names are never reissued, so an assignment keeps | ||
| 162 | meaning the same person after a rename, and the file stays readable in a plain | ||
| 163 | clone. | ||
| 164 | |||
| 156 | 165 | Options follow git's conventions: `--long value`, `--long=value`, `-o value`, | |
| 157 | 166 | `-ovalue`, repeatable `-l`, `--` to end option parsing, and `-h` for usage. | |
| 158 | 167 | Options come before arguments — `vole issue comment -m text <id>`, not the other | |