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.
Build out the forge web UI
f09ef39efe44de693748053a7b596ef328fcf06b · Keenan Nemetz · 15d ago · parent 70b70264
Build out the forge web UI Turn the web surface into a compact, server-rendered forge with HTMX as the progressive dynamic layer. - Split templates and assets into embedded files; add a tokenised dark theme, Lucide icons, and vendored HTMX. - Add the repository Overview page and a separate chronological Activity feed with type filters and HTMX fragment updates. - Finish the issue and change workflows: create pages, comments, close/reopen, labels and assignees, approval, discussion, line-anchored review comments, and HTMX list filters. - Add syntax-highlighted blobs, contributor/statistics derivation, branch-aware code browsing, and a repository Runs list. - Add a loopback-only development login for local write testing, with explicit configuration and listener checks. - Extend tests for the new pages, fragments, labels, assignees, anchors, and development login.
server/auth_test.go +3−3
| @@ -345,7 +345,7 @@ func TestContributorNamespaceAndRequestUseTheAccountName(t *testing.T) { | |||
| 345 | 345 | t.Fatalf("contributor issue: status=%d body=%s", resp.StatusCode, body) | |
| 346 | 346 | } | |
| 347 | 347 | ||
| 348 | resp, body = f.browserGet(t, "/r/"+f.repo+"/requests", cookie) | ||
| 348 | resp, body = f.browserGet(t, "/r/"+f.repo+"/requests/new", cookie) | ||
| 349 | 349 | if resp.StatusCode != http.StatusOK || !strings.Contains(body, headRef) || !strings.Contains(body, "Open change request") { | |
| 350 | 350 | t.Fatalf("request form: status=%d body=%s", resp.StatusCode, body) | |
| 351 | 351 | } | |
| @@ -433,7 +433,7 @@ jobs: | |||
| 433 | 433 | } | |
| 434 | 434 | ||
| 435 | 435 | contribSess, contribCookie := f.browserSession(t, contrib.ID) | |
| 436 | resp, body := f.browserGet(t, "/r/"+f.repo, contribCookie) | ||
| 436 | resp, body := f.browserGet(t, "/r/"+f.repo+"/code", contribCookie) | ||
| 437 | 437 | if resp.StatusCode != http.StatusOK || !strings.Contains(body, "waiting for a maintainer") { | |
| 438 | 438 | t.Fatalf("contributor branch status: status=%d body=%s", resp.StatusCode, body) | |
| 439 | 439 | } | |
| @@ -445,7 +445,7 @@ jobs: | |||
| 445 | 445 | } | |
| 446 | 446 | ||
| 447 | 447 | ownerSess, ownerCookie := f.browserSession(t, f.ownerID) | |
| 448 | resp, body = f.browserGet(t, "/r/"+f.repo, ownerCookie) | ||
| 448 | resp, body = f.browserGet(t, "/r/"+f.repo+"/code", ownerCookie) | ||
| 449 | 449 | if resp.StatusCode != http.StatusOK || !strings.Contains(body, "Allow CI for this tip") { | |
| 450 | 450 | t.Fatalf("maintainer CI control missing: status=%d body=%s", resp.StatusCode, body) | |
| 451 | 451 | } | |