bidet / internal / core / uploadpack.go

@ c4520ece0b4f0765894e05bd889c3a893b93a189 | history


package core

import (
	"io"
	"log"
	"net/http"
	"os/exec"
	"syscall"
)

func UploadPack(repoLocation string, r io.Reader, w http.ResponseWriter) error {
	cmd := exec.Command(
		"git",
		"-c", "uploadpack.allowFilter=true",
		"upload-pack",
		"--stateless-rpc",
		".",
	)
	cmd.Dir = repoLocation
	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, r); err != nil {
		log.Printf("git: failed to copy stdin: %s", err)
		return err
	}
	stdinPipe.Close()
	if _, err := io.Copy(flushWriter{w}, 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
}

type flushWriter struct {
	http.ResponseWriter
}

func (w flushWriter) Write(p []byte) (n int, err error) {
	if f, ok := w.ResponseWriter.(http.Flusher); ok {
		defer f.Flush()
	}
	return w.ResponseWriter.Write(p)
}