Merge remote-tracking branch 'origin/master' into no-errors-wrap

This commit is contained in:
Mislav Marohnić 2020-01-23 16:46:56 +01:00
commit b44dad2319
26 changed files with 983 additions and 296 deletions

View file

@ -11,8 +11,8 @@ import (
"time"
"github.com/github/gh-cli/api"
"github.com/github/gh-cli/context"
"github.com/github/gh-cli/git"
"github.com/github/gh-cli/internal/ghrepo"
"github.com/github/gh-cli/pkg/githubtemplate"
"github.com/github/gh-cli/utils"
"github.com/spf13/cobra"
@ -47,7 +47,7 @@ var issueCmd = &cobra.Command{
An issue can be supplied as argument in any of the following formats:
- by number, e.g. "123"; or
- by URL, e.g. "https://github.com/<owner>/<repo>/issues/123".`,
- by URL, e.g. "https://github.com/OWNER/REPO/issues/123".`,
}
var issueCreateCmd = &cobra.Command{
Use: "create",
@ -68,7 +68,7 @@ var issueViewCmd = &cobra.Command{
Use: "view {<number> | <url> | <branch>}",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("requires an issue number as an argument")
return FlagError{errors.New("issue required as argument")}
}
return nil
},
@ -108,7 +108,7 @@ func issueList(cmd *cobra.Command, args []string) error {
return err
}
fmt.Fprintf(colorableErr(cmd), "\nIssues for %s/%s\n\n", baseRepo.RepoOwner(), baseRepo.RepoName())
fmt.Fprintf(colorableErr(cmd), "\nIssues for %s\n\n", ghrepo.FullName(baseRepo))
issues, err := api.IssueList(apiClient, baseRepo, state, labels, assignee, limit)
if err != nil {
@ -258,7 +258,7 @@ func printIssuePreview(out io.Writer, issue *api.Issue) {
var issueURLRE = regexp.MustCompile(`^https://github\.com/([^/]+)/([^/]+)/issues/(\d+)`)
func issueFromArg(apiClient *api.Client, baseRepo context.GitHubRepository, arg string) (*api.Issue, error) {
func issueFromArg(apiClient *api.Client, baseRepo ghrepo.Interface, arg string) (*api.Issue, error) {
if issueNumber, err := strconv.Atoi(arg); err == nil {
return api.IssueByNumber(apiClient, baseRepo, issueNumber)
}
@ -279,7 +279,7 @@ func issueCreate(cmd *cobra.Command, args []string) error {
return err
}
fmt.Fprintf(colorableErr(cmd), "\nCreating issue in %s/%s\n\n", baseRepo.RepoOwner(), baseRepo.RepoName())
fmt.Fprintf(colorableErr(cmd), "\nCreating issue in %s\n\n", ghrepo.FullName(baseRepo))
var templateFiles []string
if rootDir, err := git.ToplevelDir(); err == nil {
@ -289,7 +289,7 @@ func issueCreate(cmd *cobra.Command, args []string) error {
if isWeb, err := cmd.Flags().GetBool("web"); err == nil && isWeb {
// TODO: move URL generation into GitHubRepository
openURL := fmt.Sprintf("https://github.com/%s/%s/issues/new", baseRepo.RepoOwner(), baseRepo.RepoName())
openURL := fmt.Sprintf("https://github.com/%s/issues/new", ghrepo.FullName(baseRepo))
if len(templateFiles) > 1 {
openURL += "/choose"
}
@ -307,7 +307,7 @@ func issueCreate(cmd *cobra.Command, args []string) error {
return err
}
if !repo.HasIssuesEnabled {
return fmt.Errorf("the '%s/%s' repository has disabled issues", baseRepo.RepoOwner(), baseRepo.RepoName())
return fmt.Errorf("the '%s' repository has disabled issues", ghrepo.FullName(baseRepo))
}
action := SubmitAction
@ -347,18 +347,13 @@ func issueCreate(cmd *cobra.Command, args []string) error {
if action == PreviewAction {
openURL := fmt.Sprintf(
"https://github.com/%s/%s/issues/new/?title=%s&body=%s",
baseRepo.RepoOwner(),
baseRepo.RepoName(),
"https://github.com/%s/issues/new/?title=%s&body=%s",
ghrepo.FullName(baseRepo),
url.QueryEscape(title),
url.QueryEscape(body),
)
// TODO could exceed max url length for explorer
url, err := url.Parse(openURL)
if err != nil {
return err
}
fmt.Fprintf(cmd.ErrOrStderr(), "Opening %s%s in your browser.\n", url.Host, url.Path)
fmt.Fprintf(cmd.ErrOrStderr(), "Opening %s in your browser.\n", displayURL(openURL))
return utils.OpenInBrowser(openURL)
} else if action == SubmitAction {
params := map[string]interface{}{
@ -417,3 +412,11 @@ func labelList(issue api.Issue) string {
}
return list
}
func displayURL(urlStr string) string {
u, err := url.Parse(urlStr)
if err != nil {
return urlStr
}
return u.Hostname() + u.Path
}

View file

@ -203,7 +203,7 @@ func TestIssueView(t *testing.T) {
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": { "issue": {
{ "data": { "repository": { "hasIssuesEnabled": true, "issue": {
"number": 123,
"url": "https://github.com/OWNER/REPO/issues/123"
} } } }
@ -236,7 +236,7 @@ func TestIssueView_preview(t *testing.T) {
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": { "issue": {
{ "data": { "repository": { "hasIssuesEnabled": true, "issue": {
"number": 123,
"body": "**bold story**",
"title": "ix of coins",
@ -303,12 +303,29 @@ func TestIssueView_notFound(t *testing.T) {
}
}
func TestIssueView_disabledIssues(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": {
"id": "REPOID",
"hasIssuesEnabled": false
} } }
`))
_, err := RunCommand(issueViewCmd, `issue view 6666`)
if err == nil || err.Error() != "the 'OWNER/REPO' repository has disabled issues" {
t.Errorf("error running command `issue view`: %v", err)
}
}
func TestIssueView_urlArg(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": { "issue": {
{ "data": { "repository": { "hasIssuesEnabled": true, "issue": {
"number": 123,
"url": "https://github.com/OWNER/REPO/issues/123"
} } } }

View file

@ -13,6 +13,7 @@ import (
"github.com/github/gh-cli/api"
"github.com/github/gh-cli/context"
"github.com/github/gh-cli/git"
"github.com/github/gh-cli/internal/ghrepo"
"github.com/github/gh-cli/utils"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
@ -42,8 +43,8 @@ var prCmd = &cobra.Command{
A pull request can be supplied as argument in any of the following formats:
- by number, e.g. "123";
- by URL, e.g. "https://github.com/<owner>/<repo>/pull/123"; or
- by the name of its head branch, e.g. "patch-1" or "<owner>:patch-1".`,
- by URL, e.g. "https://github.com/OWNER/REPO/pull/123"; or
- by the name of its head branch, e.g. "patch-1" or "OWNER:patch-1".`,
}
var prCheckoutCmd = &cobra.Command{
Use: "checkout {<number> | <url> | <branch>}",
@ -67,9 +68,13 @@ var prStatusCmd = &cobra.Command{
RunE: prStatus,
}
var prViewCmd = &cobra.Command{
Use: "view {<number> | <url> | <branch>}",
Use: "view [{<number> | <url> | <branch>}]",
Short: "View a pull request in the browser",
RunE: prView,
Long: `View a pull request specified by the argument in the browser.
Without an argument, the pull request that belongs to the current
branch is opened.`,
RunE: prView,
}
func prStatus(cmd *cobra.Command, args []string) error {
@ -139,7 +144,7 @@ func prList(cmd *cobra.Command, args []string) error {
return err
}
fmt.Fprintf(colorableErr(cmd), "\nPull requests for %s/%s\n\n", baseRepo.RepoOwner(), baseRepo.RepoName())
fmt.Fprintf(colorableErr(cmd), "\nPull requests for %s\n\n", ghrepo.FullName(baseRepo))
limit, err := cmd.Flags().GetInt("limit")
if err != nil {
@ -275,7 +280,7 @@ func prView(cmd *cobra.Command, args []string) error {
}
if prNumber > 0 {
openURL = fmt.Sprintf("https://github.com/%s/%s/pull/%d", baseRepo.RepoOwner(), baseRepo.RepoName(), prNumber)
openURL = fmt.Sprintf("https://github.com/%s/pull/%d", ghrepo.FullName(baseRepo), prNumber)
if preview {
pr, err = api.PullRequestByNumber(apiClient, baseRepo, prNumber)
if err != nil {
@ -285,10 +290,6 @@ func prView(cmd *cobra.Command, args []string) error {
} else {
pr, err = api.PullRequestForBranch(apiClient, baseRepo, branchWithOwner)
if err != nil {
var notFoundErr *api.NotFoundError
if errors.As(err, &notFoundErr) {
return fmt.Errorf("%s. To open a specific pull request use the pull request's number as an argument", err)
}
return err
}
@ -323,7 +324,7 @@ func printPrPreview(out io.Writer, pr *api.PullRequest) {
var prURLRE = regexp.MustCompile(`^https://github\.com/([^/]+)/([^/]+)/pull/(\d+)`)
func prFromArg(apiClient *api.Client, baseRepo context.GitHubRepository, arg string) (*api.PullRequest, error) {
func prFromArg(apiClient *api.Client, baseRepo ghrepo.Interface, arg string) (*api.PullRequest, error) {
if prNumber, err := strconv.Atoi(arg); err == nil {
return api.PullRequestByNumber(apiClient, baseRepo, prNumber)
}
@ -357,7 +358,7 @@ func prSelectorForCurrentBranch(ctx context.Context) (prNumber int, prHeadRef st
var branchOwner string
if branchConfig.RemoteURL != nil {
// the branch merges from a remote specified by URL
if r, err := context.RepoFromURL(branchConfig.RemoteURL); err == nil {
if r, err := ghrepo.FromURL(branchConfig.RemoteURL); err == nil {
branchOwner = r.RepoOwner()
}
} else if branchConfig.RemoteName != "" {

View file

@ -1,12 +1,16 @@
package command
import (
"errors"
"fmt"
"net/url"
"sort"
"time"
"github.com/github/gh-cli/api"
"github.com/github/gh-cli/context"
"github.com/github/gh-cli/git"
"github.com/github/gh-cli/internal/ghrepo"
"github.com/github/gh-cli/pkg/githubtemplate"
"github.com/github/gh-cli/utils"
"github.com/spf13/cobra"
@ -14,43 +18,95 @@ import (
func prCreate(cmd *cobra.Command, _ []string) error {
ctx := contextForCommand(cmd)
ucc, err := git.UncommittedChangeCount()
remotes, err := ctx.Remotes()
if err != nil {
return err
}
if ucc > 0 {
client, err := apiClientForContext(ctx)
if err != nil {
return fmt.Errorf("could not initialize API client: %w", err)
}
baseRepoOverride, _ := cmd.Flags().GetString("repo")
repoContext, err := resolveRemotesToRepos(remotes, client, baseRepoOverride)
if err != nil {
return err
}
baseRepo, err := repoContext.BaseRepo()
if err != nil {
return fmt.Errorf("could not determine base repository: %w", err)
}
headBranch, err := ctx.Branch()
if err != nil {
return fmt.Errorf("could not determine the current branch: %w", err)
}
baseBranch, err := cmd.Flags().GetString("base")
if err != nil {
return err
}
if baseBranch == "" {
baseBranch = baseRepo.DefaultBranchRef.Name
}
didForkRepo := false
var headRemote *context.Remote
headRepo, err := repoContext.HeadRepo()
if err != nil {
if baseRepo.IsPrivate {
return fmt.Errorf("cannot write to private repository '%s'", ghrepo.FullName(baseRepo))
}
headRepo, err = api.ForkRepo(client, baseRepo)
if err != nil {
return fmt.Errorf("error forking repo: %w", err)
}
didForkRepo = true
// TODO: support non-HTTPS git remote URLs
baseRepoURL := fmt.Sprintf("https://github.com/%s.git", ghrepo.FullName(baseRepo))
headRepoURL := fmt.Sprintf("https://github.com/%s.git", ghrepo.FullName(headRepo))
// TODO: figure out what to name the new git remote
gitRemote, err := git.AddRemote("fork", baseRepoURL, headRepoURL)
if err != nil {
return fmt.Errorf("error adding remote: %w", err)
}
headRemote = &context.Remote{
Remote: gitRemote,
Owner: headRepo.RepoOwner(),
Repo: headRepo.RepoName(),
}
}
if headBranch == baseBranch && ghrepo.IsSame(baseRepo, headRepo) {
return fmt.Errorf("must be on a branch named differently than %q", baseBranch)
}
if headRemote == nil {
headRemote, err = repoContext.RemoteForRepo(headRepo)
if err != nil {
return fmt.Errorf("git remote not found for head repository: %w", err)
}
}
if ucc, err := git.UncommittedChangeCount(); err == nil && ucc > 0 {
fmt.Fprintf(cmd.ErrOrStderr(), "Warning: %s\n", utils.Pluralize(ucc, "uncommitted change"))
}
repo, err := ctx.BaseRepo()
if err != nil {
return fmt.Errorf("could not determine GitHub repo: %w", err)
}
head, err := ctx.Branch()
if err != nil {
return fmt.Errorf("could not determine current branch: %w", err)
}
remote, err := guessRemote(ctx)
if err != nil {
return err
}
target, err := cmd.Flags().GetString("base")
if err != nil {
return err
}
if target == "" {
// TODO use default branch
target = "master"
}
fmt.Fprintf(colorableErr(cmd), "\nCreating pull request for %s into %s in %s/%s\n\n", utils.Cyan(head), utils.Cyan(target), repo.RepoOwner(), repo.RepoName())
if err = git.Push(remote, fmt.Sprintf("HEAD:%s", head)); err != nil {
return err
pushTries := 0
maxPushTries := 3
for {
// TODO: respect existing upstream configuration of the current branch
if err := git.Push(headRemote.Name, fmt.Sprintf("HEAD:%s", headBranch)); err != nil {
if didForkRepo && pushTries < maxPushTries {
pushTries++
// first wait 2 seconds after forking, then 4s, then 6s
time.Sleep(time.Duration(2*pushTries) * time.Second)
continue
}
return err
}
break
}
isWeb, err := cmd.Flags().GetBool("web")
@ -58,11 +114,20 @@ func prCreate(cmd *cobra.Command, _ []string) error {
return fmt.Errorf("could not parse web: %q", err)
}
if isWeb {
openURL := fmt.Sprintf(`https://github.com/%s/%s/pull/%s`, repo.RepoOwner(), repo.RepoName(), head)
openURL := fmt.Sprintf(`https://github.com/%s/pull/%s`, ghrepo.FullName(headRepo), headBranch)
fmt.Fprintf(cmd.ErrOrStderr(), "Opening %s in your browser.\n", openURL)
return utils.OpenInBrowser(openURL)
}
headBranchLabel := headBranch
if !ghrepo.IsSame(baseRepo, headRepo) {
headBranchLabel = fmt.Sprintf("%s:%s", headRepo.RepoOwner(), headBranch)
}
fmt.Fprintf(colorableErr(cmd), "\nCreating pull request for %s into %s in %s\n\n",
utils.Cyan(headBranchLabel),
utils.Cyan(baseBranch),
ghrepo.FullName(baseRepo))
title, err := cmd.Flags().GetString("title")
if err != nil {
return fmt.Errorf("could not parse title: %w", err)
@ -103,20 +168,6 @@ func prCreate(cmd *cobra.Command, _ []string) error {
}
}
base, err := cmd.Flags().GetString("base")
if err != nil {
return fmt.Errorf("could not parse base: %w", err)
}
if base == "" {
// TODO: use default branch for the repo
base = "master"
}
client, err := apiClientForContext(ctx)
if err != nil {
return fmt.Errorf("could not initialize API client: %w", err)
}
isDraft, err := cmd.Flags().GetBool("draft")
if err != nil {
return fmt.Errorf("could not parse draft: %w", err)
@ -127,11 +178,11 @@ func prCreate(cmd *cobra.Command, _ []string) error {
"title": title,
"body": body,
"draft": isDraft,
"baseRefName": base,
"headRefName": head,
"baseRefName": baseBranch,
"headRefName": headBranch,
}
pr, err := api.CreatePullRequest(client, repo, params)
pr, err := api.CreatePullRequest(client, baseRepo, params)
if err != nil {
return fmt.Errorf("failed to create pull request: %w", err)
}
@ -139,20 +190,15 @@ func prCreate(cmd *cobra.Command, _ []string) error {
fmt.Fprintln(cmd.OutOrStdout(), pr.URL)
} else if action == PreviewAction {
openURL := fmt.Sprintf(
"https://github.com/%s/%s/compare/%s...%s?expand=1&title=%s&body=%s",
repo.RepoOwner(),
repo.RepoName(),
target,
head,
"https://github.com/%s/compare/%s...%s?expand=1&title=%s&body=%s",
ghrepo.FullName(baseRepo),
baseBranch,
headBranchLabel,
url.QueryEscape(title),
url.QueryEscape(body),
)
// TODO could exceed max url length for explorer
url, err := url.Parse(openURL)
if err != nil {
return err
}
fmt.Fprintf(cmd.ErrOrStderr(), "Opening %s%s in your browser.\n", url.Host, url.Path)
fmt.Fprintf(cmd.ErrOrStderr(), "Opening %s in your browser.\n", displayURL(openURL))
return utils.OpenInBrowser(openURL)
} else {
panic("Unreachable state")
@ -162,22 +208,6 @@ func prCreate(cmd *cobra.Command, _ []string) error {
}
func guessRemote(ctx context.Context) (string, error) {
remotes, err := ctx.Remotes()
if err != nil {
return "", fmt.Errorf("could not read git remotes: %w", err)
}
// TODO: consolidate logic with fsContext.BaseRepo
// TODO: check if the GH repo that the remote points to is writeable
remote, err := remotes.FindByName("upstream", "github", "origin", "*")
if err != nil {
return "", fmt.Errorf("could not determine suitable remote: %w", err)
}
return remote.Name, nil
}
var prCreateCmd = &cobra.Command{
Use: "create",
Short: "Create a pull request",
@ -195,3 +225,97 @@ func init() {
"The branch into which you want your code merged")
prCreateCmd.Flags().BoolP("web", "w", false, "Open the web browser to create a pull request")
}
// cap the number of git remotes looked up, since the user might have an
// unusally large number of git remotes
const maxRemotesForLookup = 5
func resolveRemotesToRepos(remotes context.Remotes, client *api.Client, base string) (resolvedRemotes, error) {
sort.Stable(remotes)
lenRemotesForLookup := len(remotes)
if lenRemotesForLookup > maxRemotesForLookup {
lenRemotesForLookup = maxRemotesForLookup
}
hasBaseOverride := base != ""
baseOverride := ghrepo.FromFullName(base)
foundBaseOverride := false
repos := []ghrepo.Interface{}
for _, r := range remotes[:lenRemotesForLookup] {
repos = append(repos, r)
if ghrepo.IsSame(r, baseOverride) {
foundBaseOverride = true
}
}
if hasBaseOverride && !foundBaseOverride {
// additionally, look up the explicitly specified base repo if it's not
// already covered by git remotes
repos = append(repos, baseOverride)
}
result := resolvedRemotes{remotes: remotes}
if hasBaseOverride {
result.baseOverride = baseOverride
}
networkResult, err := api.RepoNetwork(client, repos)
if err != nil {
return result, err
}
result.network = networkResult
return result, nil
}
type resolvedRemotes struct {
baseOverride ghrepo.Interface
remotes context.Remotes
network api.RepoNetworkResult
}
// BaseRepo is the first found repository in the "upstream", "github", "origin"
// git remote order, resolved to the parent repo if the git remote points to a fork
func (r resolvedRemotes) BaseRepo() (*api.Repository, error) {
if r.baseOverride != nil {
for _, repo := range r.network.Repositories {
if repo != nil && ghrepo.IsSame(repo, r.baseOverride) {
return repo, nil
}
}
return nil, fmt.Errorf("failed looking up information about the '%s' repository",
ghrepo.FullName(r.baseOverride))
}
for _, repo := range r.network.Repositories {
if repo == nil {
continue
}
if repo.IsFork() {
return repo.Parent, nil
}
return repo, nil
}
return nil, errors.New("not found")
}
// HeadRepo is the first found repository that has push access
func (r resolvedRemotes) HeadRepo() (*api.Repository, error) {
for _, repo := range r.network.Repositories {
if repo != nil && repo.ViewerCanPush() {
return repo, nil
}
}
return nil, errors.New("none of the repositories have push access")
}
// RemoteForRepo finds the git remote that points to a repository
func (r resolvedRemotes) RemoteForRepo(repo ghrepo.Interface) (*context.Remote, error) {
for i, remote := range r.remotes {
if ghrepo.IsSame(remote, repo) ||
// additionally, look up the resolved repository name in case this
// git remote points to this repository via a redirect
(r.network.Repositories[i] != nil && ghrepo.IsSame(r.network.Repositories[i], repo)) {
return remote, nil
}
}
return nil, errors.New("not found")
}

View file

@ -10,8 +10,10 @@ import (
"strings"
"testing"
"github.com/github/gh-cli/api"
"github.com/github/gh-cli/context"
"github.com/github/gh-cli/git"
"github.com/github/gh-cli/internal/ghrepo"
"github.com/github/gh-cli/test"
"github.com/github/gh-cli/utils"
)
@ -52,8 +54,15 @@ func TestPRCreate(t *testing.T) {
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": {
"id": "REPOID"
{ "data": { "repo_000": {
"id": "REPOID",
"name": "REPO",
"owner": {"login": "OWNER"},
"defaultBranchRef": {
"name": "master",
"target": {"oid": "deadbeef"}
},
"viewerPermission": "WRITE"
} } }
`))
http.StubResponse(200, bytes.NewBufferString(`
@ -103,7 +112,20 @@ func TestPRCreate_web(t *testing.T) {
initContext = func() context.Context {
return ctx
}
initFakeHTTP()
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repo_000": {
"id": "REPOID",
"name": "REPO",
"owner": {"login": "OWNER"},
"defaultBranchRef": {
"name": "master",
"target": {"oid": "deadbeef"}
},
"viewerPermission": "WRITE"
} } }
`))
ranCommands := [][]string{}
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
@ -116,11 +138,7 @@ func TestPRCreate_web(t *testing.T) {
eq(t, err, nil)
eq(t, output.String(), "")
eq(t, output.Stderr(), `
Creating pull request for feature into master in OWNER/REPO
Opening https://github.com/OWNER/REPO/pull/feature in your browser.
`)
eq(t, output.Stderr(), "Opening https://github.com/OWNER/REPO/pull/feature in your browser.\n")
eq(t, len(ranCommands), 3)
eq(t, strings.Join(ranCommands[1], " "), "git push --set-upstream origin HEAD:feature")
@ -139,8 +157,15 @@ func TestPRCreate_ReportsUncommittedChanges(t *testing.T) {
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": {
"id": "REPOID"
{ "data": { "repo_000": {
"id": "REPOID",
"name": "REPO",
"owner": {"login": "OWNER"},
"defaultBranchRef": {
"name": "master",
"target": {"oid": "deadbeef"}
},
"viewerPermission": "WRITE"
} } }
`))
http.StubResponse(200, bytes.NewBufferString(`
@ -165,3 +190,111 @@ Creating pull request for feature into master in OWNER/REPO
`)
}
func Test_resolvedRemotes_clonedFork(t *testing.T) {
resolved := resolvedRemotes{
baseOverride: nil,
remotes: context.Remotes{
&context.Remote{
Remote: &git.Remote{Name: "origin"},
Owner: "OWNER",
Repo: "REPO",
},
},
network: api.RepoNetworkResult{
Repositories: []*api.Repository{
&api.Repository{
Name: "REPO",
Owner: api.RepositoryOwner{Login: "OWNER"},
ViewerPermission: "ADMIN",
Parent: &api.Repository{
Name: "REPO",
Owner: api.RepositoryOwner{Login: "PARENTOWNER"},
ViewerPermission: "READ",
},
},
},
},
}
baseRepo, err := resolved.BaseRepo()
if err != nil {
t.Fatalf("got %v", err)
}
eq(t, ghrepo.FullName(baseRepo), "PARENTOWNER/REPO")
baseRemote, err := resolved.RemoteForRepo(baseRepo)
if baseRemote != nil || err == nil {
t.Error("did not expect any remote for base")
}
headRepo, err := resolved.HeadRepo()
if err != nil {
t.Fatalf("got %v", err)
}
eq(t, ghrepo.FullName(headRepo), "OWNER/REPO")
headRemote, err := resolved.RemoteForRepo(headRepo)
if err != nil {
t.Fatalf("got %v", err)
}
if headRemote.Name != "origin" {
t.Errorf("got remote %q", headRemote.Name)
}
}
func Test_resolvedRemotes_triangularSetup(t *testing.T) {
resolved := resolvedRemotes{
baseOverride: nil,
remotes: context.Remotes{
&context.Remote{
Remote: &git.Remote{Name: "origin"},
Owner: "OWNER",
Repo: "REPO",
},
&context.Remote{
Remote: &git.Remote{Name: "fork"},
Owner: "MYSELF",
Repo: "REPO",
},
},
network: api.RepoNetworkResult{
Repositories: []*api.Repository{
&api.Repository{
Name: "NEWNAME",
Owner: api.RepositoryOwner{Login: "NEWOWNER"},
ViewerPermission: "READ",
},
&api.Repository{
Name: "REPO",
Owner: api.RepositoryOwner{Login: "MYSELF"},
ViewerPermission: "ADMIN",
},
},
},
}
baseRepo, err := resolved.BaseRepo()
if err != nil {
t.Fatalf("got %v", err)
}
eq(t, ghrepo.FullName(baseRepo), "NEWOWNER/NEWNAME")
baseRemote, err := resolved.RemoteForRepo(baseRepo)
if err != nil {
t.Fatalf("got %v", err)
}
if baseRemote.Name != "origin" {
t.Errorf("got remote %q", baseRemote.Name)
}
headRepo, err := resolved.HeadRepo()
if err != nil {
t.Fatalf("got %v", err)
}
eq(t, ghrepo.FullName(headRepo), "MYSELF/REPO")
headRemote, err := resolved.RemoteForRepo(headRepo)
if err != nil {
t.Fatalf("got %v", err)
}
if headRemote.Name != "fork" {
t.Errorf("got remote %q", headRemote.Name)
}
}

View file

@ -359,7 +359,7 @@ func TestPRView_noResultsForBranch(t *testing.T) {
defer restoreCmd()
_, err := RunCommand(prViewCmd, "pr view")
if err == nil || err.Error() != `no open pull requests found for branch "blueberries". To open a specific pull request use the pull request's number as an argument` {
if err == nil || err.Error() != `no open pull requests found for branch "blueberries"` {
t.Errorf("error running command `pr view`: %v", err)
}

View file

@ -35,13 +35,21 @@ func init() {
// RootCmd.PersistentFlags().BoolP("verbose", "V", false, "enable verbose output")
RootCmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
return FlagError{err}
return &FlagError{Err: err}
})
}
// FlagError is the kind of error raised in flag processing
type FlagError struct {
error
Err error
}
func (fe FlagError) Error() string {
return fe.Err.Error()
}
func (fe FlagError) Unwrap() error {
return fe.Err
}
// RootCmd is the entry point of command-line execution
@ -84,7 +92,7 @@ func BasicClient() (*api.Client, error) {
opts = append(opts, api.AddHeader("Authorization", fmt.Sprintf("token %s", c.Token)))
}
if verbose := os.Getenv("DEBUG"); verbose != "" {
opts = append(opts, api.VerboseLog(os.Stderr))
opts = append(opts, api.VerboseLog(os.Stderr, false))
}
return api.NewClient(opts...), nil
}
@ -93,7 +101,6 @@ func contextForCommand(cmd *cobra.Command) context.Context {
ctx := initContext()
if repo, err := cmd.Flags().GetString("repo"); err == nil && repo != "" {
ctx.SetBaseRepo(repo)
ctx.SetBranch("master")
}
return ctx
}
@ -113,7 +120,7 @@ var apiClientForContext = func(ctx context.Context) (*api.Client, error) {
api.AddHeader("GraphQL-Features", "pe_mobile"),
}
if verbose := os.Getenv("DEBUG"); verbose != "" {
opts = append(opts, api.VerboseLog(os.Stderr))
opts = append(opts, api.VerboseLog(os.Stderr, strings.Contains(verbose, "api")))
}
return api.NewClient(opts...), nil
}