diff options
author | Anirudh Oppiliappan <x@icyphox.sh> | 2024-07-13 20:06:37 +0300 |
---|---|---|
committer | Anirudh Oppiliappan <x@icyphox.sh> | 2024-07-13 20:08:54 +0300 |
commit | fba146ac6867b13c40802c4d7a21a8a32571473c (patch) | |
tree | 90fe936d2ed43b97c531e08f5189fccf3ef625cd /git/service | |
parent | deps: bump golang.org/x/net from 0.22.0 to 0.23.0 (#40) (diff) | |
download | legitrx-fba146ac6867b13c40802c4d7a21a8a32571473c.tar.gz legitrx-fba146ac6867b13c40802c4d7a21a8a32571473c.tar.zst legitrx-fba146ac6867b13c40802c4d7a21a8a32571473c.zip |
git: use system's git upload-pack
This is an intermediate workaround for
https://github.com/go-git/go-git/issues/1062. This should also fix #33.
Diffstat (limited to 'git/service')
-rw-r--r-- | git/service/service.go | 121 | ||||
-rw-r--r-- | git/service/write_flusher.go | 25 |
2 files changed, 146 insertions, 0 deletions
diff --git a/git/service/service.go b/git/service/service.go new file mode 100644 index 0000000..d2c7fdd --- /dev/null +++ b/git/service/service.go @@ -0,0 +1,121 @@ +package service + +import ( + "bytes" + "fmt" + "io" + "log" + "net/http" + "os/exec" + "strings" + "syscall" +) + +// Mostly from charmbracelet/soft-serve and sosedoff/gitkit. + +type ServiceCommand struct { + Dir string + Stdin io.Reader + Stdout http.ResponseWriter +} + +func (c *ServiceCommand) InfoRefs() error { + cmd := exec.Command("git", []string{ + "upload-pack", + "--stateless-rpc", + "--advertise-refs", + ".", + }...) + + cmd.Dir = c.Dir + cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + stdoutPipe, _ := cmd.StdoutPipe() + cmd.Stderr = cmd.Stdout + + if err := cmd.Start(); err != nil { + log.Printf("git: failed to start git-upload-pack (info/refs): %s", err) + return err + } + + if err := packLine(c.Stdout, "# service=git-upload-pack\n"); err != nil { + log.Printf("git: failed to write pack line: %s", err) + return err + } + + if err := packFlush(c.Stdout); err != nil { + log.Printf("git: failed to flush pack: %s", err) + return err + } + + buf := bytes.Buffer{} + if _, err := io.Copy(&buf, stdoutPipe); err != nil { + log.Printf("git: failed to copy stdout to tmp buffer: %s", err) + return err + } + + if err := cmd.Wait(); err != nil { + out := strings.Builder{} + _, _ = io.Copy(&out, &buf) + log.Printf("git: failed to run git-upload-pack; err: %s; output: %s", err, out.String()) + return err + } + + if _, err := io.Copy(c.Stdout, &buf); err != nil { + log.Printf("git: failed to copy stdout: %s", err) + } + + return nil +} + +func (c *ServiceCommand) UploadPack() error { + cmd := exec.Command("git", []string{ + "-c", "uploadpack.allowFilter=true", + "upload-pack", + "--stateless-rpc", + ".", + }...) + cmd.Dir = c.Dir + cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + + stdoutPipe, _ := cmd.StdoutPipe() + cmd.Stderr = cmd.Stdout + defer stdoutPipe.Close() + + stdinPipe, err := cmd.StdinPipe() + if err != nil { + return err + } + defer stdinPipe.Close() + + if err := cmd.Start(); err != nil { + log.Printf("git: failed to start git-upload-pack: %s", err) + return err + } + + if _, err := io.Copy(stdinPipe, c.Stdin); err != nil { + log.Printf("git: failed to copy stdin: %s", err) + return err + } + stdinPipe.Close() + + if _, err := io.Copy(newWriteFlusher(c.Stdout), stdoutPipe); err != nil { + log.Printf("git: failed to copy stdout: %s", err) + return err + } + if err := cmd.Wait(); err != nil { + log.Printf("git: failed to wait for git-upload-pack: %s", err) + return err + } + + return nil +} + +func packLine(w io.Writer, s string) error { + _, err := fmt.Fprintf(w, "%04x%s", len(s)+4, s) + return err +} + +func packFlush(w io.Writer) error { + _, err := fmt.Fprint(w, "0000") + return err +} diff --git a/git/service/write_flusher.go b/git/service/write_flusher.go new file mode 100644 index 0000000..f25a5ae --- /dev/null +++ b/git/service/write_flusher.go @@ -0,0 +1,25 @@ +package service + +import ( + "io" + "net/http" +) + +func newWriteFlusher(w http.ResponseWriter) io.Writer { + return writeFlusher{w.(interface { + io.Writer + http.Flusher + })} +} + +type writeFlusher struct { + wf interface { + io.Writer + http.Flusher + } +} + +func (w writeFlusher) Write(p []byte) (int, error) { + defer w.wf.Flush() + return w.wf.Write(p) +} |