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