forked from tangled.org/core
this repo has no description
at master 2.4 kB view raw
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}