git-pr
created pr with
56.1
added 56.2
1: 0200c93 ! 1: a2710a3 refactor: custom index page
added 56.3
1: a2710a3 < -: ------- refactor: custom index page
-: ------- > 1: 7338b44 feat: allow config `desc` to add a description box to index page
added 56.4
1: 7338b44 < -: ------- feat: allow config `desc` to add a description box to index page
-: ------- > 1: 26daea4 feat(pgs): lru cache for object info and special files
-: ------- > 2: b004b64 chore(pgs): use http cache clear event to rm lru cache for special files
-: ------- > 3: 59f5618 refactor(pgs): store lru cache on web router
changed status to
accepted
cmds
checkout latest patchset:
ssh pr.pico.sh print 56 | git am -3checkout any patchset in a patch request:
ssh pr.pico.sh print 56.[rev] | git am -3add changes to patch request:
git format-patch main --stdout | ssh pr.pico.sh pr add 56set PR to open (enables RSS notifications):
ssh pr.pico.sh pr open 56set PR to draft (stops RSS notifications):
ssh pr.pico.sh pr draft 56
Patchset
56.1
refactor: custom index page
Eric Bower
2025-03-27T20:15:57ZSemantic diff summary
1 added,
5 modified,
0 signature changed,
0 removed
across 2 analyzed files
(2 files skipped: unsupported file type)
+29
-2
cli.go
#
| ... | ... | @@ -173,7 +173,7 @@ func printPatchsetFromPrID(sesh ssh.Session, pr GitPatchRequest, prID int64) err | |
| 173 | 173 | } | |
| 174 | 174 | ||
| 175 | 175 | func NewCli(sesh ssh.Session, be *Backend, pr GitPatchRequest) *cli.App { | |
| 176 | - | desc := `Patchbin: a supercharged pastebin for git collaboration. | |
| 176 | + | desc := `git-pr: the simplest git collaboration tool. | |
| 177 | 177 | ||
| 178 | 178 | Here's how it works: | |
| 179 | 179 | - External contributor clones repo (git-clone) |
| ... | ... | @@ -196,7 +196,7 @@ Here's how it works: | |
| 196 | 196 | app := &cli.App{ | |
| 197 | 197 | Name: "ssh", | |
| 198 | 198 | Description: desc, | |
| 199 | - | Usage: "Collaborate with contributors for your git project", | |
| 199 | + | Usage: "Send, receive, and review git patches", | |
| 200 | 200 | Writer: sesh, | |
| 201 | 201 | ErrWriter: sesh, | |
| 202 | 202 | ExitErrHandler: func(cCtx *cli.Context, err error) { |
| ... | ... | @@ -211,6 +211,33 @@ Here's how it works: | |
| 211 | 211 | return nil | |
| 212 | 212 | }, | |
| 213 | 213 | Commands: []*cli.Command{ | |
| 214 | + | { | |
| 215 | + | Name: "index", | |
| 216 | + | Usage: "Generate an index page", | |
| 217 | + | Args: true, | |
| 218 | + | Action: func(cCtx *cli.Context) error { | |
| 219 | + | indexFile := `{{template "base" .}} | |
| 220 | + | {{define "title"}}git-pr{{end}} | |
| 221 | + | ||
| 222 | + | {{define "meta"}} | |
| 223 | + | {{end}} | |
| 224 | + | ||
| 225 | + | {{define "body"}} | |
| 226 | + | <header class="group"> | |
| 227 | + | <h1 class="text-2xl">git-pr</h1> | |
| 228 | + | <div> | |
| 229 | + | <span>The simplest git collaboration service</span> · <a href="/e">explore</a> | |
| 230 | + | </div> | |
| 231 | + | </header> | |
| 232 | + | ||
| 233 | + | <main></main> | |
| 234 | + | ||
| 235 | + | <footer class="mt">Built by <a href="https://pico.sh">pico.sh</a></footer> | |
| 236 | + | {{end}}` | |
| 237 | + | wish.Println(sesh, indexFile) | |
| 238 | + | return nil | |
| 239 | + | }, | |
| 240 | + | }, | |
| 214 | 241 | { | |
| 215 | 242 | Name: "logs", | |
| 216 | 243 | Usage: "List event logs with filters", |
+34
-0
tmpl/explore.html
#
| ... | ... | @@ -0,0 +1,34 @@ | |
| 1 | + | {{template "base" .}} | |
| 2 | + | ||
| 3 | + | {{define "title"}}explore prs{{end}} | |
| 4 | + | ||
| 5 | + | {{define "meta"}} | |
| 6 | + | <link rel="alternate" type="application/atom+xml" | |
| 7 | + | title="RSS feed for git collaboration server" | |
| 8 | + | href="/rss" /> | |
| 9 | + | {{end}} | |
| 10 | + | ||
| 11 | + | {{define "body"}} | |
| 12 | + | <header class="group"> | |
| 13 | + | <h1 class="text-2xl">explore</h1> | |
| 14 | + | <div> | |
| 15 | + | <span>View all of the latest patch requests</span> | |
| 16 | + | </div> | |
| 17 | + | </header> | |
| 18 | + | ||
| 19 | + | <main> | |
| 20 | + | <div> | |
| 21 | + | filter | |
| 22 | + | <a href="/">open</a> <code>{{.NumOpen}}</code> | |
| 23 | + | · | |
| 24 | + | <a href="/?status=accepted">accepted</a> <code>{{.NumAccepted}}</code> | |
| 25 | + | · | |
| 26 | + | <a href="/?status=closed">closed</a> <code>{{.NumClosed}}</code> | |
| 27 | + | </div> | |
| 28 | + | {{template "pr-table" .Prs}} | |
| 29 | + | </main> | |
| 30 | + | ||
| 31 | + | <footer class="mt"> | |
| 32 | + | <div><a href="/rss">rss</a></div> | |
| 33 | + | </footer> | |
| 34 | + | {{end}} |
+11
-44
tmpl/index.html
#
| ... | ... | @@ -3,60 +3,27 @@ | |
| 3 | 3 | {{define "title"}}git-pr{{end}} | |
| 4 | 4 | ||
| 5 | 5 | {{define "meta"}} | |
| 6 | - | <link rel="alternate" type="application/atom+xml" | |
| 7 | - | title="RSS feed for git collaboration server" | |
| 8 | - | href="/rss" /> | |
| 9 | 6 | {{end}} | |
| 10 | 7 | ||
| 11 | 8 | {{define "body"}} | |
| 12 | 9 | <header class="group"> | |
| 13 | - | <h1 class="text-2xl">patchbin</h1> | |
| 10 | + | <h1 class="text-2xl">git-pr</h1> | |
| 14 | 11 | <div> | |
| 15 | - | <span>A pastebin supercharged for git collaboration</span> · | |
| 16 | - | <a href="/docs">docs</a> | |
| 17 | - | </div> | |
| 18 | - | ||
| 19 | - | <div class="box-sm"> | |
| 20 | - | <div> | |
| 21 | - | Welcome to <a href="https://pico.sh">pico's</a> managed patchbin service! | |
| 22 | - | This is a <strong>public</strong> service that is free to anyone who wants | |
| 23 | - | to collaborate on git patches. The idea is simple: submit a patchset to | |
| 24 | - | our service and let anyone collaborate on it by submitting follow-up patchsets. | |
| 25 | - | Using this service for the first time? Creating a patch request is simple: | |
| 26 | - | </div> | |
| 27 | - | ||
| 28 | - | <pre class="text-sm">git format-patch main --stdout | ssh pr.pico.sh pr create {repo}</pre> | |
| 29 | - | ||
| 30 | - | <div> | |
| 31 | - | When running that command we will automatically create a user and a repo | |
| 32 | - | if one doesn't exist. Once the patches have been submitted you'll receive | |
| 33 | - | a link that you can send to a reviewer. Anyone can review patch requests. | |
| 34 | - | Want to submit a v2 of the patch request? | |
| 35 | - | </div> | |
| 36 | - | ||
| 37 | - | <pre class="text-sm">git format-patch main --stdout | ssh pr.pico.sh pr add {prID}</pre> | |
| 38 | - | ||
| 39 | - | <div> | |
| 40 | - | Downloading a patchset is easy as well: | |
| 41 | - | </div> | |
| 42 | - | ||
| 43 | - | <pre class="text-sm">ssh pr.pico.sh print pr-{prID}</pre> | |
| 12 | + | <span>The simplest git collaboration service</span> · <a href="/e">explore</a> | |
| 44 | 13 | </div> | |
| 45 | 14 | </header> | |
| 46 | 15 | ||
| 47 | 16 | <main> | |
| 48 | - | <div> | |
| 49 | - | filter | |
| 50 | - | <a href="/">open</a> <code>{{.NumOpen}}</code> | |
| 51 | - | · | |
| 52 | - | <a href="/?status=accepted">accepted</a> <code>{{.NumAccepted}}</code> | |
| 53 | - | · | |
| 54 | - | <a href="/?status=closed">closed</a> <code>{{.NumClosed}}</code> | |
| 17 | + | <div class="box-sm container-sm"> | |
| 18 | + | <div> | |
| 19 | + | Welcome to <code>git-pr</code>! If you are the owner of this service we | |
| 20 | + | recommend replacing this index page with your own custom one. To do that, | |
| 21 | + | copy the contents of this command into your <code>data_dir</code>: | |
| 22 | + | </div> | |
| 23 | + | ||
| 24 | + | <pre class="m-0 mt">ssh {{.MetaData.URL}} index > ./data/index.html</pre> | |
| 55 | 25 | </div> | |
| 56 | - | {{template "pr-table" .Prs}} | |
| 57 | 26 | </main> | |
| 58 | 27 | ||
| 59 | - | <footer class="mt"> | |
| 60 | - | <div><a href="/rss">rss</a></div> | |
| 61 | - | </footer> | |
| 28 | + | <footer class="mt">Built by <a href="https://pico.sh">pico.sh</a></footer> | |
| 62 | 29 | {{end}} |
+22
-2
web.go
#
| ... | ... | @@ -313,6 +313,25 @@ func indexHandler(w http.ResponseWriter, r *http.Request) { | |
| 313 | 313 | return | |
| 314 | 314 | } | |
| 315 | 315 | ||
| 316 | + | w.Header().Set("content-type", "text/html") | |
| 317 | + | tmpl := getTemplate("index.html") | |
| 318 | + | err = tmpl.ExecuteTemplate(w, "index.html", BasicData{ | |
| 319 | + | MetaData: MetaData{ | |
| 320 | + | URL: web.Backend.Cfg.Url, | |
| 321 | + | }, | |
| 322 | + | }) | |
| 323 | + | if err != nil { | |
| 324 | + | web.Backend.Logger.Error("cannot execute template", "err", err) | |
| 325 | + | } | |
| 326 | + | } | |
| 327 | + | ||
| 328 | + | func exploreHandler(w http.ResponseWriter, r *http.Request) { | |
| 329 | + | web, err := getWebCtx(r) | |
| 330 | + | if err != nil { | |
| 331 | + | w.WriteHeader(http.StatusInternalServerError) | |
| 332 | + | return | |
| 333 | + | } | |
| 334 | + | ||
| 316 | 335 | prs, err := web.Pr.GetPatchRequests() | |
| 317 | 336 | if err != nil { | |
| 318 | 337 | web.Logger.Error("could not get prs", "err", err) |
| ... | ... | @@ -342,8 +361,8 @@ func indexHandler(w http.ResponseWriter, r *http.Request) { | |
| 342 | 361 | } | |
| 343 | 362 | ||
| 344 | 363 | w.Header().Set("content-type", "text/html") | |
| 345 | - | tmpl := getTemplate("index.html") | |
| 346 | - | err = tmpl.ExecuteTemplate(w, "index.html", PrTableData{ | |
| 364 | + | tmpl := getTemplate("explore.html") | |
| 365 | + | err = tmpl.ExecuteTemplate(w, "explore.html", PrTableData{ | |
| 347 | 366 | NumOpen: numOpen, | |
| 348 | 367 | NumAccepted: numAccepted, | |
| 349 | 368 | NumClosed: numClosed, |
| ... | ... | @@ -1149,6 +1168,7 @@ func StartWebServer(cfg *GitCfg) { | |
| 1149 | 1168 | http.HandleFunc("GET /rss", ctxMdw(ctx, rssHandler)) | |
| 1150 | 1169 | http.HandleFunc("GET /docs", ctxMdw(ctx, docsHandler)) | |
| 1151 | 1170 | http.HandleFunc("GET /", ctxMdw(ctx, indexHandler)) | |
| 1171 | + | http.HandleFunc("GET /e", ctxMdw(ctx, exploreHandler)) | |
| 1152 | 1172 | http.HandleFunc("GET /syntax.css", ctxMdw(ctx, chromaStyleHandler)) | |
| 1153 | 1173 | embedFS, err := getEmbedFS(embedStaticFS, "static") | |
| 1154 | 1174 | if err != nil { |