add support for generating github resource links

pull/38/head
Brad Rydzewski 5 years ago
parent dc09550558
commit 12852c5860

@ -88,6 +88,7 @@ type (
// Services used for communicating with the API.
Driver Driver
Linker Linker
Contents ContentService
Git GitService
Organizations OrganizationService

@ -25,10 +25,15 @@ func New(uri string) (*scm.Client, error) {
if !strings.HasSuffix(base.Path, "/") {
base.Path = base.Path + "/"
}
home := base.String()
if home == "https://api.github.com/" {
home = "https://github.com/"
}
client := &wrapper{new(scm.Client)}
client.BaseURL = base
// initialize services
client.Driver = scm.DriverGithub
client.Linker = &linker{home}
client.Contents = &contentService{client}
client.Git = &gitService{client}
client.Issues = &issueService{client}

@ -0,0 +1,49 @@
// Copyright 2017 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"fmt"
"github.com/drone/go-scm/scm"
)
type linker struct {
base string
}
// Resource returns a link to the resource.
func (l *linker) Resource(ctx context.Context, repo string, ref scm.Reference) (string, error) {
switch {
case scm.IsTag(ref.Path):
t := scm.TrimRef(ref.Path)
return fmt.Sprintf("%s%s/tree/%s", l.base, repo, t), nil
case scm.IsPullRequest(ref.Path):
d := scm.ExtractPullRequest(ref.Path)
return fmt.Sprintf("%s%s/pull/%d", l.base, repo, d), nil
default:
return fmt.Sprintf("%s%s/commit/%s", l.base, repo, ref.Sha), nil
}
}
// Diff returns a link to the diff.
func (l *linker) Diff(ctx context.Context, repo string, source, target scm.Reference) (string, error) {
if scm.IsPullRequest(target.Path) {
d := scm.ExtractPullRequest(target.Path)
return fmt.Sprintf("%s%s/pull/%d/files", l.base, repo, d), nil
}
s := source.Sha
t := target.Sha
if s == "" {
s = scm.TrimRef(source.Path)
}
if t == "" {
t = scm.TrimRef(target.Path)
}
return fmt.Sprintf("%s%s/compare/%s...%s", l.base, repo, s, t), nil
}

@ -0,0 +1,110 @@
// Copyright 2017 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"testing"
"github.com/drone/go-scm/scm"
)
func TestLink(t *testing.T) {
tests := []struct {
path string
sha string
want string
}{
{
path: "refs/heads/master",
sha: "a7389057b0eb027e73b32a81e3c5923a71d01dde",
want: "https://github.com/octocat/hello-world/commit/a7389057b0eb027e73b32a81e3c5923a71d01dde",
},
{
path: "refs/pull/42/head",
sha: "a7389057b0eb027e73b32a81e3c5923a71d01dde",
want: "https://github.com/octocat/hello-world/pull/42",
},
{
path: "refs/tags/v1.0.0",
want: "https://github.com/octocat/hello-world/tree/v1.0.0",
},
}
for _, test := range tests {
client := NewDefault()
ref := scm.Reference{
Path: test.path,
Sha: test.sha,
}
got, err := client.Linker.Resource(context.Background(), "octocat/hello-world", ref)
if err != nil {
t.Error(err)
return
}
want := test.want
if got != want {
t.Errorf("Want link %q, got %q", want, got)
}
}
}
func TestDiff(t *testing.T) {
tests := []struct {
source scm.Reference
target scm.Reference
want string
}{
{
source: scm.Reference{Sha: "a7389057b0eb027e73b32a81e3c5923a71d01dde"},
target: scm.Reference{Sha: "49bbaf4a113bbebfa21cf604cad9aa1503c3f04d"},
want: "https://github.com/octocat/hello-world/compare/a7389057b0eb027e73b32a81e3c5923a71d01dde...49bbaf4a113bbebfa21cf604cad9aa1503c3f04d",
},
{
source: scm.Reference{Path: "refs/heads/master"},
target: scm.Reference{Sha: "49bbaf4a113bbebfa21cf604cad9aa1503c3f04d"},
want: "https://github.com/octocat/hello-world/compare/master...49bbaf4a113bbebfa21cf604cad9aa1503c3f04d",
},
{
source: scm.Reference{Sha: "a7389057b0eb027e73b32a81e3c5923a71d01dde"},
target: scm.Reference{Path: "refs/heads/master"},
want: "https://github.com/octocat/hello-world/compare/a7389057b0eb027e73b32a81e3c5923a71d01dde...master",
},
{
target: scm.Reference{Path: "refs/pull/12/head"},
want: "https://github.com/octocat/hello-world/pull/12/files",
},
}
for _, test := range tests {
client := NewDefault()
got, err := client.Linker.Diff(context.Background(), "octocat/hello-world", test.source, test.target)
if err != nil {
t.Error(err)
return
}
want := test.want
if got != want {
t.Errorf("Want link %q, got %q", want, got)
}
}
}
func TestLink_GitHub_Enterprise(t *testing.T) {
client, _ := New("https://github.acme.com")
ref := scm.Reference{
Path: "refs/heads/master",
Sha: "a7389057b0eb027e73b32a81e3c5923a71d01dde",
}
got, err := client.Linker.Resource(context.Background(), "octocat/hello-world", ref)
if err != nil {
t.Error(err)
return
}
want := "https://github.acme.com/octocat/hello-world/commit/a7389057b0eb027e73b32a81e3c5923a71d01dde"
if got != want {
t.Errorf("Want link %q, got %q", want, got)
}
}

@ -0,0 +1,16 @@
// Copyright 2017 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package scm
import "context"
// Linker provides deep links to resources.
type Linker interface {
// Resource returns a link to the resource.
Resource(ctx context.Context, repo string, ref Reference) (string, error)
// Diff returns a link to the diff.
Diff(ctx context.Context, repo string, source, target Reference) (string, error)
}

@ -4,7 +4,15 @@
package scm
import "strings"
import (
"regexp"
"strconv"
"strings"
)
// regular expression to extract the pull request number
// from the git ref (e.g. refs/pulls/{d}/head)
var re = regexp.MustCompile("\\d+")
// Split splits the full repository name into segments.
func Split(s string) (owner, name string) {
@ -42,8 +50,24 @@ func ExpandRef(name, prefix string) string {
return prefix + "/" + name
}
// ExtractPullRequest returns name extraced pull request
// number from the reference path.
func ExtractPullRequest(ref string) int {
s := re.FindString(ref)
d, _ := strconv.Atoi(s)
return d
}
// IsTag returns true if the reference path points to
// a tag object.
func IsTag(ref string) bool {
return strings.HasPrefix(ref, "refs/tags/")
}
// IsPullRequest returns true if the reference path points
// to a pull request object.
func IsPullRequest(ref string) bool {
return strings.HasPrefix(ref, "refs/pull/") ||
strings.HasPrefix(ref, "refs/pull-request/") ||
strings.HasPrefix(ref, "refs/merge-requests/")
}

@ -101,7 +101,7 @@ func TestExpandRef(t *testing.T) {
}
}
func TestIsRef(t *testing.T) {
func TestIsTag(t *testing.T) {
tests := []struct {
name string
tag bool
@ -122,3 +122,74 @@ func TestIsRef(t *testing.T) {
}
}
}
func TestIsPullRequest(t *testing.T) {
tests := []struct {
name string
tag bool
}{
{
name: "refs/pull/12/head",
tag: true,
},
{
name: "refs/pull/12/merge",
tag: true,
},
{
name: "refs/pull-request/12/head",
tag: true,
},
{
name: "refs/merge-requests/12/head",
tag: true,
},
// not pull requests
{
name: "refs/tags/v1.0.0",
tag: false,
},
{
name: "refs/heads/master",
tag: false,
},
}
for _, test := range tests {
if got, want := IsPullRequest(test.name), test.tag; got != want {
t.Errorf("Got IsPullRequest %v, want %v", got, want)
}
}
}
func TestExtractPullRequest(t *testing.T) {
tests := []struct {
name string
number int
}{
{
name: "refs/pull/12/head",
number: 12,
},
{
name: "refs/pull/12/merge",
number: 12,
},
{
name: "refs/pull-request/12/head",
number: 12,
},
{
name: "refs/merge-requests/12/head",
number: 12,
},
{
name: "refs/heads/master",
number: 0,
},
}
for _, test := range tests {
if got, want := ExtractPullRequest(test.name), test.number; got != want {
t.Errorf("Got pull request number %v, want %v", got, want)
}
}
}

Loading…
Cancel
Save