git.go

 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}