Merge branch 'master' into issue-467

This commit is contained in:
Toshiya Doi 2020-02-26 15:37:32 +09:00
commit 8b31b283f5
11 changed files with 465 additions and 82 deletions

View file

@ -109,9 +109,9 @@ func issueList(cmd *cobra.Command, args []string) error {
return err
}
fmt.Fprintf(colorableErr(cmd), "\nIssues for %s\n\n", ghrepo.FullName(*baseRepo))
fmt.Fprintf(colorableErr(cmd), "\nIssues for %s\n\n", ghrepo.FullName(baseRepo))
issues, err := api.IssueList(apiClient, *baseRepo, state, labels, assignee, limit)
issues, err := api.IssueList(apiClient, baseRepo, state, labels, assignee, limit)
if err != nil {
return err
}
@ -169,7 +169,7 @@ func issueStatus(cmd *cobra.Command, args []string) error {
return err
}
issuePayload, err := api.IssueStatus(apiClient, *baseRepo, currentUser)
issuePayload, err := api.IssueStatus(apiClient, baseRepo, currentUser)
if err != nil {
return err
}
@ -177,7 +177,7 @@ func issueStatus(cmd *cobra.Command, args []string) error {
out := colorableOut(cmd)
fmt.Fprintln(out, "")
fmt.Fprintf(out, "Relevant issues in %s\n", ghrepo.FullName(*baseRepo))
fmt.Fprintf(out, "Relevant issues in %s\n", ghrepo.FullName(baseRepo))
fmt.Fprintln(out, "")
printHeader(out, "Issues assigned to you")
@ -221,7 +221,7 @@ func issueView(cmd *cobra.Command, args []string) error {
return err
}
issue, err := issueFromArg(apiClient, *baseRepo, args[0])
issue, err := issueFromArg(apiClient, baseRepo, args[0])
if err != nil {
return err
}
@ -294,8 +294,6 @@ func issueCreate(cmd *cobra.Command, args []string) error {
return err
}
fmt.Fprintf(colorableErr(cmd), "\nCreating issue in %s\n\n", ghrepo.FullName(*baseRepo))
baseOverride, err := cmd.Flags().GetString("repo")
if err != nil {
return err
@ -309,31 +307,6 @@ 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/issues/new", ghrepo.FullName(*baseRepo))
if len(templateFiles) > 1 {
openURL += "/choose"
}
cmd.Printf("Opening %s in your browser.\n", openURL)
return utils.OpenInBrowser(openURL)
}
apiClient, err := apiClientForContext(ctx)
if err != nil {
return err
}
repo, err := api.GitHubRepo(apiClient, *baseRepo)
if err != nil {
return err
}
if !repo.HasIssuesEnabled {
return fmt.Errorf("the '%s' repository has disabled issues", ghrepo.FullName(*baseRepo))
}
action := SubmitAction
title, err := cmd.Flags().GetString("title")
if err != nil {
return fmt.Errorf("could not parse title: %w", err)
@ -343,6 +316,39 @@ func issueCreate(cmd *cobra.Command, args []string) error {
return fmt.Errorf("could not parse body: %w", err)
}
if isWeb, err := cmd.Flags().GetBool("web"); err == nil && isWeb {
// TODO: move URL generation into GitHubRepository
openURL := fmt.Sprintf("https://github.com/%s/issues/new", ghrepo.FullName(baseRepo))
if title != "" || body != "" {
openURL += fmt.Sprintf(
"?title=%s&body=%s",
url.QueryEscape(title),
url.QueryEscape(body),
)
} else if len(templateFiles) > 1 {
openURL += "/choose"
}
cmd.Printf("Opening %s in your browser.\n", displayURL(openURL))
return utils.OpenInBrowser(openURL)
}
fmt.Fprintf(colorableErr(cmd), "\nCreating issue in %s\n\n", ghrepo.FullName(baseRepo))
apiClient, err := apiClientForContext(ctx)
if err != nil {
return err
}
repo, err := api.GitHubRepo(apiClient, baseRepo)
if err != nil {
return err
}
if !repo.HasIssuesEnabled {
return fmt.Errorf("the '%s' repository has disabled issues", ghrepo.FullName(baseRepo))
}
action := SubmitAction
interactive := title == "" || body == ""
if interactive {
@ -370,7 +376,7 @@ func issueCreate(cmd *cobra.Command, args []string) error {
if action == PreviewAction {
openURL := fmt.Sprintf(
"https://github.com/%s/issues/new/?title=%s&body=%s",
ghrepo.FullName(*baseRepo),
ghrepo.FullName(baseRepo),
url.QueryEscape(title),
url.QueryEscape(body),
)

View file

@ -7,6 +7,7 @@ import (
"os"
"os/exec"
"regexp"
"strings"
"testing"
"github.com/cli/cli/utils"
@ -492,5 +493,31 @@ func TestIssueCreate_web(t *testing.T) {
}
url := seenCmd.Args[len(seenCmd.Args)-1]
eq(t, url, "https://github.com/OWNER/REPO/issues/new")
eq(t, output.String(), "Opening https://github.com/OWNER/REPO/issues/new in your browser.\n")
eq(t, output.String(), "Opening github.com/OWNER/REPO/issues/new in your browser.\n")
eq(t, output.Stderr(), "")
}
func TestIssueCreate_webTitleBody(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
})
defer restoreCmd()
output, err := RunCommand(issueCreateCmd, `issue create -w -t mytitle -b mybody`)
if err != nil {
t.Errorf("error running command `issue create`: %v", err)
}
if seenCmd == nil {
t.Fatal("expected a command to run")
}
url := strings.ReplaceAll(seenCmd.Args[len(seenCmd.Args)-1], "^", "")
eq(t, url, "https://github.com/OWNER/REPO/issues/new?title=mytitle&body=mybody")
eq(t, output.String(), "Opening github.com/OWNER/REPO/issues/new in your browser.\n")
}

View file

@ -85,7 +85,7 @@ func prStatus(cmd *cobra.Command, args []string) error {
return err
}
prPayload, err := api.PullRequests(apiClient, *baseRepo, currentPRNumber, currentPRHeadRef, currentUser)
prPayload, err := api.PullRequests(apiClient, baseRepo, currentPRNumber, currentPRHeadRef, currentUser)
if err != nil {
return err
}
@ -93,7 +93,7 @@ func prStatus(cmd *cobra.Command, args []string) error {
out := colorableOut(cmd)
fmt.Fprintln(out, "")
fmt.Fprintf(out, "Relevant pull requests in %s\n", ghrepo.FullName(*baseRepo))
fmt.Fprintf(out, "Relevant pull requests in %s\n", ghrepo.FullName(baseRepo))
fmt.Fprintln(out, "")
printHeader(out, "Current branch")
@ -136,7 +136,7 @@ func prList(cmd *cobra.Command, args []string) error {
return err
}
fmt.Fprintf(colorableErr(cmd), "\nPull requests for %s\n\n", ghrepo.FullName(*baseRepo))
fmt.Fprintf(colorableErr(cmd), "\nPull requests for %s\n\n", ghrepo.FullName(baseRepo))
limit, err := cmd.Flags().GetInt("limit")
if err != nil {
@ -174,8 +174,8 @@ func prList(cmd *cobra.Command, args []string) error {
}
params := map[string]interface{}{
"owner": (*baseRepo).RepoOwner(),
"repo": (*baseRepo).RepoName(),
"owner": baseRepo.RepoOwner(),
"repo": baseRepo.RepoName(),
"state": graphqlState,
}
if len(labels) > 0 {
@ -214,7 +214,7 @@ func prList(cmd *cobra.Command, args []string) error {
if table.IsTTY() {
prNum = "#" + prNum
}
table.AddField(prNum, nil, colorFuncForState(pr.State))
table.AddField(prNum, nil, colorFuncForPR(pr))
table.AddField(replaceExcessiveWhitespace(pr.Title), nil, nil)
table.AddField(pr.HeadLabel(), nil, utils.Cyan)
table.EndRow()
@ -227,6 +227,14 @@ func prList(cmd *cobra.Command, args []string) error {
return nil
}
func colorFuncForPR(pr api.PullRequest) func(string) string {
if pr.State == "OPEN" && pr.IsDraft {
return utils.Gray
} else {
return colorFuncForState(pr.State)
}
}
func colorFuncForState(state string) func(string) string {
switch state {
case "OPEN":
@ -261,7 +269,7 @@ func prView(cmd *cobra.Command, args []string) error {
var openURL string
var pr *api.PullRequest
if len(args) > 0 {
pr, err = prFromArg(apiClient, *baseRepo, args[0])
pr, err = prFromArg(apiClient, baseRepo, args[0])
if err != nil {
return err
}
@ -273,15 +281,15 @@ func prView(cmd *cobra.Command, args []string) error {
}
if prNumber > 0 {
openURL = fmt.Sprintf("https://github.com/%s/pull/%d", ghrepo.FullName(*baseRepo), prNumber)
openURL = fmt.Sprintf("https://github.com/%s/pull/%d", ghrepo.FullName(baseRepo), prNumber)
if preview {
pr, err = api.PullRequestByNumber(apiClient, *baseRepo, prNumber)
pr, err = api.PullRequestByNumber(apiClient, baseRepo, prNumber)
if err != nil {
return err
}
}
} else {
pr, err = api.PullRequestForBranch(apiClient, *baseRepo, branchWithOwner)
pr, err = api.PullRequestForBranch(apiClient, baseRepo, branchWithOwner)
if err != nil {
return err
}
@ -385,14 +393,17 @@ func prSelectorForCurrentBranch(ctx context.Context) (prNumber int, prHeadRef st
func printPrs(w io.Writer, totalCount int, prs ...api.PullRequest) {
for _, pr := range prs {
prNumber := fmt.Sprintf("#%d", pr.Number)
if pr.State == "OPEN" {
fmt.Fprintf(w, " %s", utils.Green(prNumber))
prNumberColorFunc := utils.Green
if pr.IsDraft {
prNumberColorFunc = utils.Gray
} else if pr.State == "MERGED" {
fmt.Fprintf(w, " %s", utils.Magenta(prNumber))
prNumberColorFunc = utils.Magenta
} else if pr.State == "CLOSED" {
fmt.Fprintf(w, " %s", utils.Red(prNumber))
prNumberColorFunc = utils.Red
}
fmt.Fprintf(w, " %s %s", text.Truncate(50, replaceExcessiveWhitespace(pr.Title)), utils.Cyan("["+pr.HeadLabel()+"]"))
fmt.Fprintf(w, " %s %s %s", prNumberColorFunc(prNumber), text.Truncate(50, replaceExcessiveWhitespace(pr.Title)), utils.Cyan("["+pr.HeadLabel()+"]"))
checks := pr.ChecksStatus()
reviews := pr.ReviewStatus()

View file

@ -99,7 +99,9 @@ func prCreate(cmd *cobra.Command, _ []string) error {
if didForkRepo && pushTries < maxPushTries {
pushTries++
// first wait 2 seconds after forking, then 4s, then 6s
time.Sleep(time.Duration(2*pushTries) * time.Second)
waitSeconds := 2 * pushTries
fmt.Fprintf(cmd.ErrOrStderr(), "waiting %s before retrying...\n", utils.Pluralize(waitSeconds, "second"))
time.Sleep(time.Duration(waitSeconds) * time.Second)
continue
}
return err

87
command/repo.go Normal file
View file

@ -0,0 +1,87 @@
package command
import (
"fmt"
"os"
"strings"
"github.com/cli/cli/git"
"github.com/cli/cli/internal/ghrepo"
"github.com/cli/cli/utils"
"github.com/spf13/cobra"
)
func init() {
RootCmd.AddCommand(repoCmd)
repoCmd.AddCommand(repoCloneCmd)
repoCmd.AddCommand(repoViewCmd)
}
var repoCmd = &cobra.Command{
Use: "repo",
Short: "View repositories",
Long: `Work with GitHub repositories.
A repository can be supplied as an argument in any of the following formats:
- "OWNER/REPO"
- by URL, e.g. "https://github.com/OWNER/REPO"`,
}
var repoCloneCmd = &cobra.Command{
Use: "clone <repo>",
Args: cobra.MinimumNArgs(1),
Short: "Clone a repository locally",
Long: `Clone a GitHub repository locally.
To pass 'git clone' options, separate them with '--'.`,
RunE: repoClone,
}
var repoViewCmd = &cobra.Command{
Use: "view [<repo>]",
Short: "View a repository in the browser",
Long: `View a GitHub repository in the browser.
With no argument, the repository for the current directory is opened.`,
RunE: repoView,
}
func repoClone(cmd *cobra.Command, args []string) error {
cloneURL := args[0]
if !strings.Contains(cloneURL, ":") {
cloneURL = fmt.Sprintf("https://github.com/%s.git", cloneURL)
}
cloneArgs := []string{"clone"}
cloneArgs = append(cloneArgs, args[1:]...)
cloneArgs = append(cloneArgs, cloneURL)
cloneCmd := git.GitCommand(cloneArgs...)
cloneCmd.Stdin = os.Stdin
cloneCmd.Stdout = os.Stdout
cloneCmd.Stderr = os.Stderr
return utils.PrepareCmd(cloneCmd).Run()
}
func repoView(cmd *cobra.Command, args []string) error {
ctx := contextForCommand(cmd)
var openURL string
if len(args) == 0 {
baseRepo, err := determineBaseRepo(cmd, ctx)
if err != nil {
return err
}
openURL = fmt.Sprintf("https://github.com/%s", ghrepo.FullName(baseRepo))
} else {
repoArg := args[0]
if strings.HasPrefix(repoArg, "http:/") || strings.HasPrefix(repoArg, "https:/") {
openURL = repoArg
} else {
openURL = fmt.Sprintf("https://github.com/%s", repoArg)
}
}
fmt.Fprintf(cmd.ErrOrStderr(), "Opening %s in your browser.\n", displayURL(openURL))
return utils.OpenInBrowser(openURL)
}

149
command/repo_test.go Normal file
View file

@ -0,0 +1,149 @@
package command
import (
"os/exec"
"strings"
"testing"
"github.com/cli/cli/context"
"github.com/cli/cli/utils"
)
func TestRepoClone(t *testing.T) {
tests := []struct {
name string
args string
want string
}{
{
name: "shorthand",
args: "repo clone OWNER/REPO",
want: "git clone https://github.com/OWNER/REPO.git",
},
{
name: "clone arguments",
args: "repo clone OWNER/REPO -- -o upstream --depth 1",
want: "git clone -o upstream --depth 1 https://github.com/OWNER/REPO.git",
},
{
name: "HTTPS URL",
args: "repo clone https://github.com/OWNER/REPO",
want: "git clone https://github.com/OWNER/REPO",
},
{
name: "SSH URL",
args: "repo clone git@github.com:OWNER/REPO.git",
want: "git clone git@github.com:OWNER/REPO.git",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
})
defer restoreCmd()
output, err := RunCommand(repoViewCmd, tt.args)
if err != nil {
t.Fatalf("error running command `repo clone`: %v", err)
}
eq(t, output.String(), "")
eq(t, output.Stderr(), "")
if seenCmd == nil {
t.Fatal("expected a command to run")
}
eq(t, strings.Join(seenCmd.Args, " "), tt.want)
})
}
}
func TestRepoView(t *testing.T) {
initBlankContext("OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
})
defer restoreCmd()
output, err := RunCommand(repoViewCmd, "repo view")
if err != nil {
t.Errorf("error running command `repo view`: %v", err)
}
eq(t, output.String(), "")
eq(t, output.Stderr(), "Opening github.com/OWNER/REPO in your browser.\n")
if seenCmd == nil {
t.Fatal("expected a command to run")
}
url := seenCmd.Args[len(seenCmd.Args)-1]
eq(t, url, "https://github.com/OWNER/REPO")
}
func TestRepoView_ownerRepo(t *testing.T) {
ctx := context.NewBlank()
ctx.SetBranch("master")
initContext = func() context.Context {
return ctx
}
initFakeHTTP()
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
})
defer restoreCmd()
output, err := RunCommand(repoViewCmd, "repo view cli/cli")
if err != nil {
t.Errorf("error running command `repo view`: %v", err)
}
eq(t, output.String(), "")
eq(t, output.Stderr(), "Opening github.com/cli/cli in your browser.\n")
if seenCmd == nil {
t.Fatal("expected a command to run")
}
url := seenCmd.Args[len(seenCmd.Args)-1]
eq(t, url, "https://github.com/cli/cli")
}
func TestRepoView_fullURL(t *testing.T) {
ctx := context.NewBlank()
ctx.SetBranch("master")
initContext = func() context.Context {
return ctx
}
initFakeHTTP()
var seenCmd *exec.Cmd
restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable {
seenCmd = cmd
return &outputStub{}
})
defer restoreCmd()
output, err := RunCommand(repoViewCmd, "repo view https://github.com/cli/cli")
if err != nil {
t.Errorf("error running command `repo view`: %v", err)
}
eq(t, output.String(), "")
eq(t, output.Stderr(), "Opening github.com/cli/cli in your browser.\n")
if seenCmd == nil {
t.Fatal("expected a command to run")
}
url := seenCmd.Args[len(seenCmd.Args)-1]
eq(t, url, "https://github.com/cli/cli")
}

View file

@ -171,7 +171,7 @@ func changelogURL(version string) string {
return url
}
func determineBaseRepo(cmd *cobra.Command, ctx context.Context) (*ghrepo.Interface, error) {
func determineBaseRepo(cmd *cobra.Command, ctx context.Context) (ghrepo.Interface, error) {
apiClient, err := apiClientForContext(ctx)
if err != nil {
return nil, err
@ -192,11 +192,10 @@ func determineBaseRepo(cmd *cobra.Command, ctx context.Context) (*ghrepo.Interfa
return nil, err
}
var baseRepo ghrepo.Interface
baseRepo, err = repoContext.BaseRepo()
baseRepo, err := repoContext.BaseRepo()
if err != nil {
return nil, err
}
return &baseRepo, nil
return baseRepo, nil
}