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