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/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 7 slice 1: review on the web, so approving does not start with a clone

78912785b00227cffe3f8fb26b057cd60159a6b4 · Keenan Nemetz · 17d ago · parent 9bea7f11

Phase 7 slice 1: review on the web, so approving does not start with a clone

Phase 7 is re-scoped: signing is deferred (the SSH handshake already establishes
who pushed, and there is no third-party verifier here), and the phase is now about
the experience of using the forge.

- GET /r/<repo>/commit/<sha>: a commit's metadata and the patch it introduced,
  per-file hunks with line numbers; the commit list links to it
- change requests show the same diff, computed three-dot: a review is what the
  branch adds since it diverged, not every commit main gained meanwhile
- diffs are capped at 512 KiB and labelled when cut, binary files are named
  rather than faked, and every line is escaped (a <script> line is a test)
- the repository README renders on its home page and markdown blobs render as
  markdown, through the sanitiser issues already use

internal/web/commit.go +64

@@ -0,0 +1,64 @@
1 package web
2
3 // Commit pages: metadata plus the patch the commit introduced. This is the view
4 // a reviewer needs before approving anything, and it is read-only like the rest
5 // of the web — the review itself is a git artifact, not a web session.
6
7 import (
8 "net/http"
9
10 "github.com/rodent-software/vole/internal/gitx"
11 )
12
13 // diffCap bounds what a single view renders. A patch is parsed in memory and
14 // shown in full; a generated 50 MiB diff is not a review, so it is cut and
15 // labelled as cut.
16 const diffCap = 512 << 10
17
18 func (w *Web) CommitView(rw http.ResponseWriter, r *http.Request) {
19 m, ok := w.open(rw, r)
20 if !ok {
21 return
22 }
23 g := m.Git()
24 sha, err := g.RevParse(r.PathValue("sha"))
25 if err != nil || sha == "" {
26 http.NotFound(rw, r)
27 return
28 }
29 log, err := g.Log(sha, 1)
30 if err != nil || len(log) == 0 {
31 http.NotFound(rw, r)
32 return
33 }
34 patch, truncated, err := g.ShowPatch(sha, diffCap)
35 if err != nil {
36 http.Error(rw, "vole: cannot read that commit", http.StatusInternalServerError)
37 return
38 }
39 body, _ := g.CommitBody(sha)
40 files := parseDiff(patch)
41 added, removed := diffSummary(files)
42
43 w.render(rw, "commit", struct {
44 Page
45 Name string
46 Commit gitx.Commit
47 Body string
48 Files []diffFile
49 Added int
50 Removed int
51 Truncated bool
52 }{
53 Page: w.base(m.Name + " · " + log[0].Subject),
54 Name: m.Name,
55 Commit: log[0],
56 Body: body,
57 Files: files,
58 Added: added,
59 Removed: removed,
60 // A cut patch and a patch with nothing in it look the same to a parser;
61 // the flag is what tells the reader which one they have.
62 Truncated: truncated,
63 }, 0)
64 }