1package git
2
3import (
4 "context"
5 "fmt"
6 "os"
7 "os/exec"
8 "smoothie/server/middleware"
9
10 "github.com/gliderlabs/ssh"
11)
12
13func Middleware(repoDir string) middleware.Middleware {
14 return func(sh ssh.Handler) ssh.Handler {
15 return func(s ssh.Session) {
16 cmd := s.Command()
17 if len(cmd) == 2 {
18 switch cmd[0] {
19 case "git-upload-pack", "git-receive-pack", "git-upload-archive":
20 r := cmd[1]
21 rp := fmt.Sprintf("%s%s", repoDir, r)
22 ctx := s.Context()
23 err := ensureRepo(ctx, repoDir, r)
24 if err != nil {
25 fatalGit(s, err)
26 break
27 }
28 c := exec.CommandContext(ctx, cmd[0], rp)
29 c.Dir = "./"
30 c.Stdout = s
31 c.Stdin = s
32 err = c.Run()
33 if err != nil {
34 fatalGit(s, err)
35 break
36 }
37 }
38 }
39 sh(s)
40 }
41 }
42}
43
44func fileExists(path string) (bool, error) {
45 _, err := os.Stat(path)
46 if err == nil {
47 return true, nil
48 }
49 if os.IsNotExist(err) {
50 return false, nil
51 }
52 return true, err
53}
54
55func fatalGit(s ssh.Session, err error) {
56 // hex length includes 4 byte length prefix and ending newline
57 msg := err.Error()
58 pktLine := fmt.Sprintf("%04x%s\n", len(msg)+5, msg)
59 _, _ = s.Write([]byte(pktLine))
60 s.Exit(1)
61}
62
63func ensureRepo(ctx context.Context, dir string, repo string) error {
64 exists, err := fileExists(dir)
65 if err != nil {
66 return err
67 }
68 if !exists {
69 err = os.MkdirAll(dir, os.ModeDir|os.FileMode(0700))
70 if err != nil {
71 return err
72 }
73 }
74 rp := fmt.Sprintf("%s%s", dir, repo)
75 exists, err = fileExists(rp)
76 if err != nil {
77 return err
78 }
79 if !exists {
80 c := exec.CommandContext(ctx, "git", "init", "--bare", rp)
81 err = c.Run()
82 if err != nil {
83 return err
84 }
85 }
86 return nil
87}