提交 49ca2c35 编写于 作者: J Jingwen Owen Ou

Merge pull request #535 from github/fork_compat

gh: Fork compatibility fix
......@@ -60,8 +60,8 @@
},
{
"ImportPath": "github.com/octokit/go-octokit/octokit",
"Comment": "v0.4.0-66-g9368061",
"Rev": "9368061969ae7fe6a1f478ffac4e4708f17c6c59"
"Comment": "v0.4.0-71-g4fee5e3",
"Rev": "4fee5e3019ca20d12c93c1b7f18f20c4a6afff24"
},
{
"ImportPath": "github.com/ogier/pflag",
......
......@@ -15,23 +15,25 @@ func NewClient(authMethod AuthMethod) *Client {
func NewClientWith(baseURL string, userAgent string, authMethod AuthMethod, httpClient *http.Client) *Client {
client, _ := sawyer.NewFromString(baseURL, httpClient)
return &Client{sawyerClient: client, UserAgent: userAgent, AuthMethod: authMethod}
return &Client{Client: client, UserAgent: userAgent, AuthMethod: authMethod}
}
type Client struct {
UserAgent string
AuthMethod AuthMethod
sawyerClient *sawyer.Client
rootRels hypermedia.Relations
*sawyer.Client
UserAgent string
AuthMethod AuthMethod
rootRels hypermedia.Relations
}
func (c *Client) NewRequest(urlStr string) (req *Request, err error) {
sawyerReq, err := c.newSawyerRequest(urlStr)
req, err = newRequest(c, urlStr)
if err != nil {
return
}
req = &Request{sawyerReq: sawyerReq}
c.applyRequestHeaders(req)
return
}
......@@ -72,32 +74,41 @@ func (c *Client) patch(url *url.URL, input interface{}, output interface{}) (res
}
func (c *Client) upload(uploadUrl *url.URL, asset io.ReadCloser, contentType string, contentLength int64) (result *Result) {
req, err := c.newSawyerRequest(uploadUrl.String())
req, err := c.NewRequest(uploadUrl.String())
if err != nil {
result = newResult(nil, err)
return
}
req.Header.Add("Content-Type", contentType)
req.Header.Set("Content-Type", contentType)
req.ContentLength = contentLength
req.Body = asset
sawyerResp := req.Post()
sawyerResp := req.Request.Post()
resp, err := NewResponse(sawyerResp)
return newResult(resp, err)
}
func (c *Client) newSawyerRequest(urlStr string) (sawyerReq *sawyer.Request, err error) {
sawyerReq, err = c.sawyerClient.NewRequest(urlStr)
if err != nil {
return
func (c *Client) applyRequestHeaders(req *Request) {
req.Header.Set("Accept", defaultMediaType)
req.Header.Set("User-Agent", c.UserAgent)
if c.AuthMethod != nil {
req.Header.Set("Authorization", c.AuthMethod.String())
}
sawyerReq.Header.Add("Accept", defaultMediaType)
sawyerReq.Header.Add("User-Agent", c.UserAgent)
if basicAuth, ok := c.AuthMethod.(BasicAuth); ok && basicAuth.OneTimePassword != "" {
req.Header.Set("X-GitHub-OTP", basicAuth.OneTimePassword)
}
c.addAuthenticationHeaders(sawyerReq.Header)
// Go doesn't apply `Host` on the header, instead it consults `Request.Host`
// Populate `Host` if it exists in `Client.Header`
// See Bug https://code.google.com/p/go/issues/detail?id=7682
host := c.Header.Get("Host")
if host != "" {
req.Request.Host = host
}
return
}
......@@ -114,13 +125,3 @@ func sendRequest(c *Client, url *url.URL, fn func(r *Request) (*Response, error)
return
}
func (c *Client) addAuthenticationHeaders(header http.Header) {
if c.AuthMethod != nil {
header.Add("Authorization", c.AuthMethod.String())
}
if basicAuth, ok := c.AuthMethod.(BasicAuth); ok && basicAuth.OneTimePassword != "" {
header.Add("X-GitHub-OTP", basicAuth.OneTimePassword)
}
}
......@@ -124,3 +124,23 @@ func TestSuccessfulPost(t *testing.T) {
assert.Equal(t, nil, err)
assert.Equal(t, "octokit", output["login"])
}
func TestAddHeader(t *testing.T) {
setup()
defer tearDown()
mux.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Foo", "Bar")
assert.Equal(t, "example.com", r.Host)
respondWithJSON(w, `{"login": "octokit"}`)
})
client.Header.Set("Host", "example.com")
client.Header.Set("Foo", "Bar")
req, err := client.NewRequest("foo")
assert.Equal(t, nil, err)
_, err = req.Get(nil)
assert.Equal(t, nil, err)
}
......@@ -5,44 +5,56 @@ import (
"github.com/lostisland/go-sawyer/mediatype"
)
func newRequest(client *Client, urlStr string) (req *Request, err error) {
sawyerReq, err := client.Client.NewRequest(urlStr)
if err != nil {
return
}
req = &Request{client: client, Request: sawyerReq}
return
}
type Request struct {
sawyerReq *sawyer.Request
*sawyer.Request
client *Client
}
func (r *Request) Head(output interface{}) (*Response, error) {
return r.createResponse(r.sawyerReq.Head(), output)
return r.createResponse(r.Request.Head(), output)
}
func (r *Request) Get(output interface{}) (*Response, error) {
return r.createResponse(r.sawyerReq.Get(), output)
return r.createResponse(r.Request.Get(), output)
}
func (r *Request) Post(input interface{}, output interface{}) (*Response, error) {
r.setBody(input)
return r.createResponse(r.sawyerReq.Post(), output)
return r.createResponse(r.Request.Post(), output)
}
func (r *Request) Put(input interface{}, output interface{}) (*Response, error) {
r.setBody(input)
return r.createResponse(r.sawyerReq.Put(), output)
return r.createResponse(r.Request.Put(), output)
}
func (r *Request) Delete(output interface{}) (*Response, error) {
return r.createResponse(r.sawyerReq.Delete(), output)
return r.createResponse(r.Request.Delete(), output)
}
func (r *Request) Patch(input interface{}, output interface{}) (*Response, error) {
r.setBody(input)
return r.createResponse(r.sawyerReq.Patch(), output)
return r.createResponse(r.Request.Patch(), output)
}
func (r *Request) Options(output interface{}) (*Response, error) {
return r.createResponse(r.sawyerReq.Options(), output)
return r.createResponse(r.Request.Options(), output)
}
func (r *Request) setBody(input interface{}) {
mtype, _ := mediatype.Parse(defaultMediaType)
r.sawyerReq.SetBody(mtype, input)
r.Request.SetBody(mtype, input)
}
func (r *Request) createResponse(sawyerResp *sawyer.Response, output interface{}) (resp *Response, err error) {
......
......@@ -67,7 +67,7 @@ func browse(command *Command, args *Args) {
subpage = args.RemoveParam(0)
}
localRepo := github.LocalRepo()
localRepo, _ := github.LocalRepo()
if dest != "" {
project = github.NewProject("", dest, "")
branch = localRepo.MasterBranch()
......
......@@ -84,7 +84,9 @@ func transformCheckoutArgs(args *Args) error {
newBranchName = fmt.Sprintf("%s-%s", user, branch)
}
repo := github.LocalRepo()
repo, err := github.LocalRepo()
utils.Check(err)
_, err = repo.RemoteByName(user)
if err == nil {
args.Before("git", "remote", "set-branches", "--add", user, branch)
......
package commands
import (
"regexp"
"github.com/github/hub/github"
"github.com/github/hub/utils"
"regexp"
)
var cmdCherryPick = &Command{
......@@ -72,7 +73,10 @@ func parseCherryPickProjectAndSha(ref string) (project *github.Project, sha stri
if ownerWithShaRegexp.MatchString(ref) {
matches := ownerWithShaRegexp.FindStringSubmatch(ref)
sha = matches[2]
project, err := github.LocalRepo().CurrentProject()
localRepo, err := github.LocalRepo()
utils.Check(err)
project, err := localRepo.CurrentProject()
utils.Check(err)
project.Owner = matches[1]
}
......
......@@ -2,10 +2,11 @@ package commands
import (
"fmt"
"os"
"github.com/github/hub/git"
"github.com/github/hub/github"
"github.com/github/hub/utils"
"os"
)
var cmdCiStatus = &Command{
......@@ -51,7 +52,9 @@ func ciStatus(cmd *Command, args *Args) {
ref = args.RemoveParam(0)
}
localRepo := github.LocalRepo()
localRepo, err := github.LocalRepo()
utils.Check(err)
project, err := localRepo.MainProject()
utils.Check(err)
......
......@@ -44,12 +44,13 @@ func init() {
> open https://github.com/other-user/REPO/compare/patch
*/
func compare(command *Command, args *Args) {
localRepo := github.LocalRepo()
localRepo, err := github.LocalRepo()
utils.Check(err)
var (
branch *github.Branch
project *github.Project
r string
err error
)
branch, project, err = localRepo.RemoteBranchAndProject("")
......
package commands
import (
"github.com/github/hub/github"
"github.com/github/hub/utils"
"regexp"
"strings"
"github.com/github/hub/github"
"github.com/github/hub/utils"
)
var cmdFetch = &Command{
......@@ -45,7 +46,9 @@ func fetch(command *Command, args *Args) {
func tranformFetchArgs(args *Args) error {
names := parseRemoteNames(args)
localRepo := github.LocalRepo()
localRepo, err := github.LocalRepo()
utils.Check(err)
projects := make(map[*github.Project]bool)
ownerRegexp := regexp.MustCompile(OwnerRe)
......
......@@ -2,11 +2,10 @@ package commands
import (
"fmt"
"os"
"reflect"
"github.com/github/hub/github"
"github.com/github/hub/utils"
"os"
"reflect"
)
var cmdFork = &Command{
......@@ -35,10 +34,13 @@ func init() {
[ repo forked on GitHub ]
*/
func fork(cmd *Command, args *Args) {
localRepo := github.LocalRepo()
localRepo, err := github.LocalRepo()
utils.Check(err)
project, err := localRepo.MainProject()
utils.Check(err)
if err != nil {
utils.Check(fmt.Errorf("Error: repository under 'origin' remote is not a GitHub project"))
}
configs := github.CurrentConfigs()
host, err := configs.PromptForHost(project.Host)
......@@ -47,7 +49,6 @@ func fork(cmd *Command, args *Args) {
}
forkProject := github.NewProject(host.User, project.Name, project.Host)
client := github.NewClient(project.Host)
existingRepo, err := client.Repository(forkProject)
if err == nil {
......@@ -70,8 +71,11 @@ func fork(cmd *Command, args *Args) {
if flagForkNoRemote {
os.Exit(0)
} else {
u := forkProject.GitURL("", "", true)
args.Replace("git", "remote", "add", "-f", forkProject.Owner, u)
originRemote, _ := localRepo.OriginRemote()
originURL := originRemote.URL.String()
url := forkProject.GitURL("", "", true)
args.Replace("git", "remote", "add", "-f", forkProject.Owner, originURL)
args.After("git", "remote", "set-url", forkProject.Owner, url)
args.After("echo", fmt.Sprintf("new remote: %s", forkProject.Owner))
}
}
......@@ -76,7 +76,8 @@ func init() {
[ create pull request with title & body from FILE ]
*/
func pullRequest(cmd *Command, args *Args) {
localRepo := github.LocalRepo()
localRepo, err := github.LocalRepo()
utils.Check(err)
currentBranch, err := localRepo.CurrentBranch()
utils.Check(err)
......
package commands
import (
"strings"
"github.com/github/hub/github"
"github.com/github/hub/utils"
"strings"
)
var cmdPush = &Command{
......@@ -44,9 +45,12 @@ func transformPushArgs(args *Args) {
args.ReplaceParam(0, remotes[0])
if len(refs) == 0 {
localRepo := github.LocalRepo()
localRepo, err := github.LocalRepo()
utils.Check(err)
head, err := localRepo.CurrentBranch()
utils.Check(err)
refs = []string{head.ShortName()}
args.AppendParams(refs...)
}
......
......@@ -48,7 +48,9 @@ func transformRemoteArgs(args *Args) {
return
}
localRepo := github.LocalRepo()
localRepo, err := github.LocalRepo()
utils.Check(err)
var repoName string
if name == "" {
project, err := localRepo.MainProject()
......
package commands
import (
"github.com/github/hub/github"
"github.com/github/hub/utils"
"github.com/octokit/go-octokit/octokit"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/github/hub/github"
"github.com/github/hub/utils"
"github.com/octokit/go-octokit/octokit"
)
type listFlag []string
......@@ -101,7 +102,9 @@ func readMsg(msg string) (title, body string) {
}
func runInLocalRepo(fn func(localRepo *github.GitHubRepo, project *github.Project, client *github.Client)) {
localRepo := github.LocalRepo()
localRepo, err := github.LocalRepo()
utils.Check(err)
project, err := localRepo.CurrentProject()
utils.Check(err)
......
......@@ -57,7 +57,7 @@ Feature: hub fork
Given the GitHub API server:
"""
get('/repos/mislav/dotfiles') {
halt 406 unless request.env['HTTP_ACCEPT'] == 'application/vnd.github.v3+json'
halt 406 unless request.env['HTTP_ACCEPT'] == 'application/vnd.github.v3+json;charset=utf-8'
json :parent => { :html_url => 'https://github.com/unrelated/dotfiles' }
}
"""
......
......@@ -106,8 +106,7 @@ Given(/^the GitHub API server:$/) do |endpoints_str|
eval endpoints_str, binding
end
# hit our Sinatra server instead of github.com
set_env 'HUB_TEST_HOST', "127.0.0.1:#{@server.port}"
set_env 'GH_API_HOST', "http://127.0.0.1:#{@server.port}"
set_env 'HUB_TEST_HOST', "http://127.0.0.1:#{@server.port}"
end
Given(/^I use a debugging proxy(?: at "(.+?)")?$/) do |address|
......
......@@ -30,7 +30,6 @@ Before do
set_env 'HUB_SYSTEM_GIT', system_git
# ensure that api.github.com is actually never hit in tests
set_env 'HUB_TEST_HOST', '127.0.0.1:0'
set_env 'GH_API_HOST', 'http://127.0.0.1:0'
# ensure we use fakebin `open` to test browsing
set_env 'BROWSER', 'open'
# sabotage opening a commit message editor interactively
......
package github
import (
"net/url"
"os"
)
type apiHost struct {
Host string
}
func (ah *apiHost) String() string {
host := os.Getenv("GH_API_HOST")
if host == "" && ah.Host != "" {
host = ah.Host
}
if host == GitHubHost {
host = GitHubApiHost
}
return absolute(host)
}
func absolute(endpoint string) string {
u, _ := url.Parse(endpoint)
if u.Scheme == "" {
u.Scheme = "https"
}
return u.String()
}
package github
import (
"testing"
"github.com/bmizerany/assert"
)
func TestApiHost_String(t *testing.T) {
ah := &apiHost{"github.com"}
assert.Equal(t, "https://api.github.com", ah.String())
ah = &apiHost{"github.corporate.com"}
assert.Equal(t, "https://github.corporate.com", ah.String())
ah = &apiHost{"http://github.corporate.com"}
assert.Equal(t, "http://github.corporate.com", ah.String())
}
package github
import (
"github.com/bmizerany/assert"
"testing"
"github.com/bmizerany/assert"
)
func TestBranch_ShortName(t *testing.T) {
b := Branch{LocalRepo(), "refs/heads/master"}
lp, _ := LocalRepo()
b := Branch{lp, "refs/heads/master"}
assert.Equal(t, "master", b.ShortName())
}
func TestBranch_LongName(t *testing.T) {
b := Branch{LocalRepo(), "refs/heads/master"}
lp, _ := LocalRepo()
b := Branch{lp, "refs/heads/master"}
assert.Equal(t, "heads/master", b.LongName())
b = Branch{LocalRepo(), "refs/remotes/origin/master"}
b = Branch{lp, "refs/remotes/origin/master"}
assert.Equal(t, "origin/master", b.LongName())
}
func TestBranch_RemoteName(t *testing.T) {
b := Branch{LocalRepo(), "refs/remotes/origin/master"}
lp, _ := LocalRepo()
b := Branch{lp, "refs/remotes/origin/master"}
assert.Equal(t, "origin", b.RemoteName())
b = Branch{LocalRepo(), "refs/head/master"}
b = Branch{lp, "refs/head/master"}
assert.Equal(t, "", b.RemoteName())
}
func TestBranch_IsRemote(t *testing.T) {
b := Branch{LocalRepo(), "refs/remotes/origin/master"}
lp, _ := LocalRepo()
b := Branch{lp, "refs/remotes/origin/master"}
assert.T(t, b.IsRemote())
}
......@@ -273,13 +273,13 @@ func (client *Client) ForkRepository(project *Project) (repo *octokit.Repository
api, err := client.api()
if err != nil {
err = FormatError("forking repository", err)
err = FormatError("creating fork", err)
return
}
repo, result := api.Repositories(client.requestURL(url)).Create(nil)
if result.HasError() {
err = FormatError("forking repository", result.Err)
err = FormatError("creating fork", result.Err)
return
}
......@@ -339,7 +339,7 @@ func (client *Client) GhLatestTagName() (tagName string, err error) {
return
}
c := octokit.NewClientWith(client.apiHost(), UserAgent, nil, nil)
c := client.newOctokitClient(nil)
releases, result := c.Releases(client.requestURL(url)).All()
if result.HasError() {
err = fmt.Errorf("Error getting gh release: %s", result.Err)
......@@ -385,7 +385,7 @@ func (client *Client) FindOrCreateToken(user, password, twoFactorCode string) (t
}
basicAuth := octokit.BasicAuth{Login: user, Password: password, OneTimePassword: twoFactorCode}
c := octokit.NewClientWith(client.apiHost(), UserAgent, basicAuth, nil)
c := client.newOctokitClient(basicAuth)
authsService := c.Authorizations(client.requestURL(url))
auths, result := authsService.All()
......@@ -451,11 +451,42 @@ func (client *Client) api() (c *octokit.Client, err error) {
}
tokenAuth := octokit.TokenAuth{AccessToken: client.Host.AccessToken}
c = client.newOctokitClient(tokenAuth)
return
}
func (client *Client) newOctokitClient(auth octokit.AuthMethod) *octokit.Client {
var host string
if client.Host != nil {
host = client.Host.Host
}
if host == "" {
host = GitHubHost
}
if host == GitHubHost {
host = GitHubApiHost
}
apiHost := host
hubTestHost := os.Getenv("HUB_TEST_HOST")
if hubTestHost != "" {
apiHost = hubTestHost
}
apiHost = absolute(apiHost)
tr := &http.Transport{Proxy: proxyFromEnvironment}
httpClient := &http.Client{Transport: tr}
c = octokit.NewClientWith(client.apiHost(), UserAgent, tokenAuth, httpClient)
c := octokit.NewClientWith(apiHost, UserAgent, auth, httpClient)
if hubTestHost != "" {
// if it's in test, make sure host name is in the header
c.Header.Set("Host", host)
}
return
return c
}
func (client *Client) requestURL(u *url.URL) (uu *url.URL) {
......@@ -467,13 +498,10 @@ func (client *Client) requestURL(u *url.URL) (uu *url.URL) {
return
}
func (client *Client) apiHost() string {
ah := &apiHost{client.Host.Host}
return ah.String()
}
func FormatError(action string, err error) (ee error) {
switch e := err.(type) {
default:
ee = err
case *octokit.ResponseError:
statusCode := e.Response.StatusCode
var reason string
......@@ -504,8 +532,6 @@ func FormatError(action string, err error) (ee error) {
case *AuthError:
errStr := fmt.Sprintf("Error %s: Unauthorized (HTTP 401)", action)
ee = fmt.Errorf(errStr)
default:
ee = err
}
return
......@@ -524,3 +550,12 @@ func warnExistenceOfRepo(project *Project, ee error) (err error) {
return
}
func absolute(endpoint string) string {
u, _ := url.Parse(endpoint)
if u.Scheme == "" {
u.Scheme = "https"
}
return u.String()
}
......@@ -3,12 +3,34 @@ package github
import (
"fmt"
"net/http"
"os"
"testing"
"github.com/bmizerany/assert"
"github.com/octokit/go-octokit/octokit"
)
func TestClient_newOctokitClient(t *testing.T) {
c := NewClient("github.com")
cc := c.newOctokitClient(nil)
assert.Equal(t, "https://api.github.com", cc.Endpoint.String())
c = NewClient("github.corporate.com")
cc = c.newOctokitClient(nil)
assert.Equal(t, "https://github.corporate.com", cc.Endpoint.String())
c = NewClient("http://github.corporate.com")
cc = c.newOctokitClient(nil)
assert.Equal(t, "http://github.corporate.com", cc.Endpoint.String())
os.Setenv("HUB_TEST_HOST", "http://127.0.0.1")
defer os.Setenv("HUB_TEST_HOST", "")
c = NewClient("github.corporate.com")
cc = c.newOctokitClient(nil)
assert.Equal(t, "http://127.0.0.1", cc.Endpoint.String())
assert.Equal(t, "github.corporate.com", cc.Header.Get("Host"))
}
func TestClient_FormatError(t *testing.T) {
e := &octokit.ResponseError{
Response: &http.Response{
......
......@@ -6,8 +6,16 @@ import (
"github.com/github/hub/git"
)
func LocalRepo() *GitHubRepo {
return &GitHubRepo{}
func LocalRepo() (repo *GitHubRepo, err error) {
repo = &GitHubRepo{}
_, err = git.Dir()
if err != nil {
err = fmt.Errorf("fatal: Not a git repository")
return
}
return
}
type GitHubRepo struct {
......@@ -123,10 +131,15 @@ func (r *GitHubRepo) RemoteBranchAndProject(owner string) (branch *Branch, proje
return
}
func (r *GitHubRepo) OriginRemote() (*Remote, error) {
return r.RemoteByName("origin")
}
func (r *GitHubRepo) MainProject() (project *Project, err error) {
origin, err := r.RemoteByName("origin")
origin, err := r.OriginRemote()
if err != nil {
err = fmt.Errorf("Aborted: the origin remote doesn't point to a GitHub repository.")
return
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册