* Inital routes to git refs api * Git refs API implementation * Update swagger * Fix copyright * Make swagger happy add basic test * Fix test * Fix test again :)
156 lines
3.1 KiB
Go
156 lines
3.1 KiB
Go
// Package file implements the file transport protocol.
|
|
package file
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"gopkg.in/src-d/go-git.v4/plumbing/transport"
|
|
"gopkg.in/src-d/go-git.v4/plumbing/transport/internal/common"
|
|
)
|
|
|
|
// DefaultClient is the default local client.
|
|
var DefaultClient = NewClient(
|
|
transport.UploadPackServiceName,
|
|
transport.ReceivePackServiceName,
|
|
)
|
|
|
|
type runner struct {
|
|
UploadPackBin string
|
|
ReceivePackBin string
|
|
}
|
|
|
|
// NewClient returns a new local client using the given git-upload-pack and
|
|
// git-receive-pack binaries.
|
|
func NewClient(uploadPackBin, receivePackBin string) transport.Transport {
|
|
return common.NewClient(&runner{
|
|
UploadPackBin: uploadPackBin,
|
|
ReceivePackBin: receivePackBin,
|
|
})
|
|
}
|
|
|
|
func prefixExecPath(cmd string) (string, error) {
|
|
// Use `git --exec-path` to find the exec path.
|
|
execCmd := exec.Command("git", "--exec-path")
|
|
|
|
stdout, err := execCmd.StdoutPipe()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
stdoutBuf := bufio.NewReader(stdout)
|
|
|
|
err = execCmd.Start()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
execPathBytes, isPrefix, err := stdoutBuf.ReadLine()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if isPrefix {
|
|
return "", errors.New("Couldn't read exec-path line all at once")
|
|
}
|
|
|
|
err = execCmd.Wait()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
execPath := string(execPathBytes)
|
|
execPath = strings.TrimSpace(execPath)
|
|
cmd = filepath.Join(execPath, cmd)
|
|
|
|
// Make sure it actually exists.
|
|
_, err = exec.LookPath(cmd)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return cmd, nil
|
|
}
|
|
|
|
func (r *runner) Command(cmd string, ep *transport.Endpoint, auth transport.AuthMethod,
|
|
) (common.Command, error) {
|
|
|
|
switch cmd {
|
|
case transport.UploadPackServiceName:
|
|
cmd = r.UploadPackBin
|
|
case transport.ReceivePackServiceName:
|
|
cmd = r.ReceivePackBin
|
|
}
|
|
|
|
_, err := exec.LookPath(cmd)
|
|
if err != nil {
|
|
if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound {
|
|
cmd, err = prefixExecPath(cmd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return &command{cmd: exec.Command(cmd, ep.Path)}, nil
|
|
}
|
|
|
|
type command struct {
|
|
cmd *exec.Cmd
|
|
stderrCloser io.Closer
|
|
closed bool
|
|
}
|
|
|
|
func (c *command) Start() error {
|
|
return c.cmd.Start()
|
|
}
|
|
|
|
func (c *command) StderrPipe() (io.Reader, error) {
|
|
// Pipe returned by Command.StderrPipe has a race with Read + Command.Wait.
|
|
// We use an io.Pipe and close it after the command finishes.
|
|
r, w := io.Pipe()
|
|
c.cmd.Stderr = w
|
|
c.stderrCloser = r
|
|
return r, nil
|
|
}
|
|
|
|
func (c *command) StdinPipe() (io.WriteCloser, error) {
|
|
return c.cmd.StdinPipe()
|
|
}
|
|
|
|
func (c *command) StdoutPipe() (io.Reader, error) {
|
|
return c.cmd.StdoutPipe()
|
|
}
|
|
|
|
func (c *command) Kill() error {
|
|
c.cmd.Process.Kill()
|
|
return c.Close()
|
|
}
|
|
|
|
// Close waits for the command to exit.
|
|
func (c *command) Close() error {
|
|
if c.closed {
|
|
return nil
|
|
}
|
|
|
|
defer func() {
|
|
c.closed = true
|
|
_ = c.stderrCloser.Close()
|
|
|
|
}()
|
|
|
|
err := c.cmd.Wait()
|
|
if _, ok := err.(*os.PathError); ok {
|
|
return nil
|
|
}
|
|
|
|
// When a repository does not exist, the command exits with code 128.
|
|
if _, ok := err.(*exec.ExitError); ok {
|
|
return nil
|
|
}
|
|
|
|
return err
|
|
}
|