repos / git-pr

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

commit
04302e8
parent
95ace53
author
Eric Bower
date
2024-05-01 16:03:05 -0400 EDT
changes
4 files changed,  +195, -53
M db.go
M mdw.go
M ssh.go
M db.go
+100, -51
  1@@ -1,65 +1,114 @@
  2 package git
  3 
  4 import (
  5-	"database/sql"
  6-	"time"
  7+	"log/slog"
  8+
  9+	"github.com/jmoiron/sqlx"
 10+	_ "modernc.org/sqlite" // sqlite driver
 11 )
 12 
 13-// User is the entity repesenting a pubkey authenticated user
 14-// A user and a single ssh key-pair are synonymous in this context
 15-type User struct {
 16-	ID        int64     `db:"id"`
 17-	Name      string    `db:"name"`
 18-	Pubkey    string    `db:"pubkey"`
 19-	CreatedAt time.Time `db:"created_at"`
 20-	UpdatedAt time.Time `db:"updated_at"`
 21+// DB is the interface for a pico/git database.
 22+type DB struct {
 23+	*sqlx.DB
 24+	logger *slog.Logger
 25 }
 26 
 27-// PatchRequest is a database model for patches submitted to a Repo
 28-type PatchRequest struct {
 29-	ID        int64     `db:"id"`
 30-	UserID    int64     `db:"user_id"`
 31-	RepoID    int64     `db:"repo_id"`
 32-	Name      string    `db:"name"`
 33-	CreatedAt time.Time `db:"created_at"`
 34-	UpdatedAt time.Time `db:"updated_at"`
 35-}
 36+var schema = `
 37+CREATE TABLE IF NOT EXISTS users (
 38+  id INTEGER PRIMARY KEY AUTOINCREMENT,
 39+  name TEXT NOT NULL UNIQUE,
 40+  admin BOOLEAN NOT NULL,
 41+  public_key TEXT NOT NULL UNIQUE,
 42+  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
 43+  updated_at DATETIME NOT NULL
 44+);
 45 
 46-// Patch is a database model for a single entry in a patchset
 47-// This usually corresponds to a git commit.
 48-type Patch struct {
 49-	ID             int64     `db:"id"`
 50-	UserID         int64     `db:"user_id"`
 51-	PatchRequestID int64     `db:"patch_request_id"`
 52-	FromName       string    `db:"from_name"`
 53-	FromEmail      string    `db:"from_email"`
 54-	Subject        string    `db:"subject"`
 55-	Text           string    `db:"text"`
 56-	Date           time.Time `db:"date"`
 57-	CreatedAt      time.Time `db:"created_at"`
 58-}
 59+CREATE TABLE IF NOT EXISTS repos (
 60+  id INTEGER PRIMARY KEY AUTOINCREMENT,
 61+  name TEXT NOT NULL UNIQUE,
 62+  description TEXT NOT NULL UNIQUE,
 63+  private BOOLEAN NOT NULL,
 64+  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
 65+  updated_at DATETIME NOT NULL
 66+);
 67+
 68+CREATE TABLE IF NOT EXISTS patch_requests (
 69+  id INTEGER PRIMARY KEY AUTOINCREMENT,
 70+  user_id INTEGER NOT NULL,
 71+  repo_id INTEGER NOT NULL,
 72+  name TEXT NOT NULL,
 73+  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
 74+  updated_at DATETIME NOT NULL,
 75+  CONSTRAINT user_id_fk
 76+  FOREIGN KEY(user_id) REFERENCES users(id)
 77+  ON DELETE CASCADE
 78+  ON UPDATE CASCADE,
 79+  CONSTRAINT repo_id_fk
 80+  FOREIGN KEY(repo_id) REFERENCES repos(id)
 81+  ON DELETE CASCADE
 82+  ON UPDATE CASCADE
 83+);
 84+
 85+CREATE TABLE IF NOT EXISTS patches (
 86+  id INTEGER PRIMARY KEY AUTOINCREMENT,
 87+  user_id INTEGER NOT NULL,
 88+  patch_request_id INTEGER NOT NULL,
 89+  from_name TEXT NOT NULL,
 90+  from_email TEXT NOT NULL,
 91+  subject TEXT NOT NULL,
 92+  text TEXT NOT NULL,
 93+  date DATETIME NOT NULL,
 94+  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
 95+  CONSTRAINT user_id_fk
 96+  FOREIGN KEY(user_id) REFERENCES users(id)
 97+  ON DELETE CASCADE
 98+  ON UPDATE CASCADE,
 99+  CONSTRAINT pr_id_fk
100+  FOREIGN KEY(patch_request_id) REFERENCES patch_requests(id)
101+  ON DELETE CASCADE
102+  ON UPDATE CASCADE
103+);
104+
105+CREATE TABLE IF NOT EXISTS comments (
106+  id INTEGER PRIMARY KEY AUTOINCREMENT,
107+  user_id INTEGER NOT NULL,
108+  patch_request_id INTEGER NOT NULL,
109+  text TEXT NOT NULL,
110+  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
111+  updated_at DATETIME NOT NULL,
112+  CONSTRAINT user_id_fk
113+  FOREIGN KEY(user_id) REFERENCES users(id)
114+  ON DELETE CASCADE
115+  ON UPDATE CASCADE,
116+  CONSTRAINT pr_id_fk
117+  FOREIGN KEY(patch_request_id) REFERENCES patch_requests(id)
118+  ON DELETE CASCADE
119+  ON UPDATE CASCADE
120+);
121+`
122+
123+// Open opens a database connection.
124+func Open(dsn string, logger *slog.Logger) (*DB, error) {
125+	db, err := sqlx.Connect("sqlite", dsn)
126+	if err != nil {
127+		return nil, err
128+	}
129+
130+	d := &DB{
131+		DB:     db,
132+		logger: logger,
133+	}
134 
135-// Comment is a database model for a non-patch comment within a PatchRequest
136-type Comment struct {
137-	ID             int64     `db:"id"`
138-	UserID         int64     `db:"user_id"`
139-	PatchRequestID int64     `db:"patch_request_id"`
140-	Text           string    `db:"text"`
141-	CreatedAt      time.Time `db:"created_at"`
142-	UpdatedAt      time.Time `db:"updated_at"`
143+	return d, nil
144 }
145 
146-// Repo is a database model for a repository.
147-type Repo struct {
148-	ID          int64         `db:"id"`
149-	Name        string        `db:"name"`
150-	ProjectName string        `db:"project_name"`
151-	Description string        `db:"description"`
152-	Private     bool          `db:"private"`
153-	UserID      sql.NullInt64 `db:"user_id"`
154-	CreatedAt   time.Time     `db:"created_at"`
155-	UpdatedAt   time.Time     `db:"updated_at"`
156+func (d *DB) Migrate() {
157+	// exec the schema or fail; multi-statement Exec behavior varies between
158+	// database drivers;  pq will exec them all, sqlite3 won't, ymmv
159+	d.DB.MustExec(schema)
160 }
161 
162-type GitDB interface {
163+// Close implements db.DB.
164+func (d *DB) Close() error {
165+	return d.DB.Close()
166 }
M mdw.go
+25, -1
 1@@ -4,6 +4,7 @@ import (
 2 	"fmt"
 3 	"path/filepath"
 4 
 5+	ssgit "github.com/charmbracelet/soft-serve/git"
 6 	"github.com/charmbracelet/soft-serve/pkg/git"
 7 	"github.com/charmbracelet/soft-serve/pkg/utils"
 8 	"github.com/charmbracelet/ssh"
 9@@ -44,7 +45,22 @@ func gitServiceCommands(sesh ssh.Session, cfg *GitCfg, cmd, repo string) error {
10 	return nil
11 }
12 
13-func GitServerMiddleware(cfg *GitCfg) wish.Middleware {
14+func createRepo(cfg *GitCfg, rawName string) (*Repo, error) {
15+	name := utils.SanitizeRepo(rawName)
16+	if err := utils.ValidateRepo(name); err != nil {
17+		return nil, err
18+	}
19+	reposDir := filepath.Join(cfg.DataPath, "repos")
20+
21+	repo := name + ".git"
22+	rp := filepath.Join(reposDir, repo)
23+	_, err := ssgit.Init(rp, true)
24+	if err != nil {
25+		return nil, err
26+	}
27+}
28+
29+func GitServerMiddleware(cfg *GitCfg, dbh *DB) wish.Middleware {
30 	return func(next ssh.Handler) ssh.Handler {
31 		return func(sesh ssh.Session) {
32 			args := sesh.Command()
33@@ -60,6 +76,14 @@ func GitServerMiddleware(cfg *GitCfg) wish.Middleware {
34 				}
35 			} else if cmd == "help" {
36 				wish.Println(sesh, "commands: [help, git-receive-pack, git-upload-pack]")
37+			} else if cmd == "pr" {
38+				repoName := args[1]
39+				fmt.Println(repoName)
40+				// dbpool.GetRepoByName(repoName)
41+				// pr, err := dbpool.InsertPatchRequest(userID, repoID, name)
42+				// dbpool.InsertPatches(userID, pr.ID, patches)
43+				// id := fmt.Sprintf("%s/%s", repoName, pr.ID)
44+				// wish.Printf("Patch Request ID: %s", id)
45 			} else {
46 				fmt.Println("made it here")
47 				next(sesh)
A models.go
+64, -0
 1@@ -0,0 +1,64 @@
 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+	RepoID    int64     `db:"repo_id"`
35+	Name      string    `db:"name"`
36+	CreatedAt time.Time `db:"created_at"`
37+	UpdatedAt time.Time `db:"updated_at"`
38+}
39+
40+// Patch is a database model for a single entry in a patchset
41+// This usually corresponds to a git commit.
42+type Patch struct {
43+	ID             int64     `db:"id"`
44+	UserID         int64     `db:"user_id"`
45+	PatchRequestID int64     `db:"patch_request_id"`
46+	FromName       string    `db:"from_name"`
47+	FromEmail      string    `db:"from_email"`
48+	Subject        string    `db:"subject"`
49+	Text           string    `db:"text"`
50+	Date           time.Time `db:"date"`
51+	CreatedAt      time.Time `db:"created_at"`
52+}
53+
54+// Comment is a database model for a non-patch comment within a PatchRequest
55+type Comment struct {
56+	ID             int64     `db:"id"`
57+	UserID         int64     `db:"user_id"`
58+	PatchRequestID int64     `db:"patch_request_id"`
59+	Text           string    `db:"text"`
60+	CreatedAt      time.Time `db:"created_at"`
61+	UpdatedAt      time.Time `db:"updated_at"`
62+}
63+
64+type GitDB interface {
65+}
M ssh.go
+6, -1
 1@@ -34,6 +34,11 @@ func GitSshServer() {
 2 	cfg := NewGitCfg()
 3 	logger := slog.Default()
 4 	handler := NewUploadHandler(cfg, logger)
 5+	dbh, err := Open(":memory:", logger)
 6+	if err != nil {
 7+		panic(err)
 8+	}
 9+	dbh.Migrate()
10 
11 	s, err := wish.NewServer(
12 		wish.WithAddress(
13@@ -47,7 +52,7 @@ func GitSshServer() {
14 		wish.WithMiddleware(
15 			scp.Middleware(handler),
16 			wishrsync.Middleware(handler),
17-			GitServerMiddleware(cfg),
18+			GitServerMiddleware(cfg, dbh),
19 		),
20 	)
21