- commit
- fbfd037
- parent
- 7fda3e7
- author
- Eric Bower
- date
- 2024-05-10 12:05:26 -0400 EDT
progress
M
mdw.go
+78,
-202
1@@ -1,18 +1,16 @@
2 package git
3
4 import (
5- "bytes"
6 "flag"
7 "fmt"
8 "io"
9- "os"
10 "path/filepath"
11 "regexp"
12 "strconv"
13 "strings"
14+ "text/tabwriter"
15 "time"
16
17- "github.com/bluekeyes/go-gitdiff/gitdiff"
18 "github.com/charmbracelet/soft-serve/pkg/git"
19 "github.com/charmbracelet/soft-serve/pkg/utils"
20 "github.com/charmbracelet/ssh"
21@@ -59,197 +57,8 @@ func flagSet(sesh ssh.Session, cmdName string) *flag.FlagSet {
22 return cmd
23 }
24
25-// ssh git.sh ls
26-// git format-patch --stdout | ssh git.sh pr test
27-// git format-patch --stdout | ssh git.sh pr 123 --review
28-// ssh git.sh pr ls
29-// ssh git.sh pr 123 --stdout | git am -3
30-// ssh git.sh pr 123 --approve # or --close
31-// echo "here is a comment" | ssh git.sh pr 123 --comment
32-
33-type GitPatchRequest interface {
34- GetRepos() ([]string, error)
35- SubmitPatchRequest(pubkey string, repoID string, patches io.Reader) (*PatchRequest, error)
36- SubmitPatch(pubkey string, prID int64, review bool, patch io.Reader) (*Patch, error)
37- GetPatchRequestByID(prID int64) (*PatchRequest, error)
38- GetPatchRequests() ([]*PatchRequest, error)
39- GetPatchesByPrID(prID int64) ([]*Patch, error)
40- UpdatePatchRequest(prID int64, status string) error
41-}
42-
43-type PrCmd struct {
44- Backend *Backend
45-}
46-
47-var _ GitPatchRequest = PrCmd{}
48-var _ GitPatchRequest = (*PrCmd)(nil)
49-
50-func (pr PrCmd) GetRepos() ([]string, error) {
51- repos := []string{}
52- entries, err := os.ReadDir(pr.Backend.ReposDir())
53- if err != nil {
54- return repos, err
55- }
56- for _, entry := range entries {
57- if entry.IsDir() {
58- repos = append(repos, entry.Name())
59- }
60- }
61- return repos, nil
62-}
63-
64-func (pr PrCmd) GetPatchesByPrID(prID int64) ([]*Patch, error) {
65- patches := []*Patch{}
66- err := pr.Backend.DB.Select(
67- &patches,
68- "SELECT * FROM patches WHERE patch_request_id=?",
69- prID,
70- )
71- if err != nil {
72- return patches, err
73- }
74- if len(patches) == 0 {
75- return patches, fmt.Errorf("no patches found for Patch Request ID: %d", prID)
76- }
77- return patches, nil
78-}
79-
80-func (cmd PrCmd) GetPatchRequests() ([]*PatchRequest, error) {
81- prs := []*PatchRequest{}
82- err := cmd.Backend.DB.Select(
83- &prs,
84- "SELECT * FROM patch_requests",
85- )
86- return prs, err
87-}
88-
89-func (cmd PrCmd) GetPatchRequestByID(prID int64) (*PatchRequest, error) {
90- pr := PatchRequest{}
91- err := cmd.Backend.DB.Get(
92- &pr,
93- "SELECT * FROM patch_requests WHERE id=?",
94- prID,
95- )
96- return &pr, err
97-}
98-
99-// status types: open, close, accept, review
100-func (cmd PrCmd) UpdatePatchRequest(prID int64, status string) error {
101- _, err := cmd.Backend.DB.Exec(
102- "UPDATE patch_requests SET status=? WHERE id=?", status, prID,
103- )
104- return err
105-}
106-
107-func (cmd PrCmd) SubmitPatch(pubkey string, prID int64, review bool, patch io.Reader) (*Patch, error) {
108- pr := PatchRequest{}
109- err := cmd.Backend.DB.Get(&pr, "SELECT * FROM patch_requests WHERE id=?", prID)
110- if err != nil {
111- return nil, err
112- }
113-
114- // need to read io.Reader from session twice
115- var buf bytes.Buffer
116- tee := io.TeeReader(patch, &buf)
117-
118- _, preamble, err := gitdiff.Parse(tee)
119- if err != nil {
120- return nil, err
121- }
122- header, err := gitdiff.ParsePatchHeader(preamble)
123- if err != nil {
124- return nil, err
125- }
126-
127- patchID := 0
128- row := cmd.Backend.DB.QueryRow(
129- "INSERT INTO patches (pubkey, patch_request_id, author_name, author_email, author_date, title, body, body_appendix, commit_sha, review, raw_text) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING id",
130- pubkey,
131- prID,
132- header.Author.Name,
133- header.Author.Email,
134- header.AuthorDate,
135- header.Title,
136- header.Body,
137- header.BodyAppendix,
138- header.SHA,
139- review,
140- buf.String(),
141- )
142- err = row.Scan(&patchID)
143- if err != nil {
144- return nil, err
145- }
146-
147- var patchRec Patch
148- err = cmd.Backend.DB.Get(&patchRec, "SELECT * FROM patches WHERE id=?", patchID)
149- return &patchRec, err
150-}
151-
152-func (cmd PrCmd) SubmitPatchRequest(pubkey string, repoID string, patches io.Reader) (*PatchRequest, error) {
153- err := git.EnsureWithin(cmd.Backend.ReposDir(), repoID)
154- if err != nil {
155- return nil, err
156- }
157- loc := filepath.Join(cmd.Backend.ReposDir(), repoID)
158- _, err = os.Stat(loc)
159- if os.IsNotExist(err) {
160- return nil, fmt.Errorf("repo does not exist: %s", loc)
161- }
162-
163- // need to read io.Reader from session twice
164- var buf bytes.Buffer
165- tee := io.TeeReader(patches, &buf)
166-
167- _, preamble, err := gitdiff.Parse(tee)
168- if err != nil {
169- return nil, err
170- }
171- header, err := gitdiff.ParsePatchHeader(preamble)
172- if err != nil {
173- return nil, err
174- }
175- prName := header.Title
176- prDesc := header.Body
177-
178- var prID int64
179- row := cmd.Backend.DB.QueryRow(
180- "INSERT INTO patch_requests (pubkey, repo_id, name, text, status, updated_at) VALUES(?, ?, ?, ?, ?, ?) RETURNING id",
181- pubkey,
182- repoID,
183- prName,
184- prDesc,
185- "open",
186- time.Now(),
187- )
188- err = row.Scan(&prID)
189- if err != nil {
190- return nil, err
191- }
192- if prID == 0 {
193- return nil, fmt.Errorf("could not create patch request")
194- }
195-
196- _, err = cmd.Backend.DB.Exec(
197- "INSERT INTO patches (pubkey, patch_request_id, author_name, author_email, author_date, title, body, body_appendix, commit_sha, raw_text) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
198- pubkey,
199- prID,
200- header.Author.Name,
201- header.Author.Email,
202- header.AuthorDate,
203- header.Title,
204- header.Body,
205- header.BodyAppendix,
206- header.SHA,
207- buf.String(),
208- )
209- if err != nil {
210- return nil, err
211- }
212-
213- var pr PatchRequest
214- err = cmd.Backend.DB.Get(&pr, "SELECT * FROM patch_requests WHERE id=?", prID)
215- return &pr, err
216+func NewTabWriter(out io.Writer) *tabwriter.Writer {
217+ return tabwriter.NewWriter(out, 0, 0, 1, ' ', tabwriter.TabIndent)
218 }
219
220 func GitPatchRequestMiddleware(be *Backend, pr GitPatchRequest) wish.Middleware {
221@@ -279,15 +88,17 @@ func GitPatchRequestMiddleware(be *Backend, pr GitPatchRequest) wish.Middleware
222 wish.Fatalln(sesh, err)
223 return
224 }
225- wish.Printf(sesh, "Name\tDir\n")
226+ writer := NewTabWriter(sesh)
227+ fmt.Fprintln(writer, "Name\tDir")
228 for _, repo := range repos {
229- wish.Printf(
230- sesh,
231+ fmt.Fprintf(
232+ writer,
233 "%s\t%s\n",
234 utils.SanitizeRepo(repo),
235 filepath.Join(be.ReposDir(), repo),
236 )
237 }
238+ writer.Flush()
239 } else if cmd == "pr" {
240 prCmd := flagSet(sesh, "pr")
241 out := prCmd.Bool("stdout", false, "print patchset to stdout")
242@@ -295,6 +106,7 @@ func GitPatchRequestMiddleware(be *Backend, pr GitPatchRequest) wish.Middleware
243 closed := prCmd.Bool("close", false, "mark patch request as closed")
244 review := prCmd.Bool("review", false, "mark patch request as reviewed")
245 stats := prCmd.Bool("stats", false, "return summary instead of patches")
246+ prLs := prCmd.Bool("ls", false, "return list of patches")
247
248 if len(args) < 2 {
249 wish.Fatalln(sesh, "must provide repo name or patch request ID")
250@@ -334,10 +146,20 @@ func GitPatchRequestMiddleware(be *Backend, pr GitPatchRequest) wish.Middleware
251 wish.Fatalln(sesh, err)
252 return
253 }
254- wish.Printf(sesh, "Name\tID\n")
255+
256+ writer := NewTabWriter(sesh)
257+ fmt.Fprintln(writer, "ID\tName\tStatus\tDate")
258 for _, req := range prs {
259- wish.Printf(sesh, "%s\t%d\n", req.Name, req.ID)
260+ fmt.Fprintf(
261+ writer,
262+ "%d\t%s\t[%s]\t%s\n",
263+ req.ID,
264+ req.Name,
265+ req.Status,
266+ req.CreatedAt.Format(time.RFC3339Nano),
267+ )
268 }
269+ writer.Flush()
270 } else if subCmd == "submitPatchRequest" {
271 request, err := pr.SubmitPatchRequest(pubkey, repoID, sesh)
272 if err != nil {
273@@ -346,6 +168,36 @@ func GitPatchRequestMiddleware(be *Backend, pr GitPatchRequest) wish.Middleware
274 }
275 wish.Printf(sesh, "Patch Request submitted! Use the ID for interacting with this Patch Request.\nID\tName\n%d\t%s\n", request.ID, request.Name)
276 } else if subCmd == "patchRequest" {
277+ if *prLs {
278+ _, err := pr.GetPatchRequestByID(prID)
279+ if err != nil {
280+ wish.Fatalln(sesh, err)
281+ return
282+ }
283+
284+ patches, err := pr.GetPatchesByPrID(prID)
285+ if err != nil {
286+ wish.Fatalln(sesh, err)
287+ return
288+ }
289+
290+ writer := NewTabWriter(sesh)
291+ fmt.Fprintln(writer, "ID\tTitle\tReview\tAuthor\tDate")
292+ for _, patch := range patches {
293+ fmt.Fprintf(
294+ writer,
295+ "%d\t%s\t%t\t%s\t%s\n",
296+ patch.ID,
297+ patch.Title,
298+ patch.Review,
299+ patch.AuthorName,
300+ patch.AuthorDate.Format(time.RFC3339Nano),
301+ )
302+ }
303+ writer.Flush()
304+ return
305+ }
306+
307 if *stats {
308 request, err := pr.GetPatchRequestByID(prID)
309 if err != nil {
310@@ -353,7 +205,15 @@ func GitPatchRequestMiddleware(be *Backend, pr GitPatchRequest) wish.Middleware
311 return
312 }
313
314- wish.Printf(sesh, "%s\t[%s]\n%s\n%s\n%s\n", request.Name, request.Status, request.CreatedAt.Format(time.RFC3339Nano), request.Pubkey, request.Text)
315+ writer := NewTabWriter(sesh)
316+ fmt.Fprintln(writer, "ID\tName\tStatus\tDate")
317+ fmt.Fprintf(
318+ writer,
319+ "%d\t%s\t[%s]\t%s\n%s\n\n",
320+ request.ID, request.Name, request.Status, request.CreatedAt.Format(time.RFC3339Nano),
321+ request.Text,
322+ )
323+ writer.Flush()
324
325 patches, err := pr.GetPatchesByPrID(prID)
326 if err != nil {
327@@ -368,7 +228,7 @@ func GitPatchRequestMiddleware(be *Backend, pr GitPatchRequest) wish.Middleware
328 }
329 wish.Printf(
330 sesh,
331- "%s %s %s\n%s <%s>\n%s\n\n---\n%s\n%s\n",
332+ "%s %s %s\n%s <%s>\n%s\n\n---\n%s\n%s\n\n\n",
333 patch.Title,
334 reviewTxt,
335 patch.CommitSha,
336@@ -436,14 +296,30 @@ func GitPatchRequestMiddleware(be *Backend, pr GitPatchRequest) wish.Middleware
337 wish.Fatalln(sesh, err)
338 return
339 }
340+ reviewTxt := ""
341 if *review {
342 err = pr.UpdatePatchRequest(prID, "review")
343 if err != nil {
344 wish.Fatalln(sesh, err)
345 return
346 }
347+ reviewTxt = "[review]"
348 }
349- wish.Printf(sesh, "Patch submitted! (ID:%d)\n", patch.ID)
350+
351+ wish.Println(sesh, "Patch submitted!")
352+ writer := NewTabWriter(sesh)
353+ fmt.Fprintln(
354+ writer,
355+ "ID\tTitle",
356+ )
357+ fmt.Fprintf(
358+ writer,
359+ "%d\t%s %s\n",
360+ patch.ID,
361+ patch.Title,
362+ reviewTxt,
363+ )
364+ writer.Flush()
365 }
366 }
367
A
pr.go
+198,
-0
1@@ -0,0 +1,198 @@
2+package git
3+
4+import (
5+ "bytes"
6+ "fmt"
7+ "io"
8+ "os"
9+ "path/filepath"
10+ "time"
11+
12+ "github.com/bluekeyes/go-gitdiff/gitdiff"
13+ "github.com/charmbracelet/soft-serve/pkg/git"
14+)
15+
16+type GitPatchRequest interface {
17+ GetRepos() ([]string, error)
18+ SubmitPatchRequest(pubkey string, repoID string, patches io.Reader) (*PatchRequest, error)
19+ SubmitPatch(pubkey string, prID int64, review bool, patch io.Reader) (*Patch, error)
20+ GetPatchRequestByID(prID int64) (*PatchRequest, error)
21+ GetPatchRequests() ([]*PatchRequest, error)
22+ GetPatchesByPrID(prID int64) ([]*Patch, error)
23+ UpdatePatchRequest(prID int64, status string) error
24+}
25+
26+type PrCmd struct {
27+ Backend *Backend
28+}
29+
30+var _ GitPatchRequest = PrCmd{}
31+var _ GitPatchRequest = (*PrCmd)(nil)
32+
33+func (pr PrCmd) GetRepos() ([]string, error) {
34+ repos := []string{}
35+ entries, err := os.ReadDir(pr.Backend.ReposDir())
36+ if err != nil {
37+ return repos, err
38+ }
39+ for _, entry := range entries {
40+ if entry.IsDir() {
41+ repos = append(repos, entry.Name())
42+ }
43+ }
44+ return repos, nil
45+}
46+
47+func (pr PrCmd) GetPatchesByPrID(prID int64) ([]*Patch, error) {
48+ patches := []*Patch{}
49+ err := pr.Backend.DB.Select(
50+ &patches,
51+ "SELECT * FROM patches WHERE patch_request_id=?",
52+ prID,
53+ )
54+ if err != nil {
55+ return patches, err
56+ }
57+ if len(patches) == 0 {
58+ return patches, fmt.Errorf("no patches found for Patch Request ID: %d", prID)
59+ }
60+ return patches, nil
61+}
62+
63+func (cmd PrCmd) GetPatchRequests() ([]*PatchRequest, error) {
64+ prs := []*PatchRequest{}
65+ err := cmd.Backend.DB.Select(
66+ &prs,
67+ "SELECT * FROM patch_requests",
68+ )
69+ return prs, err
70+}
71+
72+func (cmd PrCmd) GetPatchRequestByID(prID int64) (*PatchRequest, error) {
73+ pr := PatchRequest{}
74+ err := cmd.Backend.DB.Get(
75+ &pr,
76+ "SELECT * FROM patch_requests WHERE id=?",
77+ prID,
78+ )
79+ return &pr, err
80+}
81+
82+// Status types: open, close, accept, review.
83+func (cmd PrCmd) UpdatePatchRequest(prID int64, status string) error {
84+ _, err := cmd.Backend.DB.Exec(
85+ "UPDATE patch_requests SET status=? WHERE id=?", status, prID,
86+ )
87+ return err
88+}
89+
90+func (cmd PrCmd) SubmitPatch(pubkey string, prID int64, review bool, patch io.Reader) (*Patch, error) {
91+ pr := PatchRequest{}
92+ err := cmd.Backend.DB.Get(&pr, "SELECT * FROM patch_requests WHERE id=?", prID)
93+ if err != nil {
94+ return nil, err
95+ }
96+
97+ // need to read io.Reader from session twice
98+ var buf bytes.Buffer
99+ tee := io.TeeReader(patch, &buf)
100+
101+ _, preamble, err := gitdiff.Parse(tee)
102+ if err != nil {
103+ return nil, err
104+ }
105+ header, err := gitdiff.ParsePatchHeader(preamble)
106+ if err != nil {
107+ return nil, err
108+ }
109+
110+ patchID := 0
111+ row := cmd.Backend.DB.QueryRow(
112+ "INSERT INTO patches (pubkey, patch_request_id, author_name, author_email, author_date, title, body, body_appendix, commit_sha, review, raw_text) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING id",
113+ pubkey,
114+ prID,
115+ header.Author.Name,
116+ header.Author.Email,
117+ header.AuthorDate,
118+ header.Title,
119+ header.Body,
120+ header.BodyAppendix,
121+ header.SHA,
122+ review,
123+ buf.String(),
124+ )
125+ err = row.Scan(&patchID)
126+ if err != nil {
127+ return nil, err
128+ }
129+
130+ var patchRec Patch
131+ err = cmd.Backend.DB.Get(&patchRec, "SELECT * FROM patches WHERE id=?", patchID)
132+ return &patchRec, err
133+}
134+
135+func (cmd PrCmd) SubmitPatchRequest(pubkey string, repoID string, patches io.Reader) (*PatchRequest, error) {
136+ err := git.EnsureWithin(cmd.Backend.ReposDir(), repoID)
137+ if err != nil {
138+ return nil, err
139+ }
140+ loc := filepath.Join(cmd.Backend.ReposDir(), repoID)
141+ _, err = os.Stat(loc)
142+ if os.IsNotExist(err) {
143+ return nil, fmt.Errorf("repo does not exist: %s", loc)
144+ }
145+
146+ // need to read io.Reader from session twice
147+ var buf bytes.Buffer
148+ tee := io.TeeReader(patches, &buf)
149+
150+ _, preamble, err := gitdiff.Parse(tee)
151+ if err != nil {
152+ return nil, err
153+ }
154+ header, err := gitdiff.ParsePatchHeader(preamble)
155+ if err != nil {
156+ return nil, err
157+ }
158+ prName := header.Title
159+ prDesc := header.Body
160+
161+ var prID int64
162+ row := cmd.Backend.DB.QueryRow(
163+ "INSERT INTO patch_requests (pubkey, repo_id, name, text, status, updated_at) VALUES(?, ?, ?, ?, ?, ?) RETURNING id",
164+ pubkey,
165+ repoID,
166+ prName,
167+ prDesc,
168+ "open",
169+ time.Now(),
170+ )
171+ err = row.Scan(&prID)
172+ if err != nil {
173+ return nil, err
174+ }
175+ if prID == 0 {
176+ return nil, fmt.Errorf("could not create patch request")
177+ }
178+
179+ _, err = cmd.Backend.DB.Exec(
180+ "INSERT INTO patches (pubkey, patch_request_id, author_name, author_email, author_date, title, body, body_appendix, commit_sha, raw_text) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
181+ pubkey,
182+ prID,
183+ header.Author.Name,
184+ header.Author.Email,
185+ header.AuthorDate,
186+ header.Title,
187+ header.Body,
188+ header.BodyAppendix,
189+ header.SHA,
190+ buf.String(),
191+ )
192+ if err != nil {
193+ return nil, err
194+ }
195+
196+ var pr PatchRequest
197+ err = cmd.Backend.DB.Get(&pr, "SELECT * FROM patch_requests WHERE id=?", prID)
198+ return &pr, err
199+}