diff options
author | Santiago M. Mola <santi@mola.io> | 2017-01-04 11:18:41 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-01-04 11:18:41 +0100 |
commit | 841abfb7dc640755c443432064252907e3e55c95 (patch) | |
tree | 8af69dcd3b301a10a3e493e2cd805cdec6dcaecd /plumbing/transport/file | |
parent | 90d67bb648ae32d5b1a0f7b1af011da6dfb24315 (diff) | |
download | go-git-841abfb7dc640755c443432064252907e3e55c95.tar.gz |
server: add git server implementation (#190)
* server: add generic server implementation (transport-independent),
both for git-upload-pack and git-receive-pack.
* server: move internal functions to internal/common.
* cli: add git-receive-pack and git-upload-pack implementations.
* format/packfile: add UpdateObjectStorage function, extracted from
Remote.
* transport: implement tranport RPC-like, only with git-upload-pack and
git-receive-pack methods. Client renamed to Transport.
* storer: add storer.Storer interface.
* protocol/packp: add UploadPackResponse constructor with packfile.
* protocol/packp: fix UploadPackResponse encoding, add tests.
* protocol/packp/capability: implement All.
Diffstat (limited to 'plumbing/transport/file')
-rw-r--r-- | plumbing/transport/file/client.go (renamed from plumbing/transport/file/common.go) | 2 | ||||
-rw-r--r-- | plumbing/transport/file/client_test.go | 40 | ||||
-rw-r--r-- | plumbing/transport/file/common_test.go | 49 | ||||
-rw-r--r-- | plumbing/transport/file/receive_pack_test.go (renamed from plumbing/transport/file/send_pack_test.go) | 36 | ||||
-rw-r--r-- | plumbing/transport/file/server.go | 51 | ||||
-rw-r--r-- | plumbing/transport/file/server_test.go | 105 | ||||
-rw-r--r-- | plumbing/transport/file/upload_pack_test.go (renamed from plumbing/transport/file/fetch_pack_test.go) | 31 |
7 files changed, 249 insertions, 65 deletions
diff --git a/plumbing/transport/file/common.go b/plumbing/transport/file/client.go index e7d18b2..5484009 100644 --- a/plumbing/transport/file/common.go +++ b/plumbing/transport/file/client.go @@ -21,7 +21,7 @@ type runner struct { // NewClient returns a new local client using the given git-upload-pack and // git-receive-pack binaries. -func NewClient(uploadPackBin, receivePackBin string) transport.Client { +func NewClient(uploadPackBin, receivePackBin string) transport.Transport { return common.NewClient(&runner{ UploadPackBin: uploadPackBin, ReceivePackBin: receivePackBin, diff --git a/plumbing/transport/file/client_test.go b/plumbing/transport/file/client_test.go new file mode 100644 index 0000000..220df3d --- /dev/null +++ b/plumbing/transport/file/client_test.go @@ -0,0 +1,40 @@ +package file + +import ( + "fmt" + "io" + "os" + "strings" + "testing" + + "gopkg.in/src-d/go-git.v4/plumbing/transport" + + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { TestingT(t) } + +const bareConfig = `[core] +repositoryformatversion = 0 +filemode = true +bare = true` + +func prepareRepo(c *C, path string) transport.Endpoint { + url := fmt.Sprintf("file://%s", path) + ep, err := transport.NewEndpoint(url) + c.Assert(err, IsNil) + + // git-receive-pack refuses to update refs/heads/master on non-bare repo + // so we ensure bare repo config. + config := fmt.Sprintf("%s/config", path) + 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) + } + + return ep +} diff --git a/plumbing/transport/file/common_test.go b/plumbing/transport/file/common_test.go index 220df3d..cd7c400 100644 --- a/plumbing/transport/file/common_test.go +++ b/plumbing/transport/file/common_test.go @@ -1,40 +1,39 @@ package file import ( - "fmt" - "io" "os" - "strings" - "testing" + "os/exec" + "path/filepath" - "gopkg.in/src-d/go-git.v4/plumbing/transport" + "gopkg.in/src-d/go-git.v4/fixtures" . "gopkg.in/check.v1" + "io/ioutil" ) -func Test(t *testing.T) { TestingT(t) } +type CommonSuite struct { + fixtures.Suite + ReceivePackBin string + UploadPackBin string +} -const bareConfig = `[core] -repositoryformatversion = 0 -filemode = true -bare = true` +var _ = Suite(&CommonSuite{}) -func prepareRepo(c *C, path string) transport.Endpoint { - url := fmt.Sprintf("file://%s", path) - ep, err := transport.NewEndpoint(url) - c.Assert(err, IsNil) +func (s *CommonSuite) SetUpSuite(c *C) { + s.Suite.SetUpSuite(c) - // git-receive-pack refuses to update refs/heads/master on non-bare repo - // so we ensure bare repo config. - config := fmt.Sprintf("%s/config", path) - 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) + if err := exec.Command("git", "--version").Run(); err != nil { + c.Skip("git command not found") } - return ep + binDir, err := ioutil.TempDir(os.TempDir(), "") + c.Assert(err, IsNil) + s.ReceivePackBin = filepath.Join(binDir, "git-receive-pack") + s.UploadPackBin = filepath.Join(binDir, "git-upload-pack") + bin := filepath.Join(binDir, "go-git") + cmd := exec.Command("go", "build", "-o", bin, + "../../../cli/go-git/...") + c.Assert(cmd.Run(), IsNil) + c.Assert(os.Symlink(bin, s.ReceivePackBin), IsNil) + c.Assert(os.Symlink(bin, s.UploadPackBin), IsNil) } diff --git a/plumbing/transport/file/send_pack_test.go b/plumbing/transport/file/receive_pack_test.go index fc7ea35..c07d4ed 100644 --- a/plumbing/transport/file/send_pack_test.go +++ b/plumbing/transport/file/receive_pack_test.go @@ -2,7 +2,6 @@ package file import ( "os" - "os/exec" "gopkg.in/src-d/go-git.v4/fixtures" "gopkg.in/src-d/go-git.v4/plumbing/transport/test" @@ -10,24 +9,19 @@ import ( . "gopkg.in/check.v1" ) -type SendPackSuite struct { - fixtures.Suite - test.SendPackSuite +type ReceivePackSuite struct { + CommonSuite + test.ReceivePackSuite } -var _ = Suite(&SendPackSuite{}) +var _ = Suite(&ReceivePackSuite{}) -func (s *SendPackSuite) SetUpSuite(c *C) { - s.Suite.SetUpSuite(c) - - if err := exec.Command("git", "--version").Run(); err != nil { - c.Skip("git command not found") - } - - s.SendPackSuite.Client = DefaultClient +func (s *ReceivePackSuite) SetUpSuite(c *C) { + s.CommonSuite.SetUpSuite(c) + s.ReceivePackSuite.Client = DefaultClient } -func (s *SendPackSuite) SetUpTest(c *C) { +func (s *ReceivePackSuite) SetUpTest(c *C) { fixture := fixtures.Basic().One() path := fixture.DotGit().Base() s.Endpoint = prepareRepo(c, path) @@ -39,12 +33,12 @@ func (s *SendPackSuite) SetUpTest(c *C) { s.NonExistentEndpoint = prepareRepo(c, "/non-existent") } -func (s *SendPackSuite) TearDownTest(c *C) { +func (s *ReceivePackSuite) TearDownTest(c *C) { s.Suite.TearDownSuite(c) } // TODO: fix test -func (s *SendPackSuite) TestCommandNoOutput(c *C) { +func (s *ReceivePackSuite) TestCommandNoOutput(c *C) { c.Skip("failing test") if _, err := os.Stat("/bin/true"); os.IsNotExist(err) { @@ -52,30 +46,30 @@ func (s *SendPackSuite) TestCommandNoOutput(c *C) { } client := NewClient("true", "true") - session, err := client.NewSendPackSession(s.Endpoint) + session, err := client.NewReceivePackSession(s.Endpoint) c.Assert(err, IsNil) ar, err := session.AdvertisedReferences() c.Assert(err, IsNil) c.Assert(ar, IsNil) } -func (s *SendPackSuite) TestMalformedInputNoErrors(c *C) { +func (s *ReceivePackSuite) TestMalformedInputNoErrors(c *C) { if _, err := os.Stat("/usr/bin/yes"); os.IsNotExist(err) { c.Skip("/usr/bin/yes not found") } client := NewClient("yes", "yes") - session, err := client.NewSendPackSession(s.Endpoint) + session, err := client.NewReceivePackSession(s.Endpoint) c.Assert(err, IsNil) ar, err := session.AdvertisedReferences() c.Assert(err, NotNil) c.Assert(ar, IsNil) } -func (s *SendPackSuite) TestNonExistentCommand(c *C) { +func (s *ReceivePackSuite) TestNonExistentCommand(c *C) { cmd := "/non-existent-git" client := NewClient(cmd, cmd) - session, err := client.NewSendPackSession(s.Endpoint) + session, err := client.NewReceivePackSession(s.Endpoint) c.Assert(err, ErrorMatches, ".*no such file or directory.*") c.Assert(session, IsNil) } diff --git a/plumbing/transport/file/server.go b/plumbing/transport/file/server.go new file mode 100644 index 0000000..d83d5d9 --- /dev/null +++ b/plumbing/transport/file/server.go @@ -0,0 +1,51 @@ +package file + +import ( + "fmt" + "os" + + "gopkg.in/src-d/go-git.v4/plumbing/transport" + "gopkg.in/src-d/go-git.v4/plumbing/transport/internal/common" + "gopkg.in/src-d/go-git.v4/plumbing/transport/server" + "gopkg.in/src-d/go-git.v4/utils/ioutil" +) + +// ServeUploadPack serves a git-upload-pack request using standard output, input +// and error. This is meant to be used when implementing a git-upload-pack +// command. +func ServeUploadPack(path string) error { + ep, err := transport.NewEndpoint(fmt.Sprintf("file://%s", path)) + if err != nil { + return err + } + + s, err := server.DefaultServer.NewUploadPackSession(ep) + if err != nil { + return fmt.Errorf("error creating session: %s", err) + } + + return common.ServeUploadPack(srvCmd, s) +} + +// ServeReceivePack serves a git-receive-pack request using standard output, +// input and error. This is meant to be used when implementing a +// git-receive-pack command. +func ServeReceivePack(path string) error { + ep, err := transport.NewEndpoint(fmt.Sprintf("file://%s", path)) + if err != nil { + return err + } + + s, err := server.DefaultServer.NewReceivePackSession(ep) + if err != nil { + return fmt.Errorf("error creating session: %s", err) + } + + return common.ServeReceivePack(srvCmd, s) +} + +var srvCmd = common.ServerCommand{ + Stdin: os.Stdin, + Stdout: ioutil.WriteNopCloser(os.Stdout), + Stderr: os.Stderr, +} diff --git a/plumbing/transport/file/server_test.go b/plumbing/transport/file/server_test.go new file mode 100644 index 0000000..ff462e2 --- /dev/null +++ b/plumbing/transport/file/server_test.go @@ -0,0 +1,105 @@ +package file + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + + "gopkg.in/src-d/go-git.v4/fixtures" + + . "gopkg.in/check.v1" +) + +type ServerSuite struct { + CommonSuite + RemoteName string + SrcPath string + DstPath string + DstURL string +} + +var _ = Suite(&ServerSuite{}) + +func (s *ServerSuite) SetUpSuite(c *C) { + s.CommonSuite.SetUpSuite(c) + + s.RemoteName = "test" + + fixture := fixtures.Basic().One() + s.SrcPath = fixture.DotGit().Base() + + fixture = fixtures.ByTag("empty").One() + s.DstPath = fixture.DotGit().Base() + s.DstURL = fmt.Sprintf("file://%s", s.DstPath) + + cmd := exec.Command("git", "remote", "add", s.RemoteName, s.DstURL) + cmd.Dir = s.SrcPath + c.Assert(cmd.Run(), IsNil) +} + +func (s *ServerSuite) TestPush(c *C) { + // git <2.0 cannot push to an empty repository without a refspec. + cmd := exec.Command("git", "push", + "--receive-pack", s.ReceivePackBin, + s.RemoteName, "refs/heads/*:refs/heads/*", + ) + cmd.Dir = s.SrcPath + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, "GIT_TRACE=true", "GIT_TRACE_PACKET=true") + stdout, stderr, err := execAndGetOutput(c, cmd) + c.Assert(err, IsNil, Commentf("STDOUT:\n%s\nSTDERR:\n%s\n", stdout, stderr)) +} + +func (s *ServerSuite) TestClone(c *C) { + pathToClone := c.MkDir() + + cmd := exec.Command("git", "clone", + "--upload-pack", s.UploadPackBin, + s.SrcPath, pathToClone, + ) + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, "GIT_TRACE=true", "GIT_TRACE_PACKET=true") + stdout, stderr, err := execAndGetOutput(c, cmd) + c.Assert(err, IsNil, Commentf("STDOUT:\n%s\nSTDERR:\n%s\n", stdout, stderr)) +} + +func execAndGetOutput(c *C, cmd *exec.Cmd) (stdout, stderr string, err error) { + sout, err := cmd.StdoutPipe() + c.Assert(err, IsNil) + serr, err := cmd.StderrPipe() + c.Assert(err, IsNil) + + outChan, outErr := readAllAsync(sout) + errChan, errErr := readAllAsync(serr) + + c.Assert(cmd.Start(), IsNil) + + if err = cmd.Wait(); err != nil { + return <-outChan, <-errChan, err + } + + if err := <-outErr; err != nil { + return <-outChan, <-errChan, err + } + + return <-outChan, <-errChan, <-errErr +} + +func readAllAsync(r io.Reader) (out chan string, err chan error) { + out = make(chan string, 1) + err = make(chan error, 1) + go func() { + b, e := ioutil.ReadAll(r) + if e != nil { + err <- e + } else { + err <- nil + } + + out <- string(b) + }() + + return out, err +} diff --git a/plumbing/transport/file/fetch_pack_test.go b/plumbing/transport/file/upload_pack_test.go index 25e3fef..de232c4 100644 --- a/plumbing/transport/file/fetch_pack_test.go +++ b/plumbing/transport/file/upload_pack_test.go @@ -3,7 +3,6 @@ package file import ( "fmt" "os" - "os/exec" "gopkg.in/src-d/go-git.v4/fixtures" "gopkg.in/src-d/go-git.v4/plumbing/transport" @@ -12,21 +11,17 @@ import ( . "gopkg.in/check.v1" ) -type FetchPackSuite struct { - fixtures.Suite - test.FetchPackSuite +type UploadPackSuite struct { + CommonSuite + test.UploadPackSuite } -var _ = Suite(&FetchPackSuite{}) +var _ = Suite(&UploadPackSuite{}) -func (s *FetchPackSuite) SetUpSuite(c *C) { - s.Suite.SetUpSuite(c) +func (s *UploadPackSuite) SetUpSuite(c *C) { + s.CommonSuite.SetUpSuite(c) - if err := exec.Command("git", "--version").Run(); err != nil { - c.Skip("git command not found") - } - - s.FetchPackSuite.Client = DefaultClient + s.UploadPackSuite.Client = DefaultClient fixture := fixtures.Basic().One() path := fixture.DotGit().Base() @@ -49,7 +44,7 @@ func (s *FetchPackSuite) SetUpSuite(c *C) { } // TODO: fix test -func (s *FetchPackSuite) TestCommandNoOutput(c *C) { +func (s *UploadPackSuite) TestCommandNoOutput(c *C) { c.Skip("failing test") if _, err := os.Stat("/bin/true"); os.IsNotExist(err) { @@ -57,30 +52,30 @@ func (s *FetchPackSuite) TestCommandNoOutput(c *C) { } client := NewClient("true", "true") - session, err := client.NewFetchPackSession(s.Endpoint) + session, err := client.NewUploadPackSession(s.Endpoint) c.Assert(err, IsNil) ar, err := session.AdvertisedReferences() c.Assert(err, IsNil) c.Assert(ar, IsNil) } -func (s *FetchPackSuite) TestMalformedInputNoErrors(c *C) { +func (s *UploadPackSuite) TestMalformedInputNoErrors(c *C) { if _, err := os.Stat("/usr/bin/yes"); os.IsNotExist(err) { c.Skip("/usr/bin/yes not found") } client := NewClient("yes", "yes") - session, err := client.NewFetchPackSession(s.Endpoint) + session, err := client.NewUploadPackSession(s.Endpoint) c.Assert(err, IsNil) ar, err := session.AdvertisedReferences() c.Assert(err, NotNil) c.Assert(ar, IsNil) } -func (s *FetchPackSuite) TestNonExistentCommand(c *C) { +func (s *UploadPackSuite) TestNonExistentCommand(c *C) { cmd := "/non-existent-git" client := NewClient(cmd, cmd) - session, err := client.NewFetchPackSession(s.Endpoint) + session, err := client.NewUploadPackSession(s.Endpoint) c.Assert(err, ErrorMatches, ".*no such file or directory.*") c.Assert(session, IsNil) } |