- commit
- 457c062
- parent
- 967d908
- author
- Eric Bower
- date
- 2024-06-24 12:55:02 -0400 EDT
docs: copy
7 files changed,
+155,
-73
M
pr.go
+42,
-0
1@@ -25,6 +25,7 @@ const (
2
3 type GitPatchRequest interface {
4 GetRepos() ([]Repo, error)
5+ GetReposWithLatestPr() ([]RepoWithLatestPr, error)
6 GetRepoByID(repoID string) (*Repo, error)
7 SubmitPatchRequest(repoID string, pubkey string, patchset io.Reader) (*PatchRequest, error)
8 SubmitPatchSet(prID int64, pubkey string, op PatchsetOp, patchset io.Reader) ([]*Patch, error)
9@@ -48,10 +49,51 @@ type PrCmd struct {
10 var _ GitPatchRequest = PrCmd{}
11 var _ GitPatchRequest = (*PrCmd)(nil)
12
13+type PrWithRepo struct {
14+ LastUpdatedPrID int64
15+ RepoID string
16+}
17+
18+type RepoWithLatestPr struct {
19+ *Repo
20+ PatchRequest *PatchRequest
21+}
22+
23 func (pr PrCmd) GetRepos() ([]Repo, error) {
24 return pr.Backend.Cfg.Repos, nil
25 }
26
27+func (pr PrCmd) GetReposWithLatestPr() ([]RepoWithLatestPr, error) {
28+ repos := []RepoWithLatestPr{}
29+ prs := []PatchRequest{}
30+ err := pr.Backend.DB.Select(&prs, "SELECT * FROM patch_requests GROUP BY repo_id ORDER BY updated_at DESC")
31+ if err != nil {
32+ return repos, err
33+ }
34+
35+ // we want recently modified repos to be on top
36+ for _, prq := range prs {
37+ for _, repo := range pr.Backend.Cfg.Repos {
38+ if prq.RepoID == repo.ID {
39+ repos = append(repos, RepoWithLatestPr{
40+ Repo: &repo,
41+ PatchRequest: &prq,
42+ })
43+ }
44+ }
45+ }
46+
47+ for _, repo := range pr.Backend.Cfg.Repos {
48+ for _, curRepo := range repos {
49+ if curRepo.ID == repo.ID {
50+ continue
51+ }
52+ }
53+ }
54+
55+ return repos, nil
56+}
57+
58 func (pr PrCmd) GetRepoByID(repoID string) (*Repo, error) {
59 repos, err := pr.GetRepos()
60 if err != nil {
+21,
-25
1@@ -14,16 +14,28 @@
2 <main class="group">
3 <div class="group">
4 {{range $idx, $val := .Patches}}
5- <div class="box">
6- <h2 class="text-lg m-0 p-0 mb">
7- <code>#{{$idx}}</code>
8- {{if $val.Review}}<code>REVIEW</code>{{end}}
9- <a href="#{{$val.Url}}">{{$val.Title}}</a>
10- </h2>
11- <div class="group-h text-sm">
12- <code>{{$val.AuthorName}} <{{$val.AuthorEmail}}></code>
13- <date>{{$val.AuthorDate}}</date>
14+ <div class="box group" id="{{$val.Url}}">
15+ <div>
16+ <h2 class="text-lg m-0 p-0 mb">
17+ <code>#{{$idx}}</code>
18+ {{if $val.Review}}<code>REVIEW</code>{{end}}
19+ <a href="#{{$val.Url}}">{{$val.Title}}</a>
20+ </h2>
21+
22+ <div class="group-h text-sm">
23+ <code>{{$val.AuthorName}} <{{$val.AuthorEmail}}></code>
24+ <date>{{$val.AuthorDate}}</date>
25+ </div>
26 </div>
27+
28+ {{if $val.Body}}<pre class="m-0">{{$val.Body}}</pre>{{end}}
29+
30+ <pre class="m-0">{{$val.BodyAppendix}}</pre>
31+
32+ <details>
33+ <summary>Patch</summary>
34+ <div>{{$val.DiffStr}}</div>
35+ </details>
36 </div>
37 {{else}}
38 <div class="box">
39@@ -31,22 +43,6 @@
40 </div>
41 {{end}}
42 </div>
43-
44- <hr class="w-full" />
45-
46- <div class="group">
47- {{range .Patches}}
48- <div class="box" id="{{.Url}}">
49- <div class="group-h">
50- <h2 class="text-lg m-0 p-0">
51- {{if .Review}}<code>REVIEW</code>{{end}}
52- <a href="#{{.Url}}">{{.Title}}</a>
53- </h2>
54- </div>
55- <div>{{.DiffStr}}</div>
56- </div>
57- {{end}}
58- </div>
59 </main>
60
61 <hr />
+12,
-1
1@@ -11,7 +11,18 @@
2 <code>{{.Pr.Pubkey}}</code>
3 </div>
4
5- <pre># add changes to patch request
6+ <details>
7+ <summary>Help</summary>
8+ <div class="group">
9+ <pre class="m-0"># add changes to patch request
10 git format-patch main --stdout | ssh pr.pico.sh pr add {{.Pr.ID}}</pre>
11+ <pre class="m-0"># checkout all patches
12+ssh pr.pico.sh pr print {{.Pr.ID}} | git am -3</pre>
13+ <pre class="m-0"># checkout specific patch
14+ssh pr.pico.sh pr print {{.Pr.ID}} --filter n | git am -3</pre>
15+ <pre class="m-0"># checkout patch range
16+ssh pr.pico.sh pr print {{.Pr.ID}} --filter n:y | git am -3</pre>
17+ </div>
18+ </details>
19 </header>
20 {{end}}
+10,
-0
1@@ -0,0 +1,10 @@
2+{{define "pr-list-item"}}
3+<div>
4+ <div><a href="{{.Url}}">{{.Text}}</a> <code>{{.Status}}</code></div>
5+ <div>
6+ <code>#{{.ID}}</code>
7+ <span>opened on <date>{{.Date}}</date> by </span>
8+ <code>{{.Pubkey}}</code>
9+ </div>
10+</div>
11+{{end}}
+19,
-39
1@@ -12,57 +12,37 @@
2 <header>
3 <h1 class="text-2xl mb"><a href="/">repos</a> / {{.ID}}</h1>
4 <div class="group">
5- <div><code>git clone {{.CloneAddr}}</code></div>
6- <pre># submit a new patch request
7+ <div>
8+ <code>git clone {{.CloneAddr}}</code>
9+ </div>
10+
11+ <details>
12+ <summary>Help</summary>
13+ <pre class="m-0"># submit a new patch request
14 git format-patch main --stdout | ssh pr.pico.sh pr create {{.ID}}</pre>
15+ </details>
16 </div>
17 </header>
18 <main class="group">
19 <h3 class="text-lg">Open PRs</h3>
20-
21 {{range .OpenPrs}}
22- <article class="box">
23- <div><a href="{{.Url}}">{{.Text}}</a> <code>{{.Status}}</code></div>
24- <div class="text-sm">
25- <code>#{{.ID}}</code>
26- <span>opened on <date>{{.Date}}</date> by </span>
27- <code>{{.Pubkey}}</code>
28- </div>
29- </article>
30+ <div class="box">{{template "pr-list-item" .}}</div>
31 {{end}}
32
33 {{if .AcceptedPrs}}
34- <hr class="my w-full" />
35-
36- <h3 class="text-lg">Accepted PRs</h3>
37-
38- {{range .AcceptedPrs}}
39- <article class="box">
40- <div><a href="{{.Url}}">{{.Text}}</a> <code>{{.Status}}</code></div>
41- <div class="text-sm">
42- <code>#{{.ID}}</code>
43- <span>opened on <date>{{.Date}}</date> by </span>
44- <code>{{.Pubkey}}</code>
45- </div>
46- </article>
47- {{end}}
48+ <hr class="my w-full" />
49+ <h3 class="text-lg">Accepted PRs</h3>
50+ {{range .AcceptedPrs}}
51+ <div class="box">{{template "pr-list-item" .}}</div>
52+ {{end}}
53 {{end}}
54
55 {{if .ClosedPrs}}
56- <hr class="my w-full" />
57-
58- <h3 class="text-lg">Closed PRs</h3>
59-
60- {{range .ClosedPrs}}
61- <article class="box">
62- <div><a href="{{.Url}}">{{.Text}}</a> <code>{{.Status}}</code></div>
63- <div class="text-sm">
64- <code>#{{.ID}}</code>
65- <span>opened on <date>{{.Date}}</date> by </span>
66- <code>{{.Pubkey}}</code>
67- </div>
68- </article>
69- {{end}}
70+ <hr class="my w-full" />
71+ <h3 class="text-lg">Closed PRs</h3>
72+ {{range .ClosedPrs}}
73+ <div class="box">{{template "pr-list-item" .}}</div>
74+ {{end}}
75 {{end}}
76 </main>
77
+31,
-5
1@@ -9,18 +9,44 @@
2 {{end}}
3
4 {{define "body"}}
5-<header>
6- <h1 class="text-2xl mb">Repos</h1>
7- <p>A new git collaboration service.</p>
8+<header class="group">
9+ <h1 class="text-2xl">Repos</h1>
10+ <div>A new git collaboration service.</div>
11+ <pre class="m-0">ssh pr.pico.sh help</pre>
12+ <details>
13+ <summary>How do Patch Requests work?</summary>
14+ <div>
15+ Patch requests (PR) are the simplest way to submit, review, and accept changes to your git repository.
16+ Here's how it works:
17+ </div>
18+
19+ <ol>
20+ <li>External contributor clones repo (<code>git-clone</code>)</li>
21+ <li>External contributor makes a code change (<code>git-add</code> & <code>git-commit</code>)</li>
22+ <li>External contributor generates patches (<code>git-format-patch</code>)</li>
23+ <li>External contributor submits a PR to SSH server</li>
24+ <li>Owner receives RSS notification that there's a new PR</li>
25+ <li>Owner applies patches locally (<code>git-am</code>) from SSH server</li>
26+ <li>Owner makes suggestions in code! (<code>git-add</code> & <code>git-commit</code>)</li>
27+ <li>Owner submits review by piping patch to SSH server (<code>git-format-patch</code>)</li>
28+ <li>External contributor receives RSS notification of the PR review</li>
29+ <li>External contributor re-applies patches (<code>git-am</code>)</li>
30+ <li>External contributor reviews and removes comments in code!</li>
31+ <li>External contributor submits another patch (<code>git-format-patch</code>)</li>
32+ <li>Owner applies patches locally (<code>git-am</code>)</li>
33+ <li>Owner marks PR as accepted and pushes code to main (<code>git-push</code>)</li>
34+ </ol>
35+ </details>
36 </header>
37 <main>
38 <div class="group">
39- <pre>ssh pr.pico.sh help</pre>
40-
41 {{range .Repos}}
42 <div class="box">
43 <h3 class="text-lg m-0 p-0"><a href="{{.Url}}">{{.Text}}</a></h3>
44 <div class="text-sm">{{.Desc}}</div>
45+ {{if .LatestPr}}
46+ <div class="text-xs mt box-sm">{{template "pr-list-item" .LatestPr}}</div>
47+ {{end}}
48 </div>
49 {{end}}
50 </div>
M
web.go
+20,
-3
1@@ -71,6 +71,7 @@ func getTemplate(file string) *template.Template {
2 tmplFS,
3 filepath.Join("tmpl", file),
4 filepath.Join("tmpl", "pr-header.html"),
5+ filepath.Join("tmpl", "pr-list-item.html"),
6 filepath.Join("tmpl", "base.html"),
7 ),
8 )
9@@ -84,7 +85,8 @@ type LinkData struct {
10
11 type RepoData struct {
12 LinkData
13- Desc string
14+ Desc string
15+ LatestPr PrListData
16 }
17
18 type RepoListData struct {
19@@ -98,7 +100,7 @@ func repoListHandler(w http.ResponseWriter, r *http.Request) {
20 return
21 }
22
23- repos, err := web.Pr.GetRepos()
24+ repos, err := web.Pr.GetReposWithLatestPr()
25 if err != nil {
26 web.Pr.Backend.Logger.Error("cannot get repos", "err", err)
27 w.WriteHeader(http.StatusInternalServerError)
28@@ -107,12 +109,28 @@ func repoListHandler(w http.ResponseWriter, r *http.Request) {
29
30 repoData := []RepoData{}
31 for _, repo := range repos {
32+ var ls PrListData
33+ if repo.PatchRequest != nil {
34+ curpr := repo.PatchRequest
35+ ls = PrListData{
36+ ID: curpr.ID,
37+ Pubkey: curpr.Pubkey,
38+ LinkData: LinkData{
39+ Url: template.URL(fmt.Sprintf("/prs/%d", curpr.ID)),
40+ Text: curpr.Name,
41+ },
42+ Date: curpr.CreatedAt.Format(time.RFC3339),
43+ Status: curpr.Status,
44+ }
45+ }
46+
47 d := RepoData{
48 Desc: repo.Desc,
49 LinkData: LinkData{
50 Url: template.URL("/repos/" + repo.ID),
51 Text: repo.ID,
52 },
53+ LatestPr: ls,
54 }
55 repoData = append(repoData, d)
56 }
57@@ -433,7 +451,6 @@ func StartWebServer() {
58 }
59 formatter := formatterHtml.New(
60 formatterHtml.WithLineNumbers(true),
61- formatterHtml.WithLinkableLineNumbers(true, ""),
62 formatterHtml.WithClasses(true),
63 )
64 web := &WebCtx{