- commit
- d60f2ca
- parent
- 04302e8
- author
- Eric Bower
- date
- 2024-05-02 00:07:40 -0400 EDT
mostly working
+29,
-0
1@@ -0,0 +1,29 @@
2+package git
3+
4+import (
5+ "log/slog"
6+ "path/filepath"
7+
8+ "github.com/charmbracelet/ssh"
9+ gossh "golang.org/x/crypto/ssh"
10+ // ssgit "github.com/charmbracelet/soft-serve/git"
11+ // "github.com/charmbracelet/soft-serve/pkg/utils"
12+)
13+
14+type Backend struct {
15+ Logger *slog.Logger
16+ DB *DB
17+ Cfg *GitCfg
18+}
19+
20+func (be *Backend) ReposDir() string {
21+ return filepath.Join(be.Cfg.DataPath, "repos")
22+}
23+
24+func (be *Backend) RepoName(name string) string {
25+ return name + ".git"
26+}
27+
28+func (be *Backend) Pubkey(pk ssh.PublicKey) string {
29+ return gossh.FingerprintSHA256(pk)
30+}
M
db.go
+13,
-44
1@@ -14,55 +14,28 @@ type DB struct {
2 }
3
4 var schema = `
5-CREATE TABLE IF NOT EXISTS users (
6- id INTEGER PRIMARY KEY AUTOINCREMENT,
7- name TEXT NOT NULL UNIQUE,
8- admin BOOLEAN NOT NULL,
9- public_key TEXT NOT NULL UNIQUE,
10- created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
11- updated_at DATETIME NOT NULL
12-);
13-
14-CREATE TABLE IF NOT EXISTS repos (
15- id INTEGER PRIMARY KEY AUTOINCREMENT,
16- name TEXT NOT NULL UNIQUE,
17- description TEXT NOT NULL UNIQUE,
18- private BOOLEAN NOT NULL,
19- created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
20- updated_at DATETIME NOT NULL
21-);
22-
23 CREATE TABLE IF NOT EXISTS patch_requests (
24 id INTEGER PRIMARY KEY AUTOINCREMENT,
25- user_id INTEGER NOT NULL,
26- repo_id INTEGER NOT NULL,
27+ pubkey TEXT NOT NULL,
28+ repo_id TEXT NOT NULL,
29 name TEXT NOT NULL,
30+ text TEXT NOT NULL,
31 created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
32- updated_at DATETIME NOT NULL,
33- CONSTRAINT user_id_fk
34- FOREIGN KEY(user_id) REFERENCES users(id)
35- ON DELETE CASCADE
36- ON UPDATE CASCADE,
37- CONSTRAINT repo_id_fk
38- FOREIGN KEY(repo_id) REFERENCES repos(id)
39- ON DELETE CASCADE
40- ON UPDATE CASCADE
41+ updated_at DATETIME NOT NULL
42 );
43
44 CREATE TABLE IF NOT EXISTS patches (
45 id INTEGER PRIMARY KEY AUTOINCREMENT,
46- user_id INTEGER NOT NULL,
47+ pubkey TEXT NOT NULL,
48 patch_request_id INTEGER NOT NULL,
49- from_name TEXT NOT NULL,
50- from_email TEXT NOT NULL,
51- subject TEXT NOT NULL,
52- text TEXT NOT NULL,
53- date DATETIME NOT NULL,
54+ author_name TEXT NOT NULL,
55+ author_email TEXT NOT NULL,
56+ title TEXT NOT NULL,
57+ body TEXT NOT NULL,
58+ commit_sha TEXT NOT NULL,
59+ commit_date DATETIME NOT NULL,
60+ raw_text TEXT NOT NULL,
61 created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
62- CONSTRAINT user_id_fk
63- FOREIGN KEY(user_id) REFERENCES users(id)
64- ON DELETE CASCADE
65- ON UPDATE CASCADE,
66 CONSTRAINT pr_id_fk
67 FOREIGN KEY(patch_request_id) REFERENCES patch_requests(id)
68 ON DELETE CASCADE
69@@ -71,15 +44,11 @@ CREATE TABLE IF NOT EXISTS patches (
70
71 CREATE TABLE IF NOT EXISTS comments (
72 id INTEGER PRIMARY KEY AUTOINCREMENT,
73- user_id INTEGER NOT NULL,
74+ pubkey TEXT NOT NULL,
75 patch_request_id INTEGER NOT NULL,
76 text TEXT NOT NULL,
77 created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
78 updated_at DATETIME NOT NULL,
79- CONSTRAINT user_id_fk
80- FOREIGN KEY(user_id) REFERENCES users(id)
81- ON DELETE CASCADE
82- ON UPDATE CASCADE,
83 CONSTRAINT pr_id_fk
84 FOREIGN KEY(patch_request_id) REFERENCES patch_requests(id)
85 ON DELETE CASCADE
M
go.mod
+1,
-0
1@@ -15,6 +15,7 @@ require (
2 github.com/antoniomika/go-rsync-receiver v0.0.0-20231110145728-c94949e1ab7d // indirect
3 github.com/aymanbagabas/git-module v1.8.4-0.20231101154130-8d27204ac6d2 // indirect
4 github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
5+ github.com/bluekeyes/go-gitdiff v0.7.2 // indirect
6 github.com/caarlos0/env/v10 v10.0.0 // indirect
7 github.com/charmbracelet/bubbletea v0.25.0 // indirect
8 github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20231027181609-f7ff6baf2ed0 // indirect
M
go.sum
+2,
-0
1@@ -8,6 +8,8 @@ github.com/aymanbagabas/git-module v1.8.4-0.20231101154130-8d27204ac6d2 h1:3w5KT
2 github.com/aymanbagabas/git-module v1.8.4-0.20231101154130-8d27204ac6d2/go.mod h1:d4gQ7/3/S2sPq4NnKdtAgUOVr6XtLpWFtxyVV5/+76U=
3 github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
4 github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
5+github.com/bluekeyes/go-gitdiff v0.7.2 h1:42jrcVZdjjxXtVsFNYTo/I6T1ZvIiQL+iDDLiH904hw=
6+github.com/bluekeyes/go-gitdiff v0.7.2/go.mod h1:QpfYYO1E0fTVHVZAZKiRjtSGY9823iCdvGXBcEzHGbM=
7 github.com/caarlos0/env/v10 v10.0.0 h1:yIHUBZGsyqCnpTkbjk8asUlx6RFhhEs+h7TOBdgdzXA=
8 github.com/caarlos0/env/v10 v10.0.0/go.mod h1:ZfulV76NvVPw3tm591U4SwL3Xx9ldzBP9aGxzeN7G18=
9 github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
M
mdw.go
+72,
-31
1@@ -1,27 +1,30 @@
2 package git
3
4 import (
5+ "bytes"
6 "fmt"
7+ "io"
8 "path/filepath"
9+ "time"
10
11- ssgit "github.com/charmbracelet/soft-serve/git"
12+ "github.com/bluekeyes/go-gitdiff/gitdiff"
13 "github.com/charmbracelet/soft-serve/pkg/git"
14 "github.com/charmbracelet/soft-serve/pkg/utils"
15 "github.com/charmbracelet/ssh"
16 "github.com/charmbracelet/wish"
17 )
18
19-func gitServiceCommands(sesh ssh.Session, cfg *GitCfg, cmd, repo string) error {
20+func gitServiceCommands(sesh ssh.Session, be *Backend, cmd, repo string) error {
21 name := utils.SanitizeRepo(repo)
22 // git bare repositories should end in ".git"
23 // https://git-scm.com/docs/gitrepository-layout
24- repoDir := name + ".git"
25- reposDir := filepath.Join(cfg.DataPath, "repos")
26- err := git.EnsureWithin(reposDir, repoDir)
27+ repoName := name + ".git"
28+ reposDir := be.ReposDir()
29+ err := git.EnsureWithin(reposDir, repoName)
30 if err != nil {
31 return err
32 }
33- repoPath := filepath.Join(reposDir, repoDir)
34+ repoPath := filepath.Join(reposDir, repoName)
35 serviceCmd := git.ServiceCommand{
36 Stdin: sesh,
37 Stdout: sesh,
38@@ -45,45 +48,83 @@ func gitServiceCommands(sesh ssh.Session, cfg *GitCfg, cmd, repo string) error {
39 return nil
40 }
41
42-func createRepo(cfg *GitCfg, rawName string) (*Repo, error) {
43- name := utils.SanitizeRepo(rawName)
44- if err := utils.ValidateRepo(name); err != nil {
45- return nil, err
46- }
47- reposDir := filepath.Join(cfg.DataPath, "repos")
48-
49- repo := name + ".git"
50- rp := filepath.Join(reposDir, repo)
51- _, err := ssgit.Init(rp, true)
52+func try(sesh ssh.Session, err error) {
53 if err != nil {
54- return nil, err
55+ wish.Fatal(sesh, err)
56 }
57 }
58
59-func GitServerMiddleware(cfg *GitCfg, dbh *DB) wish.Middleware {
60+func GitServerMiddleware(be *Backend) wish.Middleware {
61 return func(next ssh.Handler) ssh.Handler {
62 return func(sesh ssh.Session) {
63+ pubkey := be.Pubkey(sesh.PublicKey())
64 args := sesh.Command()
65 cmd := args[0]
66- fmt.Println(cmd)
67
68 if cmd == "git-receive-pack" || cmd == "git-upload-pack" {
69 repoName := args[1]
70- err := gitServiceCommands(sesh, cfg, cmd, repoName)
71- if err != nil {
72- wish.Fatal(sesh, err.Error())
73- return
74- }
75+ err := gitServiceCommands(sesh, be, cmd, repoName)
76+ try(sesh, err)
77 } else if cmd == "help" {
78 wish.Println(sesh, "commands: [help, git-receive-pack, git-upload-pack]")
79 } else if cmd == "pr" {
80- repoName := args[1]
81- fmt.Println(repoName)
82- // dbpool.GetRepoByName(repoName)
83- // pr, err := dbpool.InsertPatchRequest(userID, repoID, name)
84- // dbpool.InsertPatches(userID, pr.ID, patches)
85- // id := fmt.Sprintf("%s/%s", repoName, pr.ID)
86- // wish.Printf("Patch Request ID: %s", id)
87+ if len(args) < 2 {
88+ wish.Fatal(sesh, "must provide repo name")
89+ return
90+ }
91+ repoName := utils.SanitizeRepo(args[1])
92+ err := git.EnsureWithin(be.ReposDir(), be.RepoName(repoName))
93+ try(sesh, err)
94+
95+ // need to read io.Reader from session twice
96+ var buf bytes.Buffer
97+ tee := io.TeeReader(sesh, &buf)
98+
99+ _, preamble, err := gitdiff.Parse(tee)
100+ try(sesh, err)
101+ header, err := gitdiff.ParsePatchHeader(preamble)
102+ try(sesh, err)
103+ prName := header.Title
104+ prDesc := header.Body
105+
106+ var prID int64
107+ row := be.DB.QueryRow(
108+ "INSERT INTO patch_requests (pubkey, repo_id, name, text, updated_at) VALUES(?, ?, ?, ?, ?) RETURNING id",
109+ pubkey,
110+ repoName,
111+ prName,
112+ prDesc,
113+ time.Now(),
114+ )
115+ row.Scan(&prID)
116+ if prID == 0 {
117+ wish.Fatal(sesh, "could not create patch request")
118+ return
119+ }
120+ try(sesh, err)
121+
122+ _, err = be.DB.Exec(
123+ "INSERT INTO patches (pubkey, patch_request_id, author_name, author_email, title, body, commit_sha, commit_date, raw_text) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
124+ pubkey,
125+ prID,
126+ header.Author.Name,
127+ header.Author.Email,
128+ header.Title,
129+ header.Body,
130+ header.SHA,
131+ header.CommitterDate,
132+ buf.String(),
133+ )
134+ try(sesh, err)
135+
136+ wish.Printf(
137+ sesh,
138+ "Create Patch Request!\nID: %d\nTitle: %s\n",
139+ prID,
140+ prName,
141+ )
142+
143+ return
144 } else {
145 fmt.Println("made it here")
146 next(sesh)
M
ssh.go
+7,
-2
1@@ -34,11 +34,16 @@ 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 }
10 dbh.Migrate()
11+ be := &Backend{
12+ DB: dbh,
13+ Logger: logger,
14+ Cfg: cfg,
15+ }
16
17 s, err := wish.NewServer(
18 wish.WithAddress(
19@@ -52,7 +57,7 @@ func GitSshServer() {
20 wish.WithMiddleware(
21 scp.Middleware(handler),
22 wishrsync.Middleware(handler),
23- GitServerMiddleware(cfg, dbh),
24+ GitServerMiddleware(be),
25 ),
26 )
27