repos / git-pr

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

commit
c20af52
parent
8b876e0
author
Eric Bower
date
2024-05-05 12:58:02 -0400 EDT
changes
3 files changed,  +98, -67
M db.go
M mdw.go
M ssh.go
M db.go
+3, -3
 1@@ -8,7 +8,7 @@ import (
 2 	_ "modernc.org/sqlite"
 3 )
 4 
 5-// PatchRequest is a database model for patches submitted to a Repo
 6+// PatchRequest is a database model for patches submitted to a Repo.
 7 type PatchRequest struct {
 8 	ID        int64     `db:"id"`
 9 	Pubkey    string    `db:"pubkey"`
10@@ -19,7 +19,7 @@ type PatchRequest struct {
11 	UpdatedAt time.Time `db:"updated_at"`
12 }
13 
14-// Patch is a database model for a single entry in a patchset
15+// Patch is a database model for a single entry in a patchset.
16 // This usually corresponds to a git commit.
17 type Patch struct {
18 	ID             int64     `db:"id"`
19@@ -36,7 +36,7 @@ type Patch struct {
20 	CreatedAt      time.Time `db:"created_at"`
21 }
22 
23-// Comment is a database model for a non-patch comment within a PatchRequest
24+// Comment is a database model for a non-patch comment within a PatchRequest.
25 type Comment struct {
26 	ID             int64     `db:"id"`
27 	Pubkey         string    `db:"pubkey"`
M mdw.go
+91, -63
  1@@ -55,7 +55,7 @@ func gitServiceCommands(sesh ssh.Session, be *Backend, cmd, repo string) error {
  2 
  3 func try(sesh ssh.Session, err error) {
  4 	if err != nil {
  5-		wish.Fatal(sesh, err)
  6+		wish.Fatalln(sesh, err)
  7 	}
  8 }
  9 
 10@@ -65,60 +65,67 @@ func flagSet(sesh ssh.Session, cmdName string) *flag.FlagSet {
 11 	return cmd
 12 }
 13 
 14+type GitPatchRequest interface {
 15+	GetPatchesByPrID(prID int64) ([]*Patch, error)
 16+	SubmitPatch(pubkey string, prID int64, patch io.Reader) (*Patch, error)
 17+	SubmitPatchRequest(pubkey string, repoName string, patches io.Reader) (*PatchRequest, error)
 18+}
 19+
 20 type PrCmd struct {
 21-	Session ssh.Session
 22 	Backend *Backend
 23-	Repo    string
 24-	Pubkey  string
 25 }
 26 
 27-func (pr *PrCmd) PrintPatches(prID int64) {
 28+var _ GitPatchRequest = PrCmd{}
 29+var _ GitPatchRequest = (*PrCmd)(nil)
 30+
 31+func (pr PrCmd) GetPatchesByPrID(prID int64) ([]*Patch, error) {
 32 	patches := []*Patch{}
 33-	pr.Backend.DB.Select(
 34+	err := pr.Backend.DB.Select(
 35 		&patches,
 36 		"SELECT * FROM patches WHERE patch_request_id=?",
 37 		prID,
 38 	)
 39-	if len(patches) == 0 {
 40-		wish.Printf(pr.Session, "no patches found for Patch Request ID: %d\n", prID)
 41-		return
 42-	}
 43-
 44-	if len(patches) == 1 {
 45-		wish.Println(pr.Session, patches[0].RawText)
 46-		return
 47+	if err != nil {
 48+		return patches, err
 49 	}
 50-
 51-	for _, patch := range patches {
 52-		wish.Printf(pr.Session, "%s\n\n\n", patch.RawText)
 53+	if len(patches) == 0 {
 54+		return patches, fmt.Errorf("no patches found for Patch Request ID: %d", prID)
 55 	}
 56+	return patches, nil
 57 }
 58 
 59-func (cmd *PrCmd) SubmitPatch(prID int64) {
 60+func (cmd PrCmd) SubmitPatch(pubkey string, prID int64, patch io.Reader) (*Patch, error) {
 61 	pr := PatchRequest{}
 62-	cmd.Backend.DB.Get(&pr, "SELECT * FROM patch_requests WHERE id=?", prID)
 63+	err := cmd.Backend.DB.Get(&pr, "SELECT * FROM patch_requests WHERE id=?", prID)
 64+	if err != nil {
 65+		return nil, err
 66+	}
 67 	if pr.ID == 0 {
 68-		wish.Fatalln(cmd.Session, "patch request does not exist")
 69-		return
 70+		return nil, fmt.Errorf("patch request (ID: %d) does not exist", prID)
 71 	}
 72 
 73 	review := false
 74-	if pr.Pubkey != cmd.Pubkey {
 75+	if pr.Pubkey != pubkey {
 76 		review = true
 77 	}
 78 
 79 	// need to read io.Reader from session twice
 80 	var buf bytes.Buffer
 81-	tee := io.TeeReader(cmd.Session, &buf)
 82+	tee := io.TeeReader(patch, &buf)
 83 
 84 	_, preamble, err := gitdiff.Parse(tee)
 85-	try(cmd.Session, err)
 86+	if err != nil {
 87+		return nil, err
 88+	}
 89 	header, err := gitdiff.ParsePatchHeader(preamble)
 90-	try(cmd.Session, err)
 91+	if err != nil {
 92+		return nil, err
 93+	}
 94 
 95-	_, err = cmd.Backend.DB.Exec(
 96-		"INSERT INTO patches (pubkey, patch_request_id, author_name, author_email, title, body, commit_sha, commit_date, review, raw_text) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
 97-		cmd.Pubkey,
 98+	patchID := 0
 99+	row := cmd.Backend.DB.QueryRow(
100+		"INSERT INTO patches (pubkey, patch_request_id, author_name, author_email, title, body, commit_sha, commit_date, review, raw_text) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING id",
101+		pubkey,
102 		prID,
103 		header.Author.Name,
104 		header.Author.Email,
105@@ -129,50 +136,61 @@ func (cmd *PrCmd) SubmitPatch(prID int64) {
106 		review,
107 		buf.String(),
108 	)
109-	try(cmd.Session, err)
110+	err = row.Scan(&patchID)
111+	if err != nil {
112+		return nil, err
113+	}
114 
115-	wish.Printf(cmd.Session, "submitted review!\n")
116+	var patchRec Patch
117+	err = cmd.Backend.DB.Get(&patchRec, "SELECT * FROM patches WHERE id=?")
118+	return &patchRec, err
119 }
120 
121-func (cmd *PrCmd) SubmitPatchRequest(repoName string) {
122+func (cmd PrCmd) SubmitPatchRequest(pubkey string, repoName string, patches io.Reader) (*PatchRequest, error) {
123 	err := git.EnsureWithin(cmd.Backend.ReposDir(), cmd.Backend.RepoName(repoName))
124-	try(cmd.Session, err)
125+	if err != nil {
126+		return nil, err
127+	}
128 	_, err = os.Stat(filepath.Join(cmd.Backend.ReposDir(), cmd.Backend.RepoName(repoName)))
129 	if os.IsNotExist(err) {
130-		wish.Fatalln(cmd.Session, "repo does not exist")
131-		return
132+		return nil, fmt.Errorf("repo does not exist: %s", repoName)
133 	}
134 
135 	// need to read io.Reader from session twice
136 	var buf bytes.Buffer
137-	tee := io.TeeReader(cmd.Session, &buf)
138+	tee := io.TeeReader(patches, &buf)
139 
140 	_, preamble, err := gitdiff.Parse(tee)
141-	try(cmd.Session, err)
142+	if err != nil {
143+		return nil, err
144+	}
145 	header, err := gitdiff.ParsePatchHeader(preamble)
146-	try(cmd.Session, err)
147+	if err != nil {
148+		return nil, err
149+	}
150 	prName := header.Title
151 	prDesc := header.Body
152 
153 	var prID int64
154 	row := cmd.Backend.DB.QueryRow(
155 		"INSERT INTO patch_requests (pubkey, repo_id, name, text, updated_at) VALUES(?, ?, ?, ?, ?) RETURNING id",
156-		cmd.Pubkey,
157+		pubkey,
158 		repoName,
159 		prName,
160 		prDesc,
161 		time.Now(),
162 	)
163-	row.Scan(&prID)
164+	err = row.Scan(&prID)
165+	if err != nil {
166+		return nil, err
167+	}
168 	if prID == 0 {
169-		wish.Fatal(cmd.Session, "could not create patch request")
170-		return
171+		return nil, fmt.Errorf("could not create patch request")
172 	}
173-	try(cmd.Session, err)
174 
175 	_, err = cmd.Backend.DB.Exec(
176 		"INSERT INTO patches (pubkey, patch_request_id, author_name, author_email, title, body, commit_sha, commit_date, raw_text) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
177-		cmd.Pubkey,
178+		pubkey,
179 		prID,
180 		header.Author.Name,
181 		header.Author.Email,
182@@ -182,17 +200,16 @@ func (cmd *PrCmd) SubmitPatchRequest(repoName string) {
183 		header.CommitterDate,
184 		buf.String(),
185 	)
186-	try(cmd.Session, err)
187+	if err != nil {
188+		return nil, err
189+	}
190 
191-	wish.Printf(
192-		cmd.Session,
193-		"created patch request!\nID: %d\nTitle: %s\n",
194-		prID,
195-		prName,
196-	)
197+	var pr PatchRequest
198+	err = cmd.Backend.DB.Get(&pr, "SELECT * FROM patch_requests WHERE id=?", prID)
199+	return &pr, err
200 }
201 
202-func GitPatchRequestMiddleware(be *Backend) wish.Middleware {
203+func GitPatchRequestMiddleware(be *Backend, pr GitPatchRequest) wish.Middleware {
204 	isNumRe := regexp.MustCompile(`^\d+$`)
205 
206 	return func(next ssh.Handler) ssh.Handler {
207@@ -209,9 +226,7 @@ func GitPatchRequestMiddleware(be *Backend) wish.Middleware {
208 				wish.Println(sesh, "commands: [help, pr, ls, git-receive-pack, git-upload-pack]")
209 			} else if cmd == "ls" {
210 				entries, err := os.ReadDir(be.ReposDir())
211-				if err != nil {
212-					wish.Fatal(sesh, err)
213-				}
214+				try(sesh, err)
215 
216 				for _, e := range entries {
217 					if e.IsDir() {
218@@ -246,20 +261,33 @@ func GitPatchRequestMiddleware(be *Backend) wish.Middleware {
219 				}
220 				out := prCmd.Bool("stdout", false, "print patchset to stdout")
221 
222-				pr := &PrCmd{
223-					Session: sesh,
224-					Backend: be,
225-					Pubkey:  pubkey,
226-				}
227+				if *out {
228+					patches, err := pr.GetPatchesByPrID(prID)
229+					try(sesh, err)
230 
231-				if *out == true {
232-					pr.PrintPatches(prID)
233+					if len(patches) == 0 {
234+						wish.Println(sesh, patches[0].RawText)
235+						return
236+					}
237+
238+					for _, patch := range patches {
239+						wish.Printf(sesh, "%s\n\n\n", patch.RawText)
240+					}
241 				} else if prID != 0 {
242-					pr.SubmitPatch(prID)
243+					patch, err := pr.SubmitPatch(pubkey, prID, sesh)
244+					if err != nil {
245+						wish.Fatalln(sesh, err)
246+						return
247+					}
248+					wish.Printf(sesh, "Patch submitted! (ID:%d)\n", patch.ID)
249 				} else if subCmd == "ls" {
250 					wish.Println(sesh, "list all patch requests")
251 				} else if repoName != "" {
252-					pr.SubmitPatchRequest(repoName)
253+					request, err := pr.SubmitPatchRequest(pubkey, repoName, sesh)
254+					if err != nil {
255+						wish.Fatalln(sesh, err)
256+					}
257+					wish.Printf(sesh, "Patch Request submitted! (ID:%d)\n", request.ID)
258 				}
259 
260 				return
M ssh.go
+4, -1
 1@@ -40,6 +40,9 @@ func GitSshServer() {
 2 		Logger: logger,
 3 		Cfg:    cfg,
 4 	}
 5+	prCmd := &PrCmd{
 6+		Backend: be,
 7+	}
 8 
 9 	s, err := wish.NewServer(
10 		wish.WithAddress(
11@@ -50,7 +53,7 @@ func GitSshServer() {
12 		),
13 		wish.WithPublicKeyAuth(authHandler),
14 		wish.WithMiddleware(
15-			GitPatchRequestMiddleware(be),
16+			GitPatchRequestMiddleware(be, prCmd),
17 		),
18 	)
19