diff options
Diffstat (limited to 'clients/common')
-rw-r--r-- | clients/common/common.go | 132 | ||||
-rw-r--r-- | clients/common/common_test.go | 37 |
2 files changed, 169 insertions, 0 deletions
diff --git a/clients/common/common.go b/clients/common/common.go new file mode 100644 index 0000000..3527d7c --- /dev/null +++ b/clients/common/common.go @@ -0,0 +1,132 @@ +package common + +import ( + "fmt" + "net/url" + "strings" + + "gopkg.in/sourcegraph/go-vcsurl.v1" + "gopkg.in/src-d/go-git.v2/pktline" +) + +const GitUploadPackServiceName = "git-upload-pack" + +type Endpoint string + +func NewEndpoint(url string) (Endpoint, error) { + vcs, err := vcsurl.Parse(url) + if err != nil { + return "", err + } + + link := vcs.Link() + if !strings.HasSuffix(link, ".git") { + link += ".git" + } + + return Endpoint(link), nil +} + +func (e Endpoint) Service(name string) string { + return fmt.Sprintf("%s/info/refs?service=%s", e, name) +} + +// Capabilities contains all the server capabilities +// https://github.com/git/git/blob/master/Documentation/technical/protocol-capabilities.txt +type Capabilities map[string][]string + +func parseCapabilities(line string) Capabilities { + values, _ := url.ParseQuery(strings.Replace(line, " ", "&", -1)) + + return Capabilities(values) +} + +// Supports returns true if capability is preent +func (r Capabilities) Supports(capability string) bool { + _, ok := r[capability] + return ok +} + +// Get returns the values for a capability +func (r Capabilities) Get(capability string) []string { + return r[capability] +} + +// SymbolicReference returns the reference for a given symbolic reference +func (r Capabilities) SymbolicReference(sym string) string { + if !r.Supports("symref") { + return "" + } + + for _, symref := range r.Get("symref") { + parts := strings.Split(symref, ":") + if len(parts) != 2 { + continue + } + + if parts[0] == sym { + return parts[1] + } + } + + return "" +} + +type GitUploadPackInfo struct { + Capabilities Capabilities + Branches map[string]string +} + +func NewGitUploadPackInfo(d *pktline.Decoder) (*GitUploadPackInfo, error) { + info := &GitUploadPackInfo{} + if err := info.read(d); err != nil { + return nil, err + } + + return info, nil +} + +func (r *GitUploadPackInfo) read(d *pktline.Decoder) error { + lines, err := d.ReadAll() + if err != nil { + return err + } + + r.Branches = map[string]string{} + for _, line := range lines { + if !r.isValidLine(line) { + continue + } + + if r.Capabilities == nil { + r.Capabilities = parseCapabilities(line) + continue + } + + r.readLine(line) + } + + return nil +} + +func (r *GitUploadPackInfo) isValidLine(line string) bool { + if line[0] == '#' { + return false + } + + return true +} + +func (r *GitUploadPackInfo) readLine(line string) { + commit, branch := r.getCommitAndBranch(line) + r.Branches[branch] = commit +} + +func (r *GitUploadPackInfo) getCommitAndBranch(line string) (string, string) { + parts := strings.Split(strings.Trim(line, " \n"), " ") + if len(parts) != 2 { + return "", "" + } + + return parts[0], parts[1] +} diff --git a/clients/common/common_test.go b/clients/common/common_test.go new file mode 100644 index 0000000..0b9b60a --- /dev/null +++ b/clients/common/common_test.go @@ -0,0 +1,37 @@ +package common + +import ( + "testing" + + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { TestingT(t) } + +type SuiteCommon struct{} + +var _ = Suite(&SuiteCommon{}) + +func (s *SuiteCommon) TestNewEndpoint(c *C) { + e, err := NewEndpoint("git@github.com:user/repository.git") + c.Assert(err, IsNil) + c.Assert(e, Equals, Endpoint("https://github.com/user/repository.git")) +} + +func (s *SuiteCommon) TestNewEndpointWrongForgat(c *C) { + e, err := NewEndpoint("foo") + c.Assert(err, Not(IsNil)) + c.Assert(e, Equals, Endpoint("")) +} + +func (s *SuiteCommon) TestEndpointService(c *C) { + e, _ := NewEndpoint("git@github.com:user/repository.git") + c.Assert(e.Service("foo"), Equals, "https://github.com/user/repository.git/info/refs?service=foo") +} + +const CapabilitiesFixture = "6ecf0ef2c2dffb796033e5a02219af86ec6584e5 HEADmulti_ack thin-pack side-band side-band-64k ofs-delta shallow no-progress include-tag multi_ack_detailed no-done symref=HEAD:refs/heads/master agent=git/2:2.4.8~dbussink-fix-enterprise-tokens-compilation-1167-gc7006cf" + +func (s *SuiteCommon) TestCapabilitiesSymbolicReference(c *C) { + cap := parseCapabilities(CapabilitiesFixture) + c.Assert(cap.SymbolicReference("HEAD"), Equals, "refs/heads/master") +} |