repos / git-pr

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

commit
605c47b
parent
31fcd4a
author
jolheiser
date
2024-07-22 14:05:39 -0400 EDT
contrib: add dev script

This patch adds a fairly contained dev script that almost doubles as an integration test if you squint hard enough.
It runs an isolated instance that we can add more things to, it at least seems handy to me for quickly testing how changes affect e.g. the web UI, RSS feed, etc.

Signed-off-by: jolheiser <git@jolheiser.com>
3 files changed,  +221, -0
A contrib/dev/README.md
+12, -0
 1@@ -0,0 +1,12 @@
 2+# dev script
 3+
 4+`go run ./contrib/dev/`
 5+
 6+If you want to instead use this to bootstrap:
 7+
 8+1. `go run ./contrib/dev/ --cleanup=false`
 9+2. Note the tmp dir printed out
10+3. Stop the program `Ctrl+C`
11+4. Modify as needed within the tmp dir
12+5. Run `git-dir` and point at the config contained within the tmp dir
13+6. Remember to clean up the tmp dir yourself when finished
A contrib/dev/main.go
+203, -0
  1@@ -0,0 +1,203 @@
  2+package main
  3+
  4+import (
  5+	"crypto/ed25519"
  6+	"crypto/rand"
  7+	"flag"
  8+	"fmt"
  9+	"log/slog"
 10+	"os"
 11+	"os/signal"
 12+	"path/filepath"
 13+	"time"
 14+
 15+	"github.com/picosh/git-pr"
 16+	"github.com/picosh/git-pr/fixtures"
 17+	"golang.org/x/crypto/ssh"
 18+)
 19+
 20+func main() {
 21+	cleanupFlag := flag.Bool("cleanup", true, "Clean up tmp dir after quitting (default: true)")
 22+	flag.Parse()
 23+
 24+	tmp, err := os.MkdirTemp(os.TempDir(), "git-pr*")
 25+	if err != nil {
 26+		panic(err)
 27+	}
 28+	defer func() {
 29+		if *cleanupFlag {
 30+			os.RemoveAll(tmp)
 31+		}
 32+	}()
 33+	fmt.Println(tmp)
 34+
 35+	adminKey, userKey := generateKeys()
 36+
 37+	cfgPath := filepath.Join(tmp, "git-pr.toml")
 38+	cfgFi, err := os.Create(cfgPath)
 39+	if err != nil {
 40+		panic(err)
 41+	}
 42+	cfgFi.WriteString(fmt.Sprintf(cfgTmpl, tmp, adminKey.public()))
 43+	cfgFi.Close()
 44+
 45+	opts := &slog.HandlerOptions{
 46+		AddSource: true,
 47+	}
 48+	logger := slog.New(
 49+		slog.NewTextHandler(os.Stdout, opts),
 50+	)
 51+	cfg := git.NewGitCfg(cfgPath, logger)
 52+	go git.GitSshServer(cfg)
 53+	time.Sleep(time.Second)
 54+	go git.StartWebServer(cfg)
 55+
 56+	// Hack to wait for startup
 57+	time.Sleep(time.Second)
 58+
 59+	patch, err := fixtures.Fixtures.ReadFile("single.patch")
 60+	if err != nil {
 61+		panic(err)
 62+	}
 63+	otherPatch, err := fixtures.Fixtures.ReadFile("with-cover.patch")
 64+	if err != nil {
 65+		panic(err)
 66+	}
 67+
 68+	// Accepted patch
 69+	userKey.cmd(patch, "pr create test")
 70+	userKey.cmd(nil, "pr edit 1 Accepted patch")
 71+	adminKey.cmd(nil, "pr accept 1")
 72+
 73+	// Closed patch (admin)
 74+	userKey.cmd(patch, "pr create test")
 75+	userKey.cmd(nil, "pr edit 2 Closed patch (admin)")
 76+	adminKey.cmd(nil, "pr close 2")
 77+
 78+	// Closed patch (contributor)
 79+	userKey.cmd(patch, "pr create test")
 80+	userKey.cmd(nil, "pr edit 3 Closed patch (contributor)")
 81+	userKey.cmd(nil, "pr close 3")
 82+
 83+	// Reviewed patch
 84+	userKey.cmd(patch, "pr create test")
 85+	userKey.cmd(nil, "pr edit 4 Reviewed patch")
 86+	adminKey.cmd(otherPatch, "pr add --review 4")
 87+
 88+	// Accepted patch with review
 89+	userKey.cmd(patch, "pr create test")
 90+	userKey.cmd(nil, "pr edit 5 Accepted patch with review")
 91+	adminKey.cmd(otherPatch, "pr add --accept 5")
 92+
 93+	// Closed patch with review
 94+	userKey.cmd(patch, "pr create test")
 95+	userKey.cmd(nil, "pr edit 6 Closed patch with review")
 96+	adminKey.cmd(otherPatch, "pr add --close 6")
 97+
 98+	fmt.Println("time to do some testing...")
 99+	ch := make(chan os.Signal, 1)
100+	signal.Notify(ch, os.Interrupt, os.Kill)
101+	<-ch
102+}
103+
104+type sshKey struct {
105+	username string
106+	signer   ssh.Signer
107+}
108+
109+func (s sshKey) public() string {
110+	pubkey := s.signer.PublicKey()
111+	return string(ssh.MarshalAuthorizedKey(pubkey))
112+}
113+
114+func (s sshKey) cmd(patch []byte, cmd string) {
115+	host := "localhost:2222"
116+
117+	config := &ssh.ClientConfig{
118+		User: s.username,
119+		Auth: []ssh.AuthMethod{
120+			ssh.PublicKeys(s.signer),
121+		},
122+		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
123+	}
124+
125+	client, err := ssh.Dial("tcp", host, config)
126+	if err != nil {
127+		panic(err)
128+	}
129+	defer client.Close()
130+
131+	session, err := client.NewSession()
132+	if err != nil {
133+		panic(err)
134+	}
135+	defer session.Close()
136+
137+	stdinPipe, err := session.StdinPipe()
138+	if err != nil {
139+		panic(err)
140+	}
141+
142+	if err := session.Start(cmd); err != nil {
143+		panic(err)
144+	}
145+
146+	if patch != nil {
147+		_, err = stdinPipe.Write(patch)
148+		if err != nil {
149+			panic(err)
150+		}
151+	}
152+
153+	stdinPipe.Close()
154+
155+	if err := session.Wait(); err != nil {
156+		panic(err)
157+	}
158+}
159+
160+func generateKeys() (sshKey, sshKey) {
161+	_, adminKey, err := ed25519.GenerateKey(rand.Reader)
162+	if err != nil {
163+		panic(err)
164+	}
165+
166+	adminSigner, err := ssh.NewSignerFromKey(adminKey)
167+	if err != nil {
168+		panic(err)
169+	}
170+
171+	_, userKey, err := ed25519.GenerateKey(rand.Reader)
172+	if err != nil {
173+		panic(err)
174+	}
175+
176+	userSigner, err := ssh.NewSignerFromKey(userKey)
177+	if err != nil {
178+		panic(err)
179+	}
180+
181+	return sshKey{
182+			username: "admin",
183+			signer:   adminSigner,
184+		}, sshKey{
185+			username: "contributor",
186+			signer:   userSigner,
187+		}
188+}
189+
190+// args: tmpdir, adminKey
191+var cfgTmpl = `# url is used for help commands, exclude protocol
192+url = "localhost"
193+# where we store the sqlite db, this toml file, git repos, and ssh host keys
194+data_dir = %q
195+# this gives users the ability to submit reviews and other admin permissions
196+admins = [%q]
197+# set datetime format for our clients
198+time_format = "01/02/2006 15:04:05 07:00"
199+
200+# add as many repos as you want
201+[[repo]]
202+id = "test"
203+clone_addr = "https://github.com/picosh/test.git"
204+desc = "Test repo"`
A fixtures/fixtures.go
+6, -0
1@@ -0,0 +1,6 @@
2+package fixtures
3+
4+import "embed"
5+
6+//go:embed *
7+var Fixtures embed.FS