121 lines
2.6 kB
1
package service
2
3
import (
4
"bytes"
5
"fmt"
6
"io"
7
"log"
8
"net/http"
9
"os/exec"
10
"strings"
11
"syscall"
12
)
13
14
// Mostly from charmbracelet/soft-serve and sosedoff/gitkit.
15
16
type ServiceCommand struct {
17
Dir string
18
Stdin io.Reader
19
Stdout http.ResponseWriter
20
}
21
22
func (c *ServiceCommand) InfoRefs() error {
23
cmd := exec.Command("git", []string{
24
"upload-pack",
25
"--stateless-rpc",
26
"--advertise-refs",
27
".",
28
}...)
29
30
cmd.Dir = c.Dir
31
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
32
stdoutPipe, _ := cmd.StdoutPipe()
33
cmd.Stderr = cmd.Stdout
34
35
if err := cmd.Start(); err != nil {
36
log.Printf("git: failed to start git-upload-pack (info/refs): %s", err)
37
return err
38
}
39
40
if err := packLine(c.Stdout, "# service=git-upload-pack\n"); err != nil {
41
log.Printf("git: failed to write pack line: %s", err)
42
return err
43
}
44
45
if err := packFlush(c.Stdout); err != nil {
46
log.Printf("git: failed to flush pack: %s", err)
47
return err
48
}
49
50
buf := bytes.Buffer{}
51
if _, err := io.Copy(&buf, stdoutPipe); err != nil {
52
log.Printf("git: failed to copy stdout to tmp buffer: %s", err)
53
return err
54
}
55
56
if err := cmd.Wait(); err != nil {
57
out := strings.Builder{}
58
_, _ = io.Copy(&out, &buf)
59
log.Printf("git: failed to run git-upload-pack; err: %s; output: %s", err, out.String())
60
return err
61
}
62
63
if _, err := io.Copy(c.Stdout, &buf); err != nil {
64
log.Printf("git: failed to copy stdout: %s", err)
65
}
66
67
return nil
68
}
69
70
func (c *ServiceCommand) UploadPack() error {
71
cmd := exec.Command("git", []string{
72
"-c", "uploadpack.allowFilter=true",
73
"upload-pack",
74
"--stateless-rpc",
75
".",
76
}...)
77
cmd.Dir = c.Dir
78
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
79
80
stdoutPipe, _ := cmd.StdoutPipe()
81
cmd.Stderr = cmd.Stdout
82
defer stdoutPipe.Close()
83
84
stdinPipe, err := cmd.StdinPipe()
85
if err != nil {
86
return err
87
}
88
defer stdinPipe.Close()
89
90
if err := cmd.Start(); err != nil {
91
log.Printf("git: failed to start git-upload-pack: %s", err)
92
return err
93
}
94
95
if _, err := io.Copy(stdinPipe, c.Stdin); err != nil {
96
log.Printf("git: failed to copy stdin: %s", err)
97
return err
98
}
99
stdinPipe.Close()
100
101
if _, err := io.Copy(newWriteFlusher(c.Stdout), stdoutPipe); err != nil {
102
log.Printf("git: failed to copy stdout: %s", err)
103
return err
104
}
105
if err := cmd.Wait(); err != nil {
106
log.Printf("git: failed to wait for git-upload-pack: %s", err)
107
return err
108
}
109
110
return nil
111
}
112
113
func packLine(w io.Writer, s string) error {
114
_, err := fmt.Fprintf(w, "%04x%s", len(s)+4, s)
115
return err
116
}
117
118
func packFlush(w io.Writer) error {
119
_, err := fmt.Fprint(w, "0000")
120
return err
121
}
122