repos / git-pr

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

commit
751f871
parent
d60f2ca
author
Eric Bower
date
2024-05-02 00:45:15 -0400 EDT
nice
4 files changed,  +107, -79
M cfg.go
M mdw.go
M ssh.go
M cfg.go
+4, -2
 1@@ -1,11 +1,13 @@
 2 package git
 3 
 4 type GitCfg struct {
 5-	DataPath string
 6+	DataPath     string
 7+	AdminPubkeys []string
 8 }
 9 
10 func NewGitCfg() *GitCfg {
11 	return &GitCfg{
12-		DataPath: "ssh_data",
13+		DataPath:     "ssh_data",
14+		AdminPubkeys: []string{},
15 	}
16 }
M mdw.go
+92, -47
  1@@ -4,7 +4,9 @@ import (
  2 	"bytes"
  3 	"fmt"
  4 	"io"
  5+	"os"
  6 	"path/filepath"
  7+	"strconv"
  8 	"time"
  9 
 10 	"github.com/bluekeyes/go-gitdiff/gitdiff"
 11@@ -66,63 +68,106 @@ func GitServerMiddleware(be *Backend) wish.Middleware {
 12 				err := gitServiceCommands(sesh, be, cmd, repoName)
 13 				try(sesh, err)
 14 			} else if cmd == "help" {
 15-				wish.Println(sesh, "commands: [help, git-receive-pack, git-upload-pack]")
 16+				wish.Println(sesh, "commands: [help, pr, ls, git-receive-pack, git-upload-pack]")
 17+			} else if cmd == "ls" {
 18+				entries, err := os.ReadDir(be.ReposDir())
 19+				if err != nil {
 20+					wish.Fatal(sesh, err)
 21+				}
 22+
 23+				for _, e := range entries {
 24+					if e.IsDir() {
 25+						wish.Println(sesh, utils.SanitizeRepo(e.Name()))
 26+					}
 27+				}
 28 			} else if cmd == "pr" {
 29 				if len(args) < 2 {
 30 					wish.Fatal(sesh, "must provide repo name")
 31 					return
 32 				}
 33+
 34 				repoName := utils.SanitizeRepo(args[1])
 35-				err := git.EnsureWithin(be.ReposDir(), be.RepoName(repoName))
 36-				try(sesh, err)
 37 
 38-				// need to read io.Reader from session twice
 39-				var buf bytes.Buffer
 40-				tee := io.TeeReader(sesh, &buf)
 41+				if len(args) > 2 {
 42+					prID, err := strconv.ParseInt(args[2], 10, 64)
 43+					try(sesh, err)
 44 
 45-				_, preamble, err := gitdiff.Parse(tee)
 46-				try(sesh, err)
 47-				header, err := gitdiff.ParsePatchHeader(preamble)
 48-				try(sesh, err)
 49-				prName := header.Title
 50-				prDesc := header.Body
 51-
 52-				var prID int64
 53-				row := be.DB.QueryRow(
 54-					"INSERT INTO patch_requests (pubkey, repo_id, name, text, updated_at) VALUES(?, ?, ?, ?, ?) RETURNING id",
 55-					pubkey,
 56-					repoName,
 57-					prName,
 58-					prDesc,
 59-					time.Now(),
 60-				)
 61-				row.Scan(&prID)
 62-				if prID == 0 {
 63-					wish.Fatal(sesh, "could not create patch request")
 64-					return
 65-				}
 66-				try(sesh, err)
 67+					patches := []*Patch{}
 68+					be.DB.Select(
 69+						&patches,
 70+						"SELECT * FROM patches WHERE patch_request_id=?",
 71+						prID,
 72+					)
 73+					if len(patches) == 0 {
 74+						wish.Printf(sesh, "No patches found for Patch Request ID: %d\n", prID)
 75+						return
 76+					}
 77 
 78-				_, err = be.DB.Exec(
 79-					"INSERT INTO patches (pubkey, patch_request_id, author_name, author_email, title, body, commit_sha, commit_date, raw_text) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
 80-					pubkey,
 81-					prID,
 82-					header.Author.Name,
 83-					header.Author.Email,
 84-					header.Title,
 85-					header.Body,
 86-					header.SHA,
 87-					header.CommitterDate,
 88-					buf.String(),
 89-				)
 90-				try(sesh, err)
 91+					if len(patches) == 1 {
 92+						wish.Println(sesh, patches[0].RawText)
 93+						return
 94+					}
 95+
 96+					for _, patch := range patches {
 97+						wish.Printf(sesh, "%s\n\n\n", patch.RawText)
 98+					}
 99+				} else {
100+					err := git.EnsureWithin(be.ReposDir(), be.RepoName(repoName))
101+					try(sesh, err)
102+					_, err = os.Stat(filepath.Join(be.ReposDir(), be.RepoName(repoName)))
103+					if os.IsNotExist(err) {
104+						wish.Fatalln(sesh, "repo does not exist")
105+						return
106+					}
107+
108+					// need to read io.Reader from session twice
109+					var buf bytes.Buffer
110+					tee := io.TeeReader(sesh, &buf)
111 
112-				wish.Printf(
113-					sesh,
114-					"Create Patch Request!\nID: %d\nTitle: %s\n",
115-					prID,
116-					prName,
117-				)
118+					_, preamble, err := gitdiff.Parse(tee)
119+					try(sesh, err)
120+					header, err := gitdiff.ParsePatchHeader(preamble)
121+					try(sesh, err)
122+					prName := header.Title
123+					prDesc := header.Body
124+
125+					var prID int64
126+					row := be.DB.QueryRow(
127+						"INSERT INTO patch_requests (pubkey, repo_id, name, text, updated_at) VALUES(?, ?, ?, ?, ?) RETURNING id",
128+						pubkey,
129+						repoName,
130+						prName,
131+						prDesc,
132+						time.Now(),
133+					)
134+					row.Scan(&prID)
135+					if prID == 0 {
136+						wish.Fatal(sesh, "could not create patch request")
137+						return
138+					}
139+					try(sesh, err)
140+
141+					_, err = be.DB.Exec(
142+						"INSERT INTO patches (pubkey, patch_request_id, author_name, author_email, title, body, commit_sha, commit_date, raw_text) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
143+						pubkey,
144+						prID,
145+						header.Author.Name,
146+						header.Author.Email,
147+						header.Title,
148+						header.Body,
149+						header.SHA,
150+						header.CommitterDate,
151+						buf.String(),
152+					)
153+					try(sesh, err)
154+
155+					wish.Printf(
156+						sesh,
157+						"Create Patch Request!\nID: %d\nTitle: %s\n",
158+						prID,
159+						prName,
160+					)
161+				}
162 
163 				return
164 			} else {
M models.go
+10, -29
 1@@ -1,37 +1,16 @@
 2 package git
 3 
 4 import (
 5-	"database/sql"
 6 	"time"
 7 )
 8 
 9-// User is the entity repesenting a pubkey authenticated user
10-// A user and a single ssh key-pair are synonymous in this context
11-type User struct {
12-	ID        int64     `db:"id"`
13-	Name      string    `db:"name"`
14-	Pubkey    string    `db:"pubkey"`
15-	CreatedAt time.Time `db:"created_at"`
16-	UpdatedAt time.Time `db:"updated_at"`
17-}
18-
19-// Repo is a database model for a repository.
20-type Repo struct {
21-	ID          int64         `db:"id"`
22-	Name        string        `db:"name"`
23-	Description string        `db:"description"`
24-	Private     bool          `db:"private"`
25-	UserID      sql.NullInt64 `db:"user_id"`
26-	CreatedAt   time.Time     `db:"created_at"`
27-	UpdatedAt   time.Time     `db:"updated_at"`
28-}
29-
30 // PatchRequest is a database model for patches submitted to a Repo
31 type PatchRequest struct {
32 	ID        int64     `db:"id"`
33-	UserID    int64     `db:"user_id"`
34+	Pubkey    string    `db:"pubkey"`
35 	RepoID    int64     `db:"repo_id"`
36 	Name      string    `db:"name"`
37+	Text      string    `db:"text"`
38 	CreatedAt time.Time `db:"created_at"`
39 	UpdatedAt time.Time `db:"updated_at"`
40 }
41@@ -40,13 +19,15 @@ type PatchRequest struct {
42 // This usually corresponds to a git commit.
43 type Patch struct {
44 	ID             int64     `db:"id"`
45-	UserID         int64     `db:"user_id"`
46+	Pubkey         string    `db:"pubkey"`
47 	PatchRequestID int64     `db:"patch_request_id"`
48-	FromName       string    `db:"from_name"`
49-	FromEmail      string    `db:"from_email"`
50-	Subject        string    `db:"subject"`
51-	Text           string    `db:"text"`
52-	Date           time.Time `db:"date"`
53+	AuthorName     string    `db:"author_name"`
54+	AuthorEmail    string    `db:"author_email"`
55+	Title          string    `db:"title"`
56+	Body           string    `db:"body"`
57+	CommitSha      string    `db:"commit_sha"`
58+	CommitDate     time.Time `db:"commit_date"`
59+	RawText        string    `db:"raw_text"`
60 	CreatedAt      time.Time `db:"created_at"`
61 }
62 
M ssh.go
+1, -1
1@@ -34,7 +34,7 @@ func GitSshServer() {
2 	cfg := NewGitCfg()
3 	logger := slog.Default()
4 	handler := NewUploadHandler(cfg, logger)
5-	dbh, err := Open(":memory", logger)
6+	dbh, err := Open(":memory:", logger)
7 	if err != nil {
8 		panic(err)
9 	}