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