зеркало из https://github.com/mislav/hub.git
Import cucumber tests from Hub and get the pull_request.feature going a bit
This commit is contained in:
Родитель
65ba149f30
Коммит
a270fd25f6
|
@ -2,3 +2,4 @@ gh
|
|||
*.test
|
||||
target
|
||||
.vagrant
|
||||
/tmp
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
gem 'aruba', '~> 0.5.3'
|
||||
gem 'cucumber', '~> 1.3.9'
|
||||
gem 'sinatra'
|
||||
gem 'rake'
|
|
@ -0,0 +1,42 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
aruba (0.5.3)
|
||||
childprocess (>= 0.3.6)
|
||||
cucumber (>= 1.1.1)
|
||||
rspec-expectations (>= 2.7.0)
|
||||
builder (3.2.2)
|
||||
childprocess (0.3.9)
|
||||
ffi (~> 1.0, >= 1.0.11)
|
||||
cucumber (1.3.10)
|
||||
builder (>= 2.1.2)
|
||||
diff-lcs (>= 1.1.3)
|
||||
gherkin (~> 2.12)
|
||||
multi_json (>= 1.7.5, < 2.0)
|
||||
multi_test (>= 0.0.2)
|
||||
diff-lcs (1.2.5)
|
||||
ffi (1.9.3)
|
||||
gherkin (2.12.2)
|
||||
multi_json (~> 1.3)
|
||||
multi_json (1.8.2)
|
||||
multi_test (0.0.2)
|
||||
rack (1.5.2)
|
||||
rack-protection (1.5.1)
|
||||
rack
|
||||
rake (10.1.0)
|
||||
rspec-expectations (2.14.4)
|
||||
diff-lcs (>= 1.1.3, < 2.0)
|
||||
sinatra (1.4.4)
|
||||
rack (~> 1.4)
|
||||
rack-protection (~> 1.4)
|
||||
tilt (~> 1.3, >= 1.3.4)
|
||||
tilt (1.4.1)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
aruba (~> 0.5.3)
|
||||
cucumber (~> 1.3.9)
|
||||
rake
|
||||
sinatra
|
|
@ -42,7 +42,7 @@ func fetch(command *Command, args *Args) {
|
|||
func tranformFetchArgs(args *Args) error {
|
||||
names := parseRemoteNames(args)
|
||||
gh := github.New()
|
||||
projects := []github.Project{}
|
||||
projects := []*github.Project{}
|
||||
ownerRegexp := regexp.MustCompile(OwnerRe)
|
||||
for _, name := range names {
|
||||
if ownerRegexp.MatchString(name) && !hasGitRemote(name) {
|
||||
|
|
|
@ -9,13 +9,14 @@ import (
|
|||
"github.com/jingweno/gh/utils"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var cmdPullRequest = &Command{
|
||||
Run: pullRequest,
|
||||
Usage: "pull-request [-f] [-i ISSUE] [-b BASE] [-d HEAD] [TITLE]",
|
||||
Usage: "pull-request [-f] [-i ISSUE] [-b BASE] [-d HEAD] [-m MESSAGE] [TITLE]",
|
||||
Short: "Open a pull request on GitHub",
|
||||
Long: `Opens a pull request on GitHub for the project that the "origin" remote
|
||||
points to. The default head of the pull request is the current branch.
|
||||
|
@ -34,12 +35,13 @@ of title you can paste a full URL to an issue on GitHub.
|
|||
`,
|
||||
}
|
||||
|
||||
var flagPullRequestBase, flagPullRequestHead, flagPullRequestIssue string
|
||||
var flagPullRequestBase, flagPullRequestHead, flagPullRequestIssue, flagPullRequestMessage string
|
||||
|
||||
func init() {
|
||||
cmdPullRequest.Flag.StringVar(&flagPullRequestBase, "b", "master", "BASE")
|
||||
cmdPullRequest.Flag.StringVar(&flagPullRequestHead, "d", "", "HEAD")
|
||||
cmdPullRequest.Flag.StringVar(&flagPullRequestIssue, "i", "", "ISSUE")
|
||||
cmdPullRequest.Flag.StringVar(&flagPullRequestMessage, "m", "", "MESSAGE")
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -51,19 +53,95 @@ func init() {
|
|||
# explicit pull base & head:
|
||||
$ gh pull-request -b jingweno:master -h jingweno:feature
|
||||
|
||||
$ gh pull-request -m "title\n\nbody"
|
||||
[ create pull request with title & body ]
|
||||
|
||||
$ gh pull-request -i 123
|
||||
[ attached pull request to issue #123 ]
|
||||
|
||||
$ gh pull-request https://github.com/jingweno/gh/pull/123
|
||||
[ attached pull request to issue #123 ]
|
||||
*/
|
||||
func pullRequest(cmd *Command, args *Args) {
|
||||
var title, body string
|
||||
if args.ParamsSize() == 1 {
|
||||
title = args.RemoveParam(0)
|
||||
localRepo := github.LocalRepo()
|
||||
|
||||
currentBranch, err := localRepo.CurrentBranch()
|
||||
if err != nil {
|
||||
utils.Check(fmt.Errorf("Aborted: not currently on any branch."))
|
||||
}
|
||||
|
||||
gh := github.New()
|
||||
repo := gh.Project.LocalRepoWith(flagPullRequestBase, flagPullRequestHead)
|
||||
baseProject, err := localRepo.MainProject()
|
||||
if err != nil {
|
||||
utils.Check(fmt.Errorf("Aborted: the origin remote doesn't point to a GitHub repository."))
|
||||
}
|
||||
|
||||
headProject, err := localRepo.CurrentProject()
|
||||
utils.Check(err)
|
||||
|
||||
gh := github.NewWithoutProject()
|
||||
gh.Project = baseProject
|
||||
|
||||
var base, head string
|
||||
if flagPullRequestBase != "" {
|
||||
base = flagPullRequestBase
|
||||
}
|
||||
if flagPullRequestHead != "" {
|
||||
head = flagPullRequestHead
|
||||
}
|
||||
if args.ParamsSize() == 1 {
|
||||
arg := args.RemoveParam(0)
|
||||
u, e := github.ParseURL(arg)
|
||||
r := regexp.MustCompile(`^issues\/(\d+)`)
|
||||
p := u.ProjectPath()
|
||||
if e == nil && r.MatchString(p) {
|
||||
flagPullRequestIssue = r.FindStringSubmatch(p)[1]
|
||||
}
|
||||
}
|
||||
|
||||
if base == "" {
|
||||
masterBranch, err := localRepo.MasterBranch()
|
||||
utils.Check(err)
|
||||
base = masterBranch.ShortName()
|
||||
}
|
||||
|
||||
trackedBranch, tberr := currentBranch.Upstream()
|
||||
if head == "" {
|
||||
if err == nil {
|
||||
if trackedBranch.IsRemote() {
|
||||
if reflect.DeepEqual(baseProject, headProject) && base == trackedBranch.ShortName() {
|
||||
e := fmt.Errorf(`Aborted: head branch is the same as base ("%s")`, base)
|
||||
e = fmt.Errorf("%s\n(use `-h <branch>` to specify an explicit pull request head)", e)
|
||||
utils.Check(e)
|
||||
}
|
||||
} else {
|
||||
// the current branch tracking another branch
|
||||
// pretend there's no upstream at all
|
||||
tberr = fmt.Errorf("No upstream found for current branch")
|
||||
}
|
||||
}
|
||||
|
||||
if tberr == nil {
|
||||
head = trackedBranch.ShortName()
|
||||
} else {
|
||||
head = currentBranch.ShortName()
|
||||
}
|
||||
}
|
||||
|
||||
// when no tracking, assume remote branch is published under active user's fork
|
||||
if tberr != nil && gh.Config.User != headProject.Owner {
|
||||
headProject = github.NewProjectFromNameAndOwner(headProject.Name, "")
|
||||
}
|
||||
|
||||
var title, body string
|
||||
if flagPullRequestMessage != "" {
|
||||
title, body = readMsg(flagPullRequestMessage)
|
||||
}
|
||||
|
||||
fullBase := fmt.Sprintf("%s:%s", baseProject.Owner, base)
|
||||
fullHead := fmt.Sprintf("%s:%s", headProject.Owner, head)
|
||||
|
||||
if title == "" && flagPullRequestIssue == "" {
|
||||
t, b, err := writePullRequestTitleAndBody(repo)
|
||||
t, b, err := writePullRequestTitleAndBody(base, head, fullBase, fullHead)
|
||||
utils.Check(err)
|
||||
title = t
|
||||
body = b
|
||||
|
@ -75,17 +153,17 @@ func pullRequest(cmd *Command, args *Args) {
|
|||
|
||||
var pullRequestURL string
|
||||
if args.Noop {
|
||||
args.Before(fmt.Sprintf("Would request a pull request to %s from %s", repo.FullBase(), repo.FullHead()), "")
|
||||
args.Before(fmt.Sprintf("Would request a pull request to %s from %s", fullBase, fullHead), "")
|
||||
pullRequestURL = "PULL_REQUEST_URL"
|
||||
} else {
|
||||
if title != "" {
|
||||
pr, err := gh.CreatePullRequest(repo.Base, repo.Head, title, body)
|
||||
pr, err := gh.CreatePullRequest(base, fullHead, title, body)
|
||||
utils.Check(err)
|
||||
pullRequestURL = pr.HTMLURL
|
||||
}
|
||||
|
||||
if flagPullRequestIssue != "" {
|
||||
pr, err := gh.CreatePullRequestForIssue(repo.Base, repo.Head, flagPullRequestIssue)
|
||||
pr, err := gh.CreatePullRequestForIssue(base, fullHead, flagPullRequestIssue)
|
||||
utils.Check(err)
|
||||
pullRequestURL = pr.HTMLURL
|
||||
}
|
||||
|
@ -94,13 +172,14 @@ func pullRequest(cmd *Command, args *Args) {
|
|||
args.Replace("echo", "", pullRequestURL)
|
||||
}
|
||||
|
||||
func writePullRequestTitleAndBody(repo *github.Repo) (title, body string, err error) {
|
||||
func writePullRequestTitleAndBody(base, head, fullBase, fullHead string) (title, body string, err error) {
|
||||
messageFile, err := git.PullReqMsgFile()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer os.Remove(messageFile)
|
||||
|
||||
err = writePullRequestChanges(repo, messageFile)
|
||||
err = writePullRequestChanges(base, head, fullBase, fullHead, messageFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -112,6 +191,7 @@ func writePullRequestTitleAndBody(repo *github.Repo) (title, body string, err er
|
|||
|
||||
err = editTitleAndBody(editor, messageFile)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error using text editor for pull request message")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -120,26 +200,21 @@ func writePullRequestTitleAndBody(repo *github.Repo) (title, body string, err er
|
|||
return
|
||||
}
|
||||
|
||||
err = os.Remove(messageFile)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func writePullRequestChanges(repo *github.Repo, messageFile string) error {
|
||||
commits, err := git.RefList(repo.Base, repo.Head)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func writePullRequestChanges(base, head, fullBase, fullHead string, messageFile string) error {
|
||||
commits, _ := git.RefList(base, head)
|
||||
|
||||
var defaultMsg, commitSummary string
|
||||
if len(commits) == 1 {
|
||||
defaultMsg, err = git.Show(commits[0])
|
||||
defaultMsg, err := git.Show(commits[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defaultMsg = fmt.Sprintf("%s\n", defaultMsg)
|
||||
} else if len(commits) > 1 {
|
||||
commitLogs, err := git.Log(repo.Base, repo.Head)
|
||||
commitLogs, err := git.Log(base, head)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -166,7 +241,7 @@ func writePullRequestChanges(repo *github.Repo, messageFile string) error {
|
|||
# Write a message for this pull request. The first block
|
||||
# of the text is the title and the rest is description.%s
|
||||
`
|
||||
message = fmt.Sprintf(message, defaultMsg, repo.FullBase(), repo.FullHead(), commitSummary)
|
||||
message = fmt.Sprintf(message, defaultMsg, fullBase, fullHead, commitSummary)
|
||||
|
||||
return ioutil.WriteFile(messageFile, []byte(message), 0644)
|
||||
}
|
||||
|
@ -237,3 +312,13 @@ func readLine(r *bufio.Reader) (string, error) {
|
|||
|
||||
return string(ln), err
|
||||
}
|
||||
|
||||
func readMsg(msg string) (title, body string) {
|
||||
split := strings.SplitN(msg, "\n\n", 2)
|
||||
title = strings.TrimSpace(split[0])
|
||||
if len(split) > 1 {
|
||||
body = strings.TrimSpace(split[1])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"github.com/jingweno/gh/git"
|
||||
"github.com/jingweno/gh/github"
|
||||
"github.com/jingweno/gh/utils"
|
||||
"strings"
|
||||
)
|
||||
|
@ -40,7 +40,8 @@ func transformPushArgs(args *Args) {
|
|||
args.ReplaceParam(0, remotes[0])
|
||||
|
||||
if len(refs) == 0 {
|
||||
head, err := git.Head()
|
||||
localRepo := github.LocalRepo()
|
||||
head, err := localRepo.CurrentBranch()
|
||||
utils.Check(err)
|
||||
refs = []string{head.ShortName()}
|
||||
args.AppendParams(refs...)
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type Branch struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (b *Branch) ShortName() string {
|
||||
reg := regexp.MustCompile("^refs/(remotes/)?.+?/")
|
||||
return reg.ReplaceAllString(b.Name, "")
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"github.com/bmizerany/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestShortName(t *testing.T) {
|
||||
b := Branch{"refs/heads/master"}
|
||||
assert.Equal(t, "master", b.ShortName())
|
||||
}
|
17
git/git.go
17
git/git.go
|
@ -50,13 +50,22 @@ func Editor() (string, error) {
|
|||
return output[0], nil
|
||||
}
|
||||
|
||||
func Head() (*Branch, error) {
|
||||
func Head() (string, error) {
|
||||
output, err := execGitCmd("symbolic-ref", "-q", "HEAD")
|
||||
if err != nil {
|
||||
return nil, errors.New("Can't load git HEAD")
|
||||
return "", errors.New("Can't load git HEAD")
|
||||
}
|
||||
|
||||
return &Branch{output[0]}, nil
|
||||
return output[0], nil
|
||||
}
|
||||
|
||||
func SymbolicFullName(name string) (string, error) {
|
||||
output, err := execGitCmd("rev-parse", "--symbolic-full-name", name)
|
||||
if err != nil {
|
||||
return "", errors.New("Unknown revision or path not in the working tree: " + name)
|
||||
}
|
||||
|
||||
return output[0], nil
|
||||
}
|
||||
|
||||
func Ref(ref string) (string, error) {
|
||||
|
@ -72,7 +81,7 @@ func RefList(a, b string) ([]string, error) {
|
|||
ref := fmt.Sprintf("%s...%s", a, b)
|
||||
output, err := execGitCmd("rev-list", "--cherry-pick", "--right-only", "--no-merges", ref)
|
||||
if err != nil {
|
||||
return nil, errors.New("Can't load rev-list for %s" + ref)
|
||||
return []string{}, fmt.Errorf("Can't load rev-list for %s", ref)
|
||||
}
|
||||
|
||||
return output, nil
|
||||
|
|
|
@ -26,7 +26,7 @@ func TestGitEditor(t *testing.T) {
|
|||
func TestGitRemote(t *testing.T) {
|
||||
gitRemote, _ := OriginRemote()
|
||||
assert.Equal(t, "origin", gitRemote.Name)
|
||||
assert.T(t, strings.Contains(gitRemote.URL, "gh"))
|
||||
assert.T(t, strings.Contains(gitRemote.URL.String(), "gh"))
|
||||
}
|
||||
|
||||
func TestGitHead(t *testing.T) {
|
||||
|
|
|
@ -2,37 +2,46 @@ package git
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type GitRemote struct {
|
||||
type Remote struct {
|
||||
Name string
|
||||
URL string
|
||||
URL *url.URL
|
||||
}
|
||||
|
||||
func Remotes() ([]*GitRemote, error) {
|
||||
r := regexp.MustCompile("(.+)\t(.+github.com.+) \\(push\\)")
|
||||
func Remotes() (remotes []Remote, err error) {
|
||||
re := regexp.MustCompile(`(.+)\s+(.+)\s+\((push|fetch)\)`)
|
||||
|
||||
output, err := execGitCmd("remote", "-v")
|
||||
if err != nil {
|
||||
return nil, errors.New("Can't load git remote")
|
||||
err = errors.New("Can't load git remote")
|
||||
return
|
||||
}
|
||||
|
||||
remotes := make([]*GitRemote, 0)
|
||||
remotesMap := make(map[string]string)
|
||||
for _, o := range output {
|
||||
if r.MatchString(o) {
|
||||
match := r.FindStringSubmatch(o)
|
||||
remotes = append(remotes, &GitRemote{Name: match[1], URL: match[2]})
|
||||
if re.MatchString(o) {
|
||||
match := re.FindStringSubmatch(o)
|
||||
remotesMap[match[1]] = match[2]
|
||||
}
|
||||
}
|
||||
|
||||
if len(remotes) == 0 {
|
||||
return nil, errors.New("Can't find git remote (push)")
|
||||
for k, v := range remotesMap {
|
||||
url, e := ParseURL(v)
|
||||
if e != nil {
|
||||
err = e
|
||||
return
|
||||
}
|
||||
|
||||
remotes = append(remotes, Remote{Name: k, URL: url})
|
||||
}
|
||||
|
||||
return remotes, nil
|
||||
return
|
||||
}
|
||||
|
||||
func OriginRemote() (*GitRemote, error) {
|
||||
func OriginRemote() (*Remote, error) {
|
||||
remotes, err := Remotes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -40,9 +49,9 @@ func OriginRemote() (*GitRemote, error) {
|
|||
|
||||
for _, r := range remotes {
|
||||
if r.Name == "origin" {
|
||||
return r, nil
|
||||
return &r, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("Can't find git remote origin (push)")
|
||||
return nil, errors.New("Can't find git remote origin")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
func ParseURL(rawurl string) (u *url.URL, err error) {
|
||||
sshGitRegexp := regexp.MustCompile(`(\w+)@([\w.]+):([\w\/]+)(\.git)?`)
|
||||
if sshGitRegexp.MatchString(rawurl) {
|
||||
match := sshGitRegexp.FindStringSubmatch(rawurl)
|
||||
user := match[1]
|
||||
host := match[2]
|
||||
path := match[3]
|
||||
ext := match[4]
|
||||
rawurl = fmt.Sprintf("ssh://%s@%s/%s%s", user, host, path, ext)
|
||||
}
|
||||
|
||||
return url.Parse(rawurl)
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"github.com/bmizerany/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestURL_ParseURL(t *testing.T) {
|
||||
u, err := ParseURL("https://github.com/jingweno/gh.git")
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, "github.com", u.Host)
|
||||
assert.Equal(t, "https", u.Scheme)
|
||||
assert.Equal(t, "/jingweno/gh.git", u.Path)
|
||||
|
||||
u, err = ParseURL("git://github.com/jingweno/gh.git")
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, "github.com", u.Host)
|
||||
assert.Equal(t, "git", u.Scheme)
|
||||
assert.Equal(t, "/jingweno/gh.git", u.Path)
|
||||
|
||||
u, err = ParseURL("git@github.com:jingweno/gh.git")
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, "github.com", u.Host)
|
||||
assert.Equal(t, "ssh", u.Scheme)
|
||||
assert.Equal(t, "git", u.User.Username())
|
||||
assert.Equal(t, "/jingweno/gh.git", u.Path)
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package github
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jingweno/gh/git"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Branch string
|
||||
|
||||
func (b Branch) ShortName() string {
|
||||
reg := regexp.MustCompile("^refs/(remotes/)?.+?/")
|
||||
return reg.ReplaceAllString(string(b), "")
|
||||
}
|
||||
|
||||
func (b Branch) Upstream() (u Branch, err error) {
|
||||
name, err := git.SymbolicFullName(fmt.Sprintf("%s@{upstream}", b.ShortName()))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
u = Branch(name)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (b Branch) RemoteName() string {
|
||||
reg := regexp.MustCompile("^refs/remotes/([^/]+)")
|
||||
if reg.MatchString(string(b)) {
|
||||
return reg.FindStringSubmatch(string(b))[1]
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (b Branch) IsRemote() bool {
|
||||
return strings.HasPrefix(string(b), "refs/remotes")
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package github
|
||||
|
||||
import (
|
||||
"github.com/bmizerany/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBranch_ShortName(t *testing.T) {
|
||||
b := Branch("refs/heads/master")
|
||||
assert.Equal(t, "master", b.ShortName())
|
||||
}
|
||||
|
||||
func TestBranch_RemoveName(t *testing.T) {
|
||||
b := Branch("refs/remotes/origin/master")
|
||||
assert.Equal(t, "origin", b.RemoteName())
|
||||
}
|
||||
|
||||
func TestBranch_IsRemote(t *testing.T) {
|
||||
b := Branch("refs/remotes/origin/master")
|
||||
assert.T(t, b.IsRemote())
|
||||
}
|
|
@ -95,7 +95,11 @@ func CurrentConfig() *Config {
|
|||
}
|
||||
|
||||
func loadConfig() (Config, error) {
|
||||
return loadFrom(DefaultConfigFile)
|
||||
configFile := os.Getenv("GH_CONFIG")
|
||||
if configFile == "" {
|
||||
configFile = DefaultConfigFile
|
||||
}
|
||||
return loadFrom(configFile)
|
||||
}
|
||||
|
||||
func loadFrom(filename string) (Config, error) {
|
||||
|
|
|
@ -3,6 +3,7 @@ package github
|
|||
import (
|
||||
"fmt"
|
||||
"github.com/jingweno/go-octokit/octokit"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -62,7 +63,7 @@ func (gh *GitHub) CreatePullRequestForIssue(base, head, issue string) (pr *octok
|
|||
return
|
||||
}
|
||||
|
||||
func (gh *GitHub) Repository(project Project) (repo *octokit.Repository, err error) {
|
||||
func (gh *GitHub) Repository(project *Project) (repo *octokit.Repository, err error) {
|
||||
url, err := octokit.RepositoryURL.Expand(octokit.M{"owner": project.Owner, "repo": project.Name})
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -78,13 +79,13 @@ func (gh *GitHub) Repository(project Project) (repo *octokit.Repository, err err
|
|||
}
|
||||
|
||||
// TODO: detach GitHub from Project
|
||||
func (gh *GitHub) IsRepositoryExist(project Project) bool {
|
||||
func (gh *GitHub) IsRepositoryExist(project *Project) bool {
|
||||
repo, err := gh.Repository(project)
|
||||
|
||||
return err == nil && repo != nil
|
||||
}
|
||||
|
||||
func (gh *GitHub) CreateRepository(project Project, description, homepage string, isPrivate bool) (repo *octokit.Repository, err error) {
|
||||
func (gh *GitHub) CreateRepository(project *Project, description, homepage string, isPrivate bool) (repo *octokit.Repository, err error) {
|
||||
var repoURL octokit.Hyperlink
|
||||
if project.Owner != gh.Config.FetchUser() {
|
||||
repoURL = octokit.OrgRepositoriesURL
|
||||
|
@ -145,7 +146,7 @@ func (gh *GitHub) CIStatus(sha string) (status *octokit.Status, err error) {
|
|||
|
||||
func (gh *GitHub) ForkRepository(name, owner string, noRemote bool) (repo *octokit.Repository, err error) {
|
||||
config := gh.Config
|
||||
project := Project{Name: name, Owner: config.User}
|
||||
project := &Project{Name: name, Owner: config.User}
|
||||
r, err := gh.Repository(project)
|
||||
if err == nil && r != nil {
|
||||
err = fmt.Errorf("Error creating fork: %s exists on %s", r.FullName, GitHubHost)
|
||||
|
@ -233,12 +234,19 @@ func findOrCreateToken(user, password, twoFactorCode string) (token string, err
|
|||
return
|
||||
}
|
||||
|
||||
func (gh *GitHub) octokit() *octokit.Client {
|
||||
func (gh *GitHub) octokit() (c *octokit.Client) {
|
||||
config := gh.Config
|
||||
config.FetchCredentials()
|
||||
tokenAuth := octokit.TokenAuth{AccessToken: config.Token}
|
||||
|
||||
return octokit.NewClient(tokenAuth)
|
||||
host := os.Getenv("GH_API_HOST")
|
||||
if host == "" {
|
||||
c = octokit.NewClient(tokenAuth)
|
||||
} else {
|
||||
c = octokit.NewClientWith(host, nil, tokenAuth)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func New() *GitHub {
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package github
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Hosts []string
|
||||
|
||||
func (h Hosts) Include(host string) bool {
|
||||
for _, hh := range h {
|
||||
if hh == host {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func KnownHosts() (hosts Hosts) {
|
||||
host := os.Getenv("GITHUB_HOST")
|
||||
var mainHost string
|
||||
if host != "" {
|
||||
mainHost = host
|
||||
} else {
|
||||
mainHost = GitHubHost
|
||||
}
|
||||
|
||||
hosts = append(hosts, mainHost)
|
||||
hosts = append(hosts, fmt.Sprintf("ssh.%s", mainHost))
|
||||
|
||||
return
|
||||
}
|
|
@ -58,8 +58,10 @@ func (p *Project) LocalRepoWith(base, head string) *Repo {
|
|||
}
|
||||
if head == "" {
|
||||
headBranch, err := git.Head()
|
||||
utils.Check(err)
|
||||
head = headBranch.ShortName()
|
||||
if err != nil {
|
||||
utils.Check(fmt.Errorf("Aborted: not currently on any branch."))
|
||||
}
|
||||
head = Branch(headBranch).ShortName()
|
||||
}
|
||||
|
||||
return &Repo{base, head, p}
|
||||
|
@ -73,13 +75,13 @@ func CurrentProject() *Project {
|
|||
remote, err := git.OriginRemote()
|
||||
utils.Check(err)
|
||||
|
||||
owner, name := parseOwnerAndName(remote.URL)
|
||||
owner, name := parseOwnerAndName(remote.URL.String())
|
||||
|
||||
return &Project{name, owner}
|
||||
}
|
||||
|
||||
func NewProjectFromURL(url *url.URL) (*Project, error) {
|
||||
if url.Host != GitHubHost || url.Scheme != "https" {
|
||||
if !KnownHosts().Include(url.Host) {
|
||||
return nil, fmt.Errorf("Invalid GitHub URL: %s", url)
|
||||
}
|
||||
|
||||
|
@ -88,10 +90,12 @@ func NewProjectFromURL(url *url.URL) (*Project, error) {
|
|||
return nil, fmt.Errorf("Invalid GitHub URL: %s", url)
|
||||
}
|
||||
|
||||
return &Project{Name: parts[2], Owner: parts[1]}, nil
|
||||
name := strings.TrimRight(parts[2], ".git")
|
||||
|
||||
return &Project{Name: name, Owner: parts[1]}, nil
|
||||
}
|
||||
|
||||
func NewProjectFromNameAndOwner(name, owner string) Project {
|
||||
func NewProjectFromNameAndOwner(name, owner string) *Project {
|
||||
if strings.Contains(owner, "/") {
|
||||
result := strings.SplitN(owner, "/", 2)
|
||||
owner = result[0]
|
||||
|
@ -110,7 +114,7 @@ func NewProjectFromNameAndOwner(name, owner string) Project {
|
|||
name, _ = utils.DirName()
|
||||
}
|
||||
|
||||
return Project{Name: name, Owner: owner}
|
||||
return &Project{Name: name, Owner: owner}
|
||||
}
|
||||
|
||||
func parseOwnerAndName(remote string) (owner string, name string) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package github
|
|||
|
||||
import (
|
||||
"github.com/bmizerany/assert"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
@ -89,3 +90,26 @@ func TestMustMatchGitHubURL(t *testing.T) {
|
|||
assert.Equal(t, "jingweno", url[1])
|
||||
assert.Equal(t, "gh", url[2])
|
||||
}
|
||||
|
||||
func TestNewProjectFromURL(t *testing.T) {
|
||||
u, _ := url.Parse("ssh://git@github.com/jingweno/gh.git")
|
||||
p, err := NewProjectFromURL(u)
|
||||
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, "gh", p.Name)
|
||||
assert.Equal(t, "jingweno", p.Owner)
|
||||
|
||||
u, _ = url.Parse("git://github.com/jingweno/gh.git")
|
||||
p, err = NewProjectFromURL(u)
|
||||
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, "gh", p.Name)
|
||||
assert.Equal(t, "jingweno", p.Owner)
|
||||
|
||||
u, _ = url.Parse("https://github.com/jingweno/gh")
|
||||
p, err = NewProjectFromURL(u)
|
||||
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, "gh", p.Name)
|
||||
assert.Equal(t, "jingweno", p.Owner)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,102 @@
|
|||
package github
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jingweno/gh/git"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func LocalRepo() *GitHubRepo {
|
||||
return &GitHubRepo{}
|
||||
}
|
||||
|
||||
type GitHubRepo struct {
|
||||
remotes []git.Remote
|
||||
}
|
||||
|
||||
func (r *GitHubRepo) remotesByName(name string) (*git.Remote, error) {
|
||||
if r.remotes == nil {
|
||||
remotes, err := git.Remotes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.remotes = remotes
|
||||
}
|
||||
|
||||
for _, remote := range r.remotes {
|
||||
if remote.Name == name {
|
||||
return &remote, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("No git remote with name %s", name)
|
||||
}
|
||||
|
||||
func (r *GitHubRepo) CurrentBranch() (branch Branch, err error) {
|
||||
head, err := git.Head()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
branch = Branch(head)
|
||||
return
|
||||
}
|
||||
|
||||
func (r *GitHubRepo) MasterBranch() (branch Branch, err error) {
|
||||
origin, err := r.remotesByName("origin")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
name, err := git.SymbolicFullName(origin.Name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
branch = Branch(name)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r *GitHubRepo) MainProject() (project *Project, err error) {
|
||||
origin, err := r.remotesByName("origin")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return NewProjectFromURL(origin.URL)
|
||||
}
|
||||
|
||||
func (r *GitHubRepo) CurrentProject() (project *Project, err error) {
|
||||
project, err = r.UpstreamProject()
|
||||
if err != nil {
|
||||
project, err = r.MainProject()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r *GitHubRepo) UpstreamProject() (project *Project, err error) {
|
||||
currentBranch, err := r.CurrentBranch()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
upstream, err := currentBranch.Upstream()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
remote, err := r.remotesByName(upstream.RemoteName())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
project, err = NewProjectFromURL(remote.URL)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type Repo struct {
|
||||
Base string
|
||||
Head string
|
||||
|
|
|
@ -26,12 +26,4 @@ func TestParseURL(t *testing.T) {
|
|||
assert.Equal(t, "jingweno", url.Owner)
|
||||
assert.Equal(t, "gh", url.Name)
|
||||
assert.Equal(t, "", url.ProjectPath())
|
||||
|
||||
url, err =
|
||||
ParseURL("http://github.com/jingweno/gh/")
|
||||
assert.NotEqual(t, nil, err)
|
||||
|
||||
url, err =
|
||||
ParseURL("http://github.com/jingweno/")
|
||||
assert.NotEqual(t, nil, err)
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче