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.
Celebrate a landed change request, once
c8fa5759b293126f1b247abc746bc339791568cb · Keenan Nemetz · 13d ago · parent 53ff967b
Celebrate a landed change request, once A successful landing now redirects with ?merged=1, and app.js answers that parameter by fetching static/confetti.browser.js (canvas-confetti 1.9.3, ISC) and firing one burst. The flag is removed from the address as soon as it is read, so a reload, a bookmark or a shared link is an ordinary visit, and no page without the parameter ever requests the bundle. The library honours prefers-reduced-motion through disableForReducedMotion, and reset() before firing means a page restored from the back/forward cache cannot stack a second canvas. The asset is vendored the way htmx and Lucide are: the file is unmodified from the published npm tarball, with the license beside it, so script-src 'self' stays as it is. It is deliberately not in the page shell, because Static() serves assets uncompressed and a shell script would make every page pay for a celebration only one page uses. Refs #01M5XZ7T4R88BA9KV1KTSV7ZXP
server/issues_changes_test.go +45
| @@ -785,3 +785,48 @@ func TestCommentEditAndDeleteFromTheWeb(t *testing.T) { | |||
| 785 | 785 | t.Fatalf("deleted change root did not become a tombstone:\n%s", page) | |
| 786 | 786 | } | |
| 787 | 787 | } | |
| 788 | |||
| 789 | // Landing is the one write that comes back with a flag on it, because the page it | ||
| 790 | // lands on has to know the difference between "you just merged this" and "you are | ||
| 791 | // reading this". The flag belongs to the redirect only: the request page renders | ||
| 792 | // exactly the same with it present. | ||
| 793 | func TestMergingRedirectsWithTheCelebrationFlag(t *testing.T) { | ||
| 794 | t.Parallel() | ||
| 795 | f := newFixture(t) | ||
| 796 | f.publish(f.repo) | ||
| 797 | |||
| 798 | base := workRepo(t, map[string]string{"readme.md": "v1\n"}) | ||
| 799 | if _, errs, err := f.git(base, "push", f.sshURL(f.repo), "main:refs/heads/main"); err != nil { | ||
| 800 | t.Fatalf("initial main: %s", errs) | ||
| 801 | } | ||
| 802 | feature, head := f.cloneWithChange(t, map[string]string{"readme.md": "v2\n"}, "feature") | ||
| 803 | if _, errs, err := f.git(feature, "push", f.sshURL(f.repo), "HEAD:refs/heads/feature"); err != nil { | ||
| 804 | t.Fatalf("feature push: %s", errs) | ||
| 805 | } | ||
| 806 | meta := f.newMetaRepo(t) | ||
| 807 | if err := meta.apply(map[string]string{ | ||
| 808 | "requests/" + reqID + "/request.md": requestMeta("main", head, "wilbur"), | ||
| 809 | }, "request"); err != nil { | ||
| 810 | t.Fatalf("request: %v", err) | ||
| 811 | } | ||
| 812 | |||
| 813 | sess, cookie := f.browserSession(t, f.ownerID) | ||
| 814 | resp, body := f.browserPost(t, "/r/"+f.repo+"/requests/"+reqID+"/merge", cookie, | ||
| 815 | url.Values{"csrf": {sess.CSRF}}) | ||
| 816 | if resp.StatusCode != http.StatusSeeOther { | ||
| 817 | t.Fatalf("merge = %d, want 303:\n%s", resp.StatusCode, body) | ||
| 818 | } | ||
| 819 | want := "/r/" + f.repo + "/requests/" + reqID + "?merged=1" | ||
| 820 | if got := resp.Header.Get("Location"); got != want { | ||
| 821 | t.Fatalf("merge redirect = %q, want %q", got, want) | ||
| 822 | } | ||
| 823 | |||
| 824 | // The page the flag points at is the ordinary merged view, not a special case. | ||
| 825 | resp, page := f.browserGet(t, want, cookie) | ||
| 826 | if resp.StatusCode != http.StatusOK { | ||
| 827 | t.Fatalf("landing page = %d", resp.StatusCode) | ||
| 828 | } | ||
| 829 | if !strings.Contains(page, "merged into") { | ||
| 830 | t.Fatalf("landing page does not show the merged state:\n%s", page) | ||
| 831 | } | ||
| 832 | } | ||