diff options
Diffstat (limited to 'plumbing/transport')
-rw-r--r-- | plumbing/transport/common.go | 14 | ||||
-rw-r--r-- | plumbing/transport/common_test.go | 12 | ||||
-rw-r--r-- | plumbing/transport/file/client.go | 55 | ||||
-rw-r--r-- | plumbing/transport/file/client_test.go | 22 | ||||
-rw-r--r-- | plumbing/transport/file/server_test.go | 15 | ||||
-rw-r--r-- | plumbing/transport/git/receive_pack_test.go | 6 | ||||
-rw-r--r-- | plumbing/transport/http/receive_pack.go | 13 | ||||
-rw-r--r-- | plumbing/transport/http/receive_pack_test.go | 112 | ||||
-rw-r--r-- | plumbing/transport/internal/common/common.go | 18 | ||||
-rw-r--r-- | plumbing/transport/server/server.go | 3 | ||||
-rw-r--r-- | plumbing/transport/ssh/auth_method.go | 26 | ||||
-rw-r--r-- | plumbing/transport/ssh/auth_method_test.go | 2 | ||||
-rw-r--r-- | plumbing/transport/test/receive_pack.go | 2 | ||||
-rw-r--r-- | plumbing/transport/test/upload_pack.go | 6 |
14 files changed, 230 insertions, 76 deletions
diff --git a/plumbing/transport/common.go b/plumbing/transport/common.go index 2088500..ac71bb3 100644 --- a/plumbing/transport/common.go +++ b/plumbing/transport/common.go @@ -187,6 +187,7 @@ func (e urlEndpoint) Path() string { type scpEndpoint struct { user string host string + port string path string } @@ -194,8 +195,14 @@ func (e *scpEndpoint) Protocol() string { return "ssh" } func (e *scpEndpoint) User() string { return e.user } func (e *scpEndpoint) Password() string { return "" } func (e *scpEndpoint) Host() string { return e.host } -func (e *scpEndpoint) Port() int { return 22 } func (e *scpEndpoint) Path() string { return e.path } +func (e *scpEndpoint) Port() int { + i, err := strconv.Atoi(e.port) + if err != nil { + return 22 + } + return i +} func (e *scpEndpoint) String() string { var user string @@ -220,7 +227,7 @@ func (e *fileEndpoint) String() string { return e.path } var ( isSchemeRegExp = regexp.MustCompile(`^[^:]+://`) - scpLikeUrlRegExp = regexp.MustCompile(`^(?:(?P<user>[^@]+)@)?(?P<host>[^:\s]+):(?P<path>[^\\].*)$`) + scpLikeUrlRegExp = regexp.MustCompile(`^(?:(?P<user>[^@]+)@)?(?P<host>[^:\s]+):(?:(?P<port>[0-9]{1,5})/)?(?P<path>[^\\].*)$`) ) func parseSCPLike(endpoint string) (Endpoint, bool) { @@ -232,7 +239,8 @@ func parseSCPLike(endpoint string) (Endpoint, bool) { return &scpEndpoint{ user: m[1], host: m[2], - path: m[3], + port: m[3], + path: m[4], }, true } diff --git a/plumbing/transport/common_test.go b/plumbing/transport/common_test.go index ec617bd..52759e6 100644 --- a/plumbing/transport/common_test.go +++ b/plumbing/transport/common_test.go @@ -74,6 +74,18 @@ func (s *SuiteCommon) TestNewEndpointSCPLike(c *C) { c.Assert(e.String(), Equals, "git@github.com:user/repository.git") } +func (s *SuiteCommon) TestNewEndpointSCPLikeWithPort(c *C) { + e, err := NewEndpoint("git@github.com:9999/user/repository.git") + c.Assert(err, IsNil) + c.Assert(e.Protocol(), Equals, "ssh") + c.Assert(e.User(), Equals, "git") + c.Assert(e.Password(), Equals, "") + c.Assert(e.Host(), Equals, "github.com") + c.Assert(e.Port(), Equals, 9999) + c.Assert(e.Path(), Equals, "user/repository.git") + c.Assert(e.String(), Equals, "git@github.com:user/repository.git") +} + func (s *SuiteCommon) TestNewEndpointFileAbs(c *C) { e, err := NewEndpoint("/foo.git") c.Assert(err, IsNil) diff --git a/plumbing/transport/file/client.go b/plumbing/transport/file/client.go index 0b42abf..d229fdd 100644 --- a/plumbing/transport/file/client.go +++ b/plumbing/transport/file/client.go @@ -2,9 +2,13 @@ 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" @@ -30,6 +34,45 @@ func NewClient(uploadPackBin, receivePackBin string) transport.Transport { }) } +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) { @@ -40,8 +83,16 @@ func (r *runner) Command(cmd string, ep transport.Endpoint, auth transport.AuthM cmd = r.ReceivePackBin } - if _, err := exec.LookPath(cmd); err != nil { - return nil, err + _, 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 diff --git a/plumbing/transport/file/client_test.go b/plumbing/transport/file/client_test.go index 030175e..864cddc 100644 --- a/plumbing/transport/file/client_test.go +++ b/plumbing/transport/file/client_test.go @@ -14,6 +14,28 @@ import ( func Test(t *testing.T) { TestingT(t) } +type ClientSuite struct { + CommonSuite +} + +var _ = Suite(&ClientSuite{}) + +func (s *ClientSuite) TestCommand(c *C) { + runner := &runner{ + UploadPackBin: transport.UploadPackServiceName, + ReceivePackBin: transport.ReceivePackServiceName, + } + ep, err := transport.NewEndpoint(filepath.Join("fake", "repo")) + c.Assert(err, IsNil) + var emptyAuth transport.AuthMethod + _, err = runner.Command("git-receive-pack", ep, emptyAuth) + c.Assert(err, IsNil) + + // Make sure we get an error for one that doesn't exist. + _, err = runner.Command("git-fake-command", ep, emptyAuth) + c.Assert(err, NotNil) +} + const bareConfig = `[core] repositoryformatversion = 0 filemode = true diff --git a/plumbing/transport/file/server_test.go b/plumbing/transport/file/server_test.go index ee72282..080beef 100644 --- a/plumbing/transport/file/server_test.go +++ b/plumbing/transport/file/server_test.go @@ -35,6 +35,10 @@ func (s *ServerSuite) SetUpSuite(c *C) { } func (s *ServerSuite) TestPush(c *C) { + if !s.checkExecPerm(c) { + c.Skip("go-git binary has not execution permissions") + } + // git <2.0 cannot push to an empty repository without a refspec. cmd := exec.Command("git", "push", "--receive-pack", s.ReceivePackBin, @@ -48,6 +52,10 @@ func (s *ServerSuite) TestPush(c *C) { } func (s *ServerSuite) TestClone(c *C) { + if !s.checkExecPerm(c) { + c.Skip("go-git binary has not execution permissions") + } + pathToClone := c.MkDir() cmd := exec.Command("git", "clone", @@ -59,3 +67,10 @@ func (s *ServerSuite) TestClone(c *C) { out, err := cmd.CombinedOutput() c.Assert(err, IsNil, Commentf("combined stdout and stderr:\n%s\n", out)) } + +func (s *ServerSuite) checkExecPerm(c *C) bool { + const userExecPermMask = 0100 + info, err := os.Stat(s.ReceivePackBin) + c.Assert(err, IsNil) + return (info.Mode().Perm() & userExecPermMask) == userExecPermMask +} diff --git a/plumbing/transport/git/receive_pack_test.go b/plumbing/transport/git/receive_pack_test.go index f9afede..7b0fa46 100644 --- a/plumbing/transport/git/receive_pack_test.go +++ b/plumbing/transport/git/receive_pack_test.go @@ -8,6 +8,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "strings" "time" @@ -29,6 +30,11 @@ type ReceivePackSuite struct { var _ = Suite(&ReceivePackSuite{}) func (s *ReceivePackSuite) SetUpTest(c *C) { + if runtime.GOOS == "windows" { + c.Skip(`git for windows has issues with write operations through git:// protocol. + See https://github.com/git-for-windows/git/issues/907`) + } + s.ReceivePackSuite.Client = DefaultClient port, err := freePort() diff --git a/plumbing/transport/http/receive_pack.go b/plumbing/transport/http/receive_pack.go index b54b70f..d2dfeb7 100644 --- a/plumbing/transport/http/receive_pack.go +++ b/plumbing/transport/http/receive_pack.go @@ -9,6 +9,8 @@ import ( "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp" + "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability" + "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband" "gopkg.in/src-d/go-git.v4/plumbing/transport" "gopkg.in/src-d/go-git.v4/utils/ioutil" ) @@ -52,6 +54,17 @@ func (s *rpSession) ReceivePack(ctx context.Context, req *packp.ReferenceUpdateR return nil, err } + var d *sideband.Demuxer + if req.Capabilities.Supports(capability.Sideband64k) { + d = sideband.NewDemuxer(sideband.Sideband64k, r) + } else if req.Capabilities.Supports(capability.Sideband) { + d = sideband.NewDemuxer(sideband.Sideband, r) + } + if d != nil { + d.Progress = req.Progress + r = d + } + rc := ioutil.NewReadCloser(r, res.Body) report := packp.NewReportStatus() diff --git a/plumbing/transport/http/receive_pack_test.go b/plumbing/transport/http/receive_pack_test.go index d870e5d..970121d 100644 --- a/plumbing/transport/http/receive_pack_test.go +++ b/plumbing/transport/http/receive_pack_test.go @@ -25,6 +25,8 @@ type ReceivePackSuite struct { fixtures.Suite base string + host string + port int } var _ = Suite(&ReceivePackSuite{}) @@ -32,52 +34,35 @@ var _ = Suite(&ReceivePackSuite{}) func (s *ReceivePackSuite) SetUpTest(c *C) { s.ReceivePackSuite.Client = DefaultClient - port, err := freePort() + l, err := net.Listen("tcp", "localhost:0") c.Assert(err, IsNil) base, err := ioutil.TempDir(os.TempDir(), "go-git-http-backend-test") c.Assert(err, IsNil) - s.base = base - host := fmt.Sprintf("localhost_%d", port) - interpolatedBase := filepath.Join(base, host) - err = os.MkdirAll(interpolatedBase, 0755) - c.Assert(err, IsNil) - - dotgit := fixtures.Basic().One().DotGit().Root() - prepareRepo(c, dotgit) - err = os.Rename(dotgit, filepath.Join(interpolatedBase, "basic.git")) - c.Assert(err, IsNil) - - ep, err := transport.NewEndpoint(fmt.Sprintf("http://localhost:%d/basic.git", port)) - c.Assert(err, IsNil) - s.ReceivePackSuite.Endpoint = ep - - dotgit = fixtures.ByTag("empty").One().DotGit().Root() - prepareRepo(c, dotgit) - err = os.Rename(dotgit, filepath.Join(interpolatedBase, "empty.git")) - c.Assert(err, IsNil) + s.port = l.Addr().(*net.TCPAddr).Port + s.host = fmt.Sprintf("localhost_%d", s.port) + s.base = filepath.Join(base, s.host) - ep, err = transport.NewEndpoint(fmt.Sprintf("http://localhost:%d/empty.git", port)) + err = os.MkdirAll(s.base, 0755) c.Assert(err, IsNil) - s.ReceivePackSuite.EmptyEndpoint = ep - ep, err = transport.NewEndpoint(fmt.Sprintf("http://localhost:%d/non-existent.git", port)) - c.Assert(err, IsNil) - s.ReceivePackSuite.NonExistentEndpoint = ep + s.ReceivePackSuite.Endpoint = s.prepareRepository(c, fixtures.Basic().One(), "basic.git") + s.ReceivePackSuite.EmptyEndpoint = s.prepareRepository(c, fixtures.ByTag("empty").One(), "empty.git") + s.ReceivePackSuite.NonExistentEndpoint = s.newEndpoint(c, "non-existent.git") cmd := exec.Command("git", "--exec-path") out, err := cmd.CombinedOutput() c.Assert(err, IsNil) - p := filepath.Join(strings.Trim(string(out), "\n"), "git-http-backend") - h := &cgi.Handler{ - Path: p, - Env: []string{"GIT_HTTP_EXPORT_ALL=true", fmt.Sprintf("GIT_PROJECT_ROOT=%s", interpolatedBase)}, + server := &http.Server{ + Handler: &cgi.Handler{ + Path: filepath.Join(strings.Trim(string(out), "\n"), "git-http-backend"), + Env: []string{"GIT_HTTP_EXPORT_ALL=true", fmt.Sprintf("GIT_PROJECT_ROOT=%s", s.base)}, + }, } - go func() { - log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), h)) + log.Fatal(server.Serve(l)) }() } @@ -86,37 +71,44 @@ func (s *ReceivePackSuite) TearDownTest(c *C) { c.Assert(err, IsNil) } -func freePort() (int, error) { - addr, err := net.ResolveTCPAddr("tcp", "localhost:0") - if err != nil { - return 0, err - } +func (s *ReceivePackSuite) prepareRepository(c *C, f *fixtures.Fixture, name string) transport.Endpoint { + path := filepath.Join(s.base, name) - l, err := net.ListenTCP("tcp", addr) - if err != nil { - return 0, err - } + err := os.Rename(f.DotGit().Root(), path) + c.Assert(err, IsNil) - return l.Addr().(*net.TCPAddr).Port, l.Close() + s.setConfigToRepository(c, path) + return s.newEndpoint(c, name) } -const bareConfig = `[core] -repositoryformatversion = 0 -filemode = true -bare = true -[http] -receivepack = true` - -func prepareRepo(c *C, path string) { - // git-receive-pack refuses to update refs/heads/master on non-bare repo - // so we ensure bare repo config. - config := filepath.Join(path, "config") - if _, err := os.Stat(config); err == nil { - f, err := os.OpenFile(config, os.O_TRUNC|os.O_WRONLY, 0) - c.Assert(err, IsNil) - content := strings.NewReader(bareConfig) - _, err = io.Copy(f, content) - c.Assert(err, IsNil) - c.Assert(f.Close(), IsNil) - } +// git-receive-pack refuses to update refs/heads/master on non-bare repo +// so we ensure bare repo config. +func (s *ReceivePackSuite) setConfigToRepository(c *C, path string) { + cfgPath := filepath.Join(path, "config") + _, err := os.Stat(cfgPath) + c.Assert(err, IsNil) + + cfg, err := os.OpenFile(cfgPath, os.O_TRUNC|os.O_WRONLY, 0) + c.Assert(err, IsNil) + + content := strings.NewReader("" + + "[core]\n" + + "repositoryformatversion = 0\n" + + "filemode = true\n" + + "bare = true\n" + + "[http]\n" + + "receivepack = true\n", + ) + + _, err = io.Copy(cfg, content) + c.Assert(err, IsNil) + + c.Assert(cfg.Close(), IsNil) +} + +func (s *ReceivePackSuite) newEndpoint(c *C, name string) transport.Endpoint { + ep, err := transport.NewEndpoint(fmt.Sprintf("http://localhost:%d/%s", s.port, name)) + c.Assert(err, IsNil) + + return ep } diff --git a/plumbing/transport/internal/common/common.go b/plumbing/transport/internal/common/common.go index 2db8d54..598c6b1 100644 --- a/plumbing/transport/internal/common/common.go +++ b/plumbing/transport/internal/common/common.go @@ -18,6 +18,7 @@ import ( "gopkg.in/src-d/go-git.v4/plumbing/format/pktline" "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp" "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability" + "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband" "gopkg.in/src-d/go-git.v4/plumbing/transport" "gopkg.in/src-d/go-git.v4/utils/ioutil" ) @@ -298,13 +299,26 @@ func (s *session) ReceivePack(ctx context.Context, req *packp.ReferenceUpdateReq } if !req.Capabilities.Supports(capability.ReportStatus) { - // If we have neither report-status or sideband, we can only + // If we don't have report-status, we can only // check return value error. return nil, s.Command.Close() } + r := s.StdoutContext(ctx) + + var d *sideband.Demuxer + if req.Capabilities.Supports(capability.Sideband64k) { + d = sideband.NewDemuxer(sideband.Sideband64k, r) + } else if req.Capabilities.Supports(capability.Sideband) { + d = sideband.NewDemuxer(sideband.Sideband, r) + } + if d != nil { + d.Progress = req.Progress + r = d + } + report := packp.NewReportStatus() - if err := report.Decode(s.StdoutContext(ctx)); err != nil { + if err := report.Decode(r); err != nil { return nil, err } diff --git a/plumbing/transport/server/server.go b/plumbing/transport/server/server.go index be36de5..f896f7a 100644 --- a/plumbing/transport/server/server.go +++ b/plumbing/transport/server/server.go @@ -165,7 +165,8 @@ func (s *upSession) UploadPack(ctx context.Context, req *packp.UploadPackRequest pr, pw := io.Pipe() e := packfile.NewEncoder(pw, s.storer, false) go func() { - _, err := e.Encode(objs) + // TODO: plumb through a pack window. + _, err := e.Encode(objs, 10) pw.CloseWithError(err) }() diff --git a/plumbing/transport/ssh/auth_method.go b/plumbing/transport/ssh/auth_method.go index f95235b..baae181 100644 --- a/plumbing/transport/ssh/auth_method.go +++ b/plumbing/transport/ssh/auth_method.go @@ -3,6 +3,7 @@ package ssh import ( "crypto/x509" "encoding/pem" + "errors" "fmt" "io/ioutil" "os" @@ -11,6 +12,7 @@ import ( "gopkg.in/src-d/go-git.v4/plumbing/transport" + "github.com/mitchellh/go-homedir" "github.com/xanzy/ssh-agent" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/knownhosts" @@ -164,6 +166,19 @@ func (a *PublicKeys) clientConfig() *ssh.ClientConfig { } } +func username() (string, error) { + var username string + if user, err := user.Current(); err == nil { + username = user.Username + } else { + username = os.Getenv("USER") + } + if username == "" { + return "", errors.New("failed to get username") + } + return username, nil +} + // PublicKeysCallback implements AuthMethod by asking a // ssh.agent.Agent to act as a signer. type PublicKeysCallback struct { @@ -176,13 +191,12 @@ type PublicKeysCallback struct { // a pipe with the SSH agent and uses the pipe as the implementer of the public // key callback function. func NewSSHAgentAuth(u string) (AuthMethod, error) { + var err error if u == "" { - usr, err := user.Current() + u, err = username() if err != nil { - return nil, fmt.Errorf("error getting current user: %q", err) + return nil, err } - - u = usr.Username } a, _, err := sshagent.New() @@ -241,13 +255,13 @@ func getDefaultKnownHostsFiles() ([]string, error) { return files, nil } - user, err := user.Current() + homeDirPath, err := homedir.Dir() if err != nil { return nil, err } return []string{ - filepath.Join(user.HomeDir, "/.ssh/known_hosts"), + filepath.Join(homeDirPath, "/.ssh/known_hosts"), "/etc/ssh/ssh_known_hosts", }, nil } diff --git a/plumbing/transport/ssh/auth_method_test.go b/plumbing/transport/ssh/auth_method_test.go index aa05f7f..2ee5100 100644 --- a/plumbing/transport/ssh/auth_method_test.go +++ b/plumbing/transport/ssh/auth_method_test.go @@ -115,7 +115,7 @@ func (s *SuiteCommon) TestNewSSHAgentAuthNoAgent(c *C) { k, err := NewSSHAgentAuth("foo") c.Assert(k, IsNil) - c.Assert(err, ErrorMatches, ".*SSH_AUTH_SOCK.*") + c.Assert(err, ErrorMatches, ".*SSH_AUTH_SOCK.*|.*SSH agent .* not running.*") } func (*SuiteCommon) TestNewPublicKeys(c *C) { diff --git a/plumbing/transport/test/receive_pack.go b/plumbing/transport/test/receive_pack.go index d29d9ca..ed0f517 100644 --- a/plumbing/transport/test/receive_pack.go +++ b/plumbing/transport/test/receive_pack.go @@ -348,7 +348,7 @@ func (s *ReceivePackSuite) testSendPackDeleteReference(c *C) { func (s *ReceivePackSuite) emptyPackfile() io.ReadCloser { var buf bytes.Buffer e := packfile.NewEncoder(&buf, memory.NewStorage(), false) - _, err := e.Encode(nil) + _, err := e.Encode(nil, 10) if err != nil { panic(err) } diff --git a/plumbing/transport/test/upload_pack.go b/plumbing/transport/test/upload_pack.go index ade6cdc..b3acc4f 100644 --- a/plumbing/transport/test/upload_pack.go +++ b/plumbing/transport/test/upload_pack.go @@ -31,6 +31,8 @@ type UploadPackSuite struct { func (s *UploadPackSuite) TestAdvertisedReferencesEmpty(c *C) { r, err := s.Client.NewUploadPackSession(s.EmptyEndpoint, s.EmptyAuth) c.Assert(err, IsNil) + defer func() { c.Assert(r.Close(), IsNil) }() + ar, err := r.AdvertisedReferences() c.Assert(err, Equals, transport.ErrEmptyRemoteRepository) c.Assert(ar, IsNil) @@ -39,6 +41,8 @@ func (s *UploadPackSuite) TestAdvertisedReferencesEmpty(c *C) { func (s *UploadPackSuite) TestAdvertisedReferencesNotExists(c *C) { r, err := s.Client.NewUploadPackSession(s.NonExistentEndpoint, s.EmptyAuth) c.Assert(err, IsNil) + defer func() { c.Assert(r.Close(), IsNil) }() + ar, err := r.AdvertisedReferences() c.Assert(err, Equals, transport.ErrRepositoryNotFound) c.Assert(ar, IsNil) @@ -55,6 +59,8 @@ func (s *UploadPackSuite) TestAdvertisedReferencesNotExists(c *C) { func (s *UploadPackSuite) TestCallAdvertisedReferenceTwice(c *C) { r, err := s.Client.NewUploadPackSession(s.Endpoint, s.EmptyAuth) c.Assert(err, IsNil) + defer func() { c.Assert(r.Close(), IsNil) }() + ar1, err := r.AdvertisedReferences() c.Assert(err, IsNil) c.Assert(ar1, NotNil) |