forked from
tangled.org/core
fork
Configure Feed
Select the types of activity you want to include in your feed.
this repo has no description
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 Dir string
19 Stdin io.Reader
20 Stdout http.ResponseWriter
21}
22
23func (c *ServiceCommand) InfoRefs() error {
24 cmd := exec.Command("git", []string{
25 "upload-pack",
26 "--stateless-rpc",
27 "--advertise-refs",
28 ".",
29 }...)
30
31 cmd.Dir = c.Dir
32 cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
33 stdoutPipe, _ := cmd.StdoutPipe()
34 cmd.Stderr = cmd.Stdout
35
36 if err := cmd.Start(); err != nil {
37 log.Printf("git: failed to start git-upload-pack (info/refs): %s", err)
38 return err
39 }
40
41 if err := packLine(c.Stdout, "# service=git-upload-pack\n"); err != nil {
42 log.Printf("git: failed to write pack line: %s", err)
43 return err
44 }
45
46 if err := packFlush(c.Stdout); err != nil {
47 log.Printf("git: failed to flush pack: %s", err)
48 return err
49 }
50
51 buf := bytes.Buffer{}
52 if _, err := io.Copy(&buf, stdoutPipe); err != nil {
53 log.Printf("git: failed to copy stdout to tmp buffer: %s", err)
54 return err
55 }
56
57 if err := cmd.Wait(); err != nil {
58 out := strings.Builder{}
59 _, _ = io.Copy(&out, &buf)
60 log.Printf("git: failed to run git-upload-pack; err: %s; output: %s", err, out.String())
61 return err
62 }
63
64 if _, err := io.Copy(c.Stdout, &buf); err != nil {
65 log.Printf("git: failed to copy stdout: %s", err)
66 }
67
68 return nil
69}
70
71func (c *ServiceCommand) UploadPack() error {
72 var stderr bytes.Buffer
73
74 cmd := exec.Command("git", "-c", "uploadpack.allowFilter=true",
75 "upload-pack", "--stateless-rpc", ".")
76 cmd.Dir = c.Dir
77 cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
78
79 stdoutPipe, err := cmd.StdoutPipe()
80 if err != nil {
81 return fmt.Errorf("failed to create stdout pipe: %w", err)
82 }
83
84 cmd.Stderr = &stderr
85
86 stdinPipe, err := cmd.StdinPipe()
87 if err != nil {
88 return fmt.Errorf("failed to create stdin pipe: %w", err)
89 }
90
91 if err := cmd.Start(); err != nil {
92 return fmt.Errorf("failed to start git-upload-pack: %w", err)
93 }
94
95 var wg sync.WaitGroup
96
97 wg.Add(1)
98 go func() {
99 defer wg.Done()
100 defer stdinPipe.Close()
101 io.Copy(stdinPipe, c.Stdin)
102 }()
103
104 wg.Add(1)
105 go func() {
106 defer wg.Done()
107 io.Copy(newWriteFlusher(c.Stdout), stdoutPipe)
108 stdoutPipe.Close()
109 }()
110
111 wg.Wait()
112
113 if err := cmd.Wait(); err != nil {
114 return fmt.Errorf("git-upload-pack failed: %w, stderr: %s", err, stderr.String())
115 }
116
117 return nil
118}
119
120func packLine(w io.Writer, s string) error {
121 _, err := fmt.Fprintf(w, "%04x%s", len(s)+4, s)
122 return err
123}
124
125func packFlush(w io.Writer) error {
126 _, err := fmt.Fprint(w, "0000")
127 return err
128}