repos / git-pr

a self-hosted git collaboration server
git clone https://github.com/picosh/git-pr.git

commit
6950fe8
parent
c169202
author
Eric Bower
date
2024-07-21 14:49:58 -0400 EDT
feat(cli): add `--accept` and `--close` to `pr add` cmd

refactor(cli): always print PR summary when applicable
3 files changed,  +122, -124
M cli.go
M pr.go
M cli.go
+117, -124
  1@@ -56,6 +56,87 @@ func printPatches(sesh ssh.Session, patches []*Patch) {
  2 	}
  3 }
  4 
  5+func prSummary(be *Backend, pr GitPatchRequest, sesh ssh.Session, prID int64) error {
  6+	request, err := pr.GetPatchRequestByID(prID)
  7+	if err != nil {
  8+		return err
  9+	}
 10+
 11+	wish.Printf(sesh, "Info\n====\n")
 12+	wish.Printf(sesh, "URL: https://%s/prs/%d\n\n", be.Cfg.Url, prID)
 13+
 14+	writer := NewTabWriter(sesh)
 15+	fmt.Fprintln(writer, "ID\tName\tStatus\tDate")
 16+	fmt.Fprintf(
 17+		writer,
 18+		"%d\t%s\t[%s]\t%s\n",
 19+		request.ID, request.Name, request.Status, request.CreatedAt.Format(be.Cfg.TimeFormat),
 20+	)
 21+	writer.Flush()
 22+
 23+	patchsets, err := pr.GetPatchsetsByPrID(prID)
 24+	if err != nil {
 25+		return err
 26+	}
 27+
 28+	wish.Printf(sesh, "\nPatchsets\n====\n")
 29+
 30+	writerSet := NewTabWriter(sesh)
 31+	fmt.Fprintln(writerSet, "ID\tType\tUser\tDate")
 32+	for _, patchset := range patchsets {
 33+		user, err := pr.GetUserByID(patchset.UserID)
 34+		if err != nil {
 35+			be.Logger.Error("cannot find user for patchset", "err", err)
 36+			continue
 37+		}
 38+		isReview := ""
 39+		if patchset.Review {
 40+			isReview = "[review]"
 41+		}
 42+
 43+		fmt.Fprintf(
 44+			writerSet,
 45+			"%s\t%s\t%s\t%s\n",
 46+			getFormattedPatchsetID(patchset.ID),
 47+			isReview,
 48+			user.Name,
 49+			patchset.CreatedAt.Format(be.Cfg.TimeFormat),
 50+		)
 51+	}
 52+	writerSet.Flush()
 53+
 54+	latest, err := getPatchsetFromOpt(patchsets, "")
 55+	if err != nil {
 56+		return err
 57+	}
 58+
 59+	patches, err := pr.GetPatchesByPatchsetID(latest.ID)
 60+	if err != nil {
 61+		return err
 62+	}
 63+
 64+	wish.Printf(sesh, "\nPatches from latest patchset\n====\n")
 65+
 66+	opatches := patches
 67+	w := NewTabWriter(sesh)
 68+	fmt.Fprintln(w, "Idx\tTitle\tCommit\tAuthor\tDate")
 69+	for idx, patch := range opatches {
 70+		timestamp := patch.AuthorDate.Format(be.Cfg.TimeFormat)
 71+		fmt.Fprintf(
 72+			w,
 73+			"%d\t%s\t%s\t%s <%s>\t%s\n",
 74+			idx,
 75+			patch.Title,
 76+			truncateSha(patch.CommitSha),
 77+			patch.AuthorName,
 78+			patch.AuthorEmail,
 79+			timestamp,
 80+		)
 81+	}
 82+	w.Flush()
 83+	return nil
 84+}
 85+
 86 func NewCli(sesh ssh.Session, be *Backend, pr GitPatchRequest) *cli.App {
 87 	desc := `Patch requests (PR) are the simplest way to submit, review, and accept changes to your git repository.
 88 Here's how it works:
 89@@ -369,18 +450,7 @@ Here's how it works:
 90 								"PR submitted! Use the ID for interacting with this PR.",
 91 							)
 92 
 93-							writer := NewTabWriter(sesh)
 94-							fmt.Fprintln(writer, "ID\tName\tURL")
 95-							fmt.Fprintf(
 96-								writer,
 97-								"%d\t%s\t%s\n",
 98-								prq.ID,
 99-								prq.Name,
100-								fmt.Sprintf("https://%s/prs/%d", be.Cfg.Url, prq.ID),
101-							)
102-							writer.Flush()
103-
104-							return nil
105+							return prSummary(be, pr, sesh, prq.ID)
106 						},
107 					},
108 					{
109@@ -482,84 +552,7 @@ Here's how it works:
110 							if err != nil {
111 								return err
112 							}
113-							request, err := pr.GetPatchRequestByID(prID)
114-							if err != nil {
115-								return err
116-							}
117-
118-							wish.Printf(sesh, "Info\n====\n")
119-
120-							writer := NewTabWriter(sesh)
121-							fmt.Fprintln(writer, "ID\tName\tStatus\tDate")
122-							fmt.Fprintf(
123-								writer,
124-								"%d\t%s\t[%s]\t%s\n",
125-								request.ID, request.Name, request.Status, request.CreatedAt.Format(be.Cfg.TimeFormat),
126-							)
127-							writer.Flush()
128-
129-							patchsets, err := pr.GetPatchsetsByPrID(prID)
130-							if err != nil {
131-								return err
132-							}
133-
134-							wish.Printf(sesh, "\nPatchsets\n====\n")
135-
136-							writerSet := NewTabWriter(sesh)
137-							fmt.Fprintln(writerSet, "ID\tType\tUser\tDate")
138-							for _, patchset := range patchsets {
139-								user, err := pr.GetUserByID(patchset.UserID)
140-								if err != nil {
141-									be.Logger.Error("cannot find user for patchset", "err", err)
142-									continue
143-								}
144-								isReview := ""
145-								if patchset.Review {
146-									isReview = "[review]"
147-								}
148-
149-								fmt.Fprintf(
150-									writerSet,
151-									"%s\t%s\t%s\t%s\n",
152-									getFormattedPatchsetID(patchset.ID),
153-									isReview,
154-									user.Name,
155-									patchset.CreatedAt.Format(be.Cfg.TimeFormat),
156-								)
157-							}
158-							writerSet.Flush()
159-
160-							latest, err := getPatchsetFromOpt(patchsets, "")
161-							if err != nil {
162-								return err
163-							}
164-
165-							patches, err := pr.GetPatchesByPatchsetID(latest.ID)
166-							if err != nil {
167-								return err
168-							}
169-
170-							wish.Printf(sesh, "\nPatches from latest patchset\n====\n")
171-
172-							opatches := patches
173-							w := NewTabWriter(sesh)
174-							fmt.Fprintln(w, "Idx\tTitle\tCommit\tAuthor\tDate")
175-							for idx, patch := range opatches {
176-								timestamp := patch.AuthorDate.Format(be.Cfg.TimeFormat)
177-								fmt.Fprintf(
178-									w,
179-									"%d\t%s\t%s\t%s <%s>\t%s\n",
180-									idx,
181-									patch.Title,
182-									truncateSha(patch.CommitSha),
183-									patch.AuthorName,
184-									patch.AuthorEmail,
185-									timestamp,
186-								)
187-							}
188-							w.Flush()
189-
190-							return nil
191+							return prSummary(be, pr, sesh, prID)
192 						},
193 					},
194 					{
195@@ -598,10 +591,11 @@ Here's how it works:
196 							}
197 
198 							err = pr.UpdatePatchRequestStatus(prID, user.ID, "accepted")
199-							if err == nil {
200-								wish.Printf(sesh, "Accepted PR %s (#%d)\n", patchReq.Name, patchReq.ID)
201+							if err != nil {
202+								return err
203 							}
204-							return err
205+							wish.Printf(sesh, "Accepted PR %s (#%d)\n", patchReq.Name, patchReq.ID)
206+							return prSummary(be, pr, sesh, prID)
207 						},
208 					},
209 					{
210@@ -642,10 +636,11 @@ Here's how it works:
211 							}
212 
213 							err = pr.UpdatePatchRequestStatus(prID, user.ID, "closed")
214-							if err == nil {
215-								wish.Printf(sesh, "Closed PR %s (#%d)\n", patchReq.Name, patchReq.ID)
216+							if err != nil {
217+								return err
218 							}
219-							return err
220+							wish.Printf(sesh, "Closed PR %s (#%d)\n", patchReq.Name, patchReq.ID)
221+							return prSummary(be, pr, sesh, prID)
222 						},
223 					},
224 					{
225@@ -689,7 +684,7 @@ Here's how it works:
226 							if err == nil {
227 								wish.Printf(sesh, "Reopened PR %s (#%d)\n", patchReq.Name, patchReq.ID)
228 							}
229-							return err
230+							return prSummary(be, pr, sesh, prID)
231 						},
232 					},
233 					{
234@@ -749,7 +744,15 @@ Here's how it works:
235 						Flags: []cli.Flag{
236 							&cli.BoolFlag{
237 								Name:  "review",
238-								Usage: "mark patch as a review",
239+								Usage: "submit patchset and mark PR as reviewed",
240+							},
241+							&cli.BoolFlag{
242+								Name:  "accept",
243+								Usage: "submit patchset and mark PR as accepted",
244+							},
245+							&cli.BoolFlag{
246+								Name:  "close",
247+								Usage: "submit patchset and mark PR as closed",
248 							},
249 						},
250 						Action: func(cCtx *cli.Context) error {
251@@ -774,15 +777,27 @@ Here's how it works:
252 
253 							isAdmin := be.IsAdmin(sesh.PublicKey())
254 							isReview := cCtx.Bool("review")
255+							isAccept := cCtx.Bool("accept")
256+							isClose := cCtx.Bool("close")
257 							isPrOwner := be.IsPrOwner(prq.UserID, user.ID)
258 							if !isAdmin && !isPrOwner {
259 								return fmt.Errorf("unauthorized, you are not the owner of this PR")
260 							}
261 
262 							op := OpNormal
263+							nextStatus := "open"
264 							if isReview {
265-								wish.Println(sesh, "Marking new patchset as a review")
266+								wish.Println(sesh, "Marking PR as reviewed")
267+								nextStatus = "reviewed"
268 								op = OpReview
269+							} else if isAccept {
270+								wish.Println(sesh, "Marking PR as accepted")
271+								nextStatus = "accepted"
272+								op = OpAccept
273+							} else if isClose {
274+								wish.Println(sesh, "Marking PR as closed")
275+								nextStatus = "closed"
276+								op = OpClose
277 							}
278 
279 							patches, err := pr.SubmitPatchset(prID, user.ID, op, sesh)
280@@ -795,37 +810,15 @@ Here's how it works:
281 								return nil
282 							}
283 
284-							reviewTxt := ""
285-							if isReview {
286-								err = pr.UpdatePatchRequestStatus(prID, user.ID, "reviewed")
287+							if nextStatus != "" {
288+								err = pr.UpdatePatchRequestStatus(prID, user.ID, nextStatus)
289 								if err != nil {
290 									return err
291 								}
292-								reviewTxt = "[review]"
293 							}
294 
295 							wish.Println(sesh, "Patches submitted!")
296-							writer := NewTabWriter(sesh)
297-							fmt.Fprintln(
298-								writer,
299-								"ID\tTitle",
300-							)
301-							for _, patch := range patches {
302-								fmt.Fprintf(
303-									writer,
304-									"%d\t%s %s\n",
305-									patch.ID,
306-									patch.Title,
307-									reviewTxt,
308-								)
309-							}
310-							writer.Flush()
311-
312-							wish.Println(
313-								sesh,
314-								fmt.Sprintf("https://%s/prs/%d", be.Cfg.Url, prq.ID),
315-							)
316-							return nil
317+							return prSummary(be, pr, sesh, prID)
318 						},
319 					},
320 				},
M pr.go
+2, -0
1@@ -17,6 +17,8 @@ type PatchsetOp int
2 const (
3 	OpNormal PatchsetOp = iota
4 	OpReview
5+	OpAccept
6+	OpClose
7 )
8 
9 type GitPatchRequest interface {
M util.go
+3, -0
 1@@ -31,6 +31,9 @@ func randSeq(n int) string {
 2 }
 3 
 4 func truncateSha(sha string) string {
 5+	if len(sha) < 7 {
 6+		return sha
 7+	}
 8 	return sha[:7]
 9 }
10