From aa07c5366eebc5498f4993afda7e6a26ebb5545f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Thu, 5 Mar 2020 09:33:49 +0100 Subject: [PATCH 01/38] Add support for PowerShell completion --- command/completion.go | 2 ++ command/completion_test.go | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/command/completion.go b/command/completion.go index d225082ef..866a9c3cf 100644 --- a/command/completion.go +++ b/command/completion.go @@ -36,6 +36,8 @@ When installing with Homebrew, see https://docs.brew.sh/Shell-Completion return RootCmd.GenBashCompletion(cmd.OutOrStdout()) case "zsh": return RootCmd.GenZshCompletion(cmd.OutOrStdout()) + case "powershell": + return RootCmd.GenPowerShellCompletion(cmd.OutOrStdout()) case "fish": return cobrafish.GenCompletion(RootCmd, cmd.OutOrStdout()) default: diff --git a/command/completion_test.go b/command/completion_test.go index e8a15db56..49ca8c4db 100644 --- a/command/completion_test.go +++ b/command/completion_test.go @@ -38,6 +38,17 @@ func TestCompletion_fish(t *testing.T) { } } +func TestCompletion_powerShell(t *testing.T) { + output, err := RunCommand(completionCmd, `completion -s powershell`) + if err != nil { + t.Fatal(err) + } + + if !strings.Contains(output.String(), "Register-ArgumentCompleter") { + t.Errorf("problem in fish completion:\n%s", output) + } +} + func TestCompletion_unsupported(t *testing.T) { _, err := RunCommand(completionCmd, `completion -s csh`) if err == nil || err.Error() != `unsupported shell type "csh"` { From fa595596de2bd4f3fcb1cd51a029dff6311150eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Thu, 5 Mar 2020 09:34:05 +0100 Subject: [PATCH 02/38] Improve `completion` docs --- command/completion.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/command/completion.go b/command/completion.go index 866a9c3cf..94c9ccf5d 100644 --- a/command/completion.go +++ b/command/completion.go @@ -9,21 +9,21 @@ import ( func init() { RootCmd.AddCommand(completionCmd) - completionCmd.Flags().StringP("shell", "s", "bash", "The type of shell") + completionCmd.Flags().StringP("shell", "s", "bash", "Shell type: {bash|zsh|fish|powershell}") } var completionCmd = &cobra.Command{ - Use: "completion", - Hidden: true, - Short: "Generates completion scripts", - Long: `To enable completion in your shell, run: + Use: "completion", + Short: "Generate shell completion scripts", + Long: `Generate shell completion scripts for GitHub CLI commands. - eval "$(gh completion)" +For example, for bash you could add this to your '~/.bash_profile': -You can add that to your '~/.bash_profile' to enable completion whenever you -start a new shell. + eval "$(gh completion)" -When installing with Homebrew, see https://docs.brew.sh/Shell-Completion +When installing GitHub CLI through a package manager, however, it's possible that +no additional shell configuration is necessary to gain completion support. For +Homebrew, see `, RunE: func(cmd *cobra.Command, args []string) error { shellType, err := cmd.Flags().GetString("shell") From dcedacd4f7fbb1579fdf2973f57c326226a3abd9 Mon Sep 17 00:00:00 2001 From: Alisson Santos Date: Thu, 5 Mar 2020 13:55:36 +0100 Subject: [PATCH 03/38] Remove duplicates --- api/queries_pr.go | 16 +++++++- command/pr_test.go | 20 ++++++++++ test/fixtures/prListWithDuplicates.json | 50 +++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/prListWithDuplicates.json diff --git a/api/queries_pr.go b/api/queries_pr.go index 265378b1e..092c7c12b 100644 --- a/api/queries_pr.go +++ b/api/queries_pr.go @@ -590,7 +590,21 @@ loop: } } - return prs, nil + return removeDuplicates(prs), nil +} + +func removeDuplicates(prs []PullRequest) []PullRequest { + var check = make(map[int]struct{}) + var uniqPRs []PullRequest + + for _, pr := range prs { + if _, ok := check[pr.Number]; !ok { + check[pr.Number] = struct{}{} + uniqPRs = append(uniqPRs, pr) + } + } + + return uniqPRs } func min(a, b int) int { diff --git a/command/pr_test.go b/command/pr_test.go index 51469342a..395dd4097 100644 --- a/command/pr_test.go +++ b/command/pr_test.go @@ -240,6 +240,26 @@ No pull requests match your search eq(t, reqBody.Variables.Labels, []string{"one", "two", "three"}) } +func TestPRList_filteringRemoveDuplicate(t *testing.T) { + initBlankContext("OWNER/REPO", "master") + http := initFakeHTTP() + http.StubRepoResponse("OWNER", "REPO") + + jsonFile, _ := os.Open("../test/fixtures/prListWithDuplicates.json") + defer jsonFile.Close() + http.StubResponse(200, jsonFile) + + output, err := RunCommand(prListCmd, "pr list -l one,two") + if err != nil { + t.Fatal(err) + } + + eq(t, output.String(), `32 New feature feature +29 Fixed bad bug hubot:bug-fix +28 Improve documentation docs +`) +} + func TestPRList_filteringClosed(t *testing.T) { initBlankContext("OWNER/REPO", "master") http := initFakeHTTP() diff --git a/test/fixtures/prListWithDuplicates.json b/test/fixtures/prListWithDuplicates.json new file mode 100644 index 000000000..a9cf19638 --- /dev/null +++ b/test/fixtures/prListWithDuplicates.json @@ -0,0 +1,50 @@ +{ + "data": { + "repository": { + "pullRequests": { + "edges": [ + { + "node": { + "number": 32, + "title": "New feature", + "url": "https://github.com/monalisa/hello/pull/32", + "headRefName": "feature" + } + }, + { + "node": { + "number": 32, + "title": "New feature", + "url": "https://github.com/monalisa/hello/pull/32", + "headRefName": "feature" + } + }, + { + "node": { + "number": 29, + "title": "Fixed bad bug", + "url": "https://github.com/monalisa/hello/pull/29", + "headRefName": "bug-fix", + "isCrossRepository": true, + "headRepositoryOwner": { + "login": "hubot" + } + } + }, + { + "node": { + "number": 28, + "title": "Improve documentation", + "url": "https://github.com/monalisa/hello/pull/28", + "headRefName": "docs" + } + } + ], + "pageInfo": { + "hasNextPage": false, + "endCursor": "" + } + } + } + } +} From a3557197fe1190c81f4f41d9bd9dcbd7d575a091 Mon Sep 17 00:00:00 2001 From: Alisson Santos Date: Thu, 5 Mar 2020 14:05:33 +0100 Subject: [PATCH 04/38] 2 space identation for JSON --- test/fixtures/prListWithDuplicates.json | 90 ++++++++++++------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/test/fixtures/prListWithDuplicates.json b/test/fixtures/prListWithDuplicates.json index a9cf19638..a84800f72 100644 --- a/test/fixtures/prListWithDuplicates.json +++ b/test/fixtures/prListWithDuplicates.json @@ -1,50 +1,50 @@ { - "data": { - "repository": { - "pullRequests": { - "edges": [ - { - "node": { - "number": 32, - "title": "New feature", - "url": "https://github.com/monalisa/hello/pull/32", - "headRefName": "feature" - } - }, - { - "node": { - "number": 32, - "title": "New feature", - "url": "https://github.com/monalisa/hello/pull/32", - "headRefName": "feature" - } - }, - { - "node": { - "number": 29, - "title": "Fixed bad bug", - "url": "https://github.com/monalisa/hello/pull/29", - "headRefName": "bug-fix", - "isCrossRepository": true, - "headRepositoryOwner": { - "login": "hubot" - } - } - }, - { - "node": { - "number": 28, - "title": "Improve documentation", - "url": "https://github.com/monalisa/hello/pull/28", - "headRefName": "docs" - } - } - ], - "pageInfo": { - "hasNextPage": false, - "endCursor": "" - } + "data": { + "repository": { + "pullRequests": { + "edges": [ + { + "node": { + "number": 32, + "title": "New feature", + "url": "https://github.com/monalisa/hello/pull/32", + "headRefName": "feature" } + }, + { + "node": { + "number": 32, + "title": "New feature", + "url": "https://github.com/monalisa/hello/pull/32", + "headRefName": "feature" + } + }, + { + "node": { + "number": 29, + "title": "Fixed bad bug", + "url": "https://github.com/monalisa/hello/pull/29", + "headRefName": "bug-fix", + "isCrossRepository": true, + "headRepositoryOwner": { + "login": "hubot" + } + } + }, + { + "node": { + "number": 28, + "title": "Improve documentation", + "url": "https://github.com/monalisa/hello/pull/28", + "headRefName": "docs" + } + } + ], + "pageInfo": { + "hasNextPage": false, + "endCursor": "" } + } } + } } From a1db5c30707e1234fa1908966558ec79bfdb6c4e Mon Sep 17 00:00:00 2001 From: Henrique Vicente Date: Wed, 5 Feb 2020 02:19:01 +0100 Subject: [PATCH 05/38] tests: adding the Go data race detector. https://golang.org/doc/articles/race_detector.html https://blog.golang.org/race-detector --- .github/workflows/go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 557f87049..2789ade34 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -23,5 +23,5 @@ jobs: - name: Build shell: bash run: | - go test ./... + go test -race ./... go build -v ./cmd/gh From 101d985a16d8d3346debfb26e1b43450dfcf000e Mon Sep 17 00:00:00 2001 From: Alisson Santos Date: Thu, 5 Mar 2020 18:09:09 +0100 Subject: [PATCH 06/38] remove duplicate from pages --- api/queries_pr.go | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/api/queries_pr.go b/api/queries_pr.go index 092c7c12b..dba19a500 100644 --- a/api/queries_pr.go +++ b/api/queries_pr.go @@ -504,6 +504,7 @@ func PullRequestList(client *Client, vars map[string]interface{}, limit int) ([] } }` + var check = make(map[int]struct{}) var prs []PullRequest pageLimit := min(limit, 100) variables := map[string]interface{}{} @@ -576,9 +577,13 @@ loop: } for _, edge := range prData.Edges { - prs = append(prs, edge.Node) - if len(prs) == limit { - break loop + if _, ok := check[edge.Node.Number]; !ok { + check[edge.Node.Number] = struct{}{} + prs = append(prs, edge.Node) + + if len(prs) == limit { + break loop + } } } @@ -590,21 +595,7 @@ loop: } } - return removeDuplicates(prs), nil -} - -func removeDuplicates(prs []PullRequest) []PullRequest { - var check = make(map[int]struct{}) - var uniqPRs []PullRequest - - for _, pr := range prs { - if _, ok := check[pr.Number]; !ok { - check[pr.Number] = struct{}{} - uniqPRs = append(uniqPRs, pr) - } - } - - return uniqPRs + return prs, nil } func min(a, b int) int { From 7a0a6658d516b9017f22655c04de32e3eca9e815 Mon Sep 17 00:00:00 2001 From: Alisson Santos Date: Thu, 5 Mar 2020 18:52:27 +0100 Subject: [PATCH 07/38] avoid nested ifs --- api/queries_pr.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/api/queries_pr.go b/api/queries_pr.go index dba19a500..34884d16f 100644 --- a/api/queries_pr.go +++ b/api/queries_pr.go @@ -577,13 +577,14 @@ loop: } for _, edge := range prData.Edges { - if _, ok := check[edge.Node.Number]; !ok { - check[edge.Node.Number] = struct{}{} - prs = append(prs, edge.Node) + if _, exists := check[edge.Node.Number]; exists { + continue + } - if len(prs) == limit { - break loop - } + prs = append(prs, edge.Node) + check[edge.Node.Number] = struct{}{} + if len(prs) == limit { + break loop } } From caf12adef15e2d45869ad6a4e08882f1d98b37c5 Mon Sep 17 00:00:00 2001 From: Mike Rogers Date: Sat, 7 Mar 2020 20:44:14 +0000 Subject: [PATCH 08/38] unusally > unusually --- context/context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context/context.go b/context/context.go index af4dd65fb..6c3bd5aee 100644 --- a/context/context.go +++ b/context/context.go @@ -25,7 +25,7 @@ type Context interface { } // cap the number of git remotes looked up, since the user might have an -// unusally large number of git remotes +// unusually large number of git remotes const maxRemotesForLookup = 5 func ResolveRemotesToRepos(remotes Remotes, client *api.Client, base string) (ResolvedRemotes, error) { From 50e886c5f859d4d03175544d196eac4fbaa8c17d Mon Sep 17 00:00:00 2001 From: Mike Rogers Date: Sat, 7 Mar 2020 20:44:39 +0000 Subject: [PATCH 09/38] sequental > sequential --- api/fake_http.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/fake_http.go b/api/fake_http.go index 773567aaf..92b904bd5 100644 --- a/api/fake_http.go +++ b/api/fake_http.go @@ -13,7 +13,7 @@ import ( // FakeHTTP provides a mechanism by which to stub HTTP responses through type FakeHTTP struct { - // Requests stores references to sequental requests that RoundTrip has received + // Requests stores references to sequential requests that RoundTrip has received Requests []*http.Request count int responseStubs []*http.Response From f9c32654e9c2c0195a8b93b506822a18a9413988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Mon, 9 Mar 2020 12:53:41 +0100 Subject: [PATCH 10/38] Fix setting homepage URL in `repo create` --- api/queries_repo.go | 2 +- api/queries_repo_test.go | 45 ++++++++++++++++++++++++++++++++++++++++ command/repo.go | 2 +- 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 api/queries_repo_test.go diff --git a/api/queries_repo.go b/api/queries_repo.go index 1c5bfe826..048b72b50 100644 --- a/api/queries_repo.go +++ b/api/queries_repo.go @@ -224,7 +224,7 @@ func ForkRepo(client *Client, repo ghrepo.Interface) (*Repository, error) { type RepoCreateInput struct { Name string `json:"name"` Visibility string `json:"visibility"` - Homepage string `json:"homepage,omitempty"` + HomepageURL string `json:"homepageUrl,omitempty"` Description string `json:"description,omitempty"` OwnerID string `json:"ownerId,omitempty"` diff --git a/api/queries_repo_test.go b/api/queries_repo_test.go new file mode 100644 index 000000000..454814fb4 --- /dev/null +++ b/api/queries_repo_test.go @@ -0,0 +1,45 @@ +package api + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "testing" +) + +func Test_RepoCreate(t *testing.T) { + http := &FakeHTTP{} + client := NewClient(ReplaceTripper(http)) + + http.StubResponse(200, bytes.NewBufferString(`{}`)) + + input := RepoCreateInput{ + Description: "roasted chesnuts", + HomepageURL: "http://example.com", + } + + _, err := RepoCreate(client, input) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if len(http.Requests) != 1 { + t.Fatalf("expected 1 HTTP request, seen %d", len(http.Requests)) + } + + var reqBody struct { + Query string + Variables struct { + Input map[string]interface{} + } + } + + bodyBytes, _ := ioutil.ReadAll(http.Requests[0].Body) + json.Unmarshal(bodyBytes, &reqBody) + if description := reqBody.Variables.Input["description"].(string); description != "roasted chesnuts" { + t.Errorf("expected description to be %q, got %q", "roasted chesnuts", description) + } + if homepage := reqBody.Variables.Input["homepageUrl"].(string); homepage != "http://example.com" { + t.Errorf("expected homepageUrl to be %q, got %q", "http://example.com", homepage) + } +} diff --git a/command/repo.go b/command/repo.go index 53b2ca8f8..648b5b500 100644 --- a/command/repo.go +++ b/command/repo.go @@ -158,7 +158,7 @@ func repoCreate(cmd *cobra.Command, args []string) error { OwnerID: orgName, TeamID: teamSlug, Description: description, - Homepage: homepage, + HomepageURL: homepage, HasIssuesEnabled: hasIssuesEnabled, HasWikiEnabled: hasWikiEnabled, } From 7777307588a0a952c4188ea687d0a620091d3dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Mon, 9 Mar 2020 14:51:40 +0100 Subject: [PATCH 11/38] Avoid forking/pushing until `pr create` inputs have been processed This avoids fork or push operation if there were errors processing the input arguments or if the operation has been cancelled during interactive survey. --- command/pr_create.go | 156 ++++++++++++++++++++----------------------- 1 file changed, 74 insertions(+), 82 deletions(-) diff --git a/command/pr_create.go b/command/pr_create.go index c40f5d8c9..8bb5e7462 100644 --- a/command/pr_create.go +++ b/command/pr_create.go @@ -1,6 +1,7 @@ package command import ( + "errors" "fmt" "net/url" "time" @@ -41,6 +42,7 @@ func prCreate(cmd *cobra.Command, _ []string) error { if err != nil { return fmt.Errorf("could not determine the current branch: %w", err) } + headRepo, headRepoErr := repoContext.HeadRepo() baseBranch, err := cmd.Flags().GetString("base") if err != nil { @@ -49,70 +51,13 @@ func prCreate(cmd *cobra.Command, _ []string) error { 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) { + if headBranch == baseBranch && headRepo != nil && 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")) } - 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 - 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 - } - break - } - - headBranchLabel := headBranch - if !ghrepo.IsSame(baseRepo, headRepo) { - headBranchLabel = fmt.Sprintf("%s:%s", headRepo.RepoOwner(), headBranch) - } title, err := cmd.Flags().GetString("title") if err != nil { @@ -127,22 +72,19 @@ func prCreate(cmd *cobra.Command, _ []string) error { if err != nil { return fmt.Errorf("could not parse web: %q", err) } - if isWeb { - compareURL := generateCompareURL(baseRepo, baseBranch, headBranchLabel, title, body) - fmt.Fprintf(cmd.ErrOrStderr(), "Opening %s in your browser.\n", displayURL(compareURL)) - return utils.OpenInBrowser(compareURL) - } - - fmt.Fprintf(colorableErr(cmd), "\nCreating pull request for %s into %s in %s\n\n", - utils.Cyan(headBranchLabel), - utils.Cyan(baseBranch), - ghrepo.FullName(baseRepo)) action := SubmitAction + if isWeb { + action = PreviewAction + } else { + fmt.Fprintf(colorableErr(cmd), "\nCreating pull request for %s into %s in %s\n\n", + utils.Cyan(headBranch), + utils.Cyan(baseBranch), + ghrepo.FullName(baseRepo)) + } - interactive := title == "" || body == "" - - if interactive { + // TODO: only drop into interactive mode if stdin & stdout are a tty + if !isWeb && (title == "" || body == "") { var templateFiles []string if rootDir, err := git.ToplevelDir(); err == nil { // TODO: figure out how to stub this in tests @@ -169,27 +111,78 @@ func prCreate(cmd *cobra.Command, _ []string) error { } } + if action == SubmitAction && title == "" { + return errors.New("pull request title must not be blank") + } + isDraft, err := cmd.Flags().GetBool("draft") if err != nil { return fmt.Errorf("could not parse draft: %w", err) } + didForkRepo := false + var headRemote *context.Remote + if headRepoErr != nil { + if baseRepo.IsPrivate { + return fmt.Errorf("cannot fork 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(), + } + } + + headBranchLabel := headBranch + if !ghrepo.IsSame(baseRepo, headRepo) { + headBranchLabel = fmt.Sprintf("%s:%s", headRepo.RepoOwner(), headBranch) + } + + if headRemote == nil { + headRemote, err = repoContext.RemoteForRepo(headRepo) + if err != nil { + return fmt.Errorf("git remote not found for head repository: %w", 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 + 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 + } + break + } + if action == SubmitAction { - if title == "" { - return fmt.Errorf("pull request title must not be blank") - } - - headRefName := headBranch - if !ghrepo.IsSame(headRemote, baseRepo) { - headRefName = fmt.Sprintf("%s:%s", headRemote.RepoOwner(), headBranch) - } - params := map[string]interface{}{ "title": title, "body": body, "draft": isDraft, "baseRefName": baseBranch, - "headRefName": headRefName, + "headRefName": headBranchLabel, } pr, err := api.CreatePullRequest(client, baseRepo, params) @@ -208,7 +201,6 @@ func prCreate(cmd *cobra.Command, _ []string) error { } return nil - } func generateCompareURL(r ghrepo.Interface, base, head, title, body string) string { From d584a9692c68a7e9ad711614eb906efab137669b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Mon, 9 Mar 2020 14:53:27 +0100 Subject: [PATCH 12/38] Raise error if `--draft` was used in `pr create --web` Since we cannot pass the "draft" option as query parameter, this raises an error instead of proceeding to open the browser and potentially confuse the user as to why `--draft` wasn't respected. --- command/pr_create.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/command/pr_create.go b/command/pr_create.go index 8bb5e7462..3ebbfaa61 100644 --- a/command/pr_create.go +++ b/command/pr_create.go @@ -119,6 +119,9 @@ func prCreate(cmd *cobra.Command, _ []string) error { if err != nil { return fmt.Errorf("could not parse draft: %w", err) } + if isDraft && isWeb { + return errors.New("the --draft flag is not supported with --web") + } didForkRepo := false var headRemote *context.Remote From e739d95c933067ba93d111183b75cd851f29187d Mon Sep 17 00:00:00 2001 From: rista404 Date: Mon, 9 Mar 2020 23:59:34 +0100 Subject: [PATCH 13/38] Show state instead of checks for closed/merged PRs --- command/pr.go | 56 +++++++++++---------- command/pr_test.go | 27 ++++++++++ test/fixtures/prStatusChecks.json | 3 ++ test/fixtures/prStatusClosedMerged.json | 66 +++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 25 deletions(-) create mode 100644 test/fixtures/prStatusClosedMerged.json diff --git a/command/pr.go b/command/pr.go index 688bddf2a..7157d4a71 100644 --- a/command/pr.go +++ b/command/pr.go @@ -391,45 +391,51 @@ func printPrs(w io.Writer, totalCount int, prs ...api.PullRequest) { for _, pr := range prs { prNumber := fmt.Sprintf("#%d", pr.Number) - prNumberColorFunc := utils.Green + prStateColorFunc := utils.Green if pr.IsDraft { - prNumberColorFunc = utils.Gray + prStateColorFunc = utils.Gray } else if pr.State == "MERGED" { - prNumberColorFunc = utils.Magenta + prStateColorFunc = utils.Magenta } else if pr.State == "CLOSED" { - prNumberColorFunc = utils.Red + prStateColorFunc = utils.Red } - fmt.Fprintf(w, " %s %s %s", prNumberColorFunc(prNumber), text.Truncate(50, replaceExcessiveWhitespace(pr.Title)), utils.Cyan("["+pr.HeadLabel()+"]")) + fmt.Fprintf(w, " %s %s %s", prStateColorFunc(prNumber), text.Truncate(50, replaceExcessiveWhitespace(pr.Title)), utils.Cyan("["+pr.HeadLabel()+"]")) checks := pr.ChecksStatus() reviews := pr.ReviewStatus() - if checks.Total > 0 || reviews.ChangesRequested || reviews.Approved { + + if pr.State != "OPEN" || checks.Total > 0 || reviews.ChangesRequested || reviews.Approved { fmt.Fprintf(w, "\n ") } - if checks.Total > 0 { - var summary string - if checks.Failing > 0 { - if checks.Failing == checks.Total { - summary = utils.Red("All checks failing") - } else { - summary = utils.Red(fmt.Sprintf("%d/%d checks failing", checks.Failing, checks.Total)) + if pr.State == "OPEN" { + if checks.Total > 0 { + var summary string + if checks.Failing > 0 { + if checks.Failing == checks.Total { + summary = utils.Red("All checks failing") + } else { + summary = utils.Red(fmt.Sprintf("%d/%d checks failing", checks.Failing, checks.Total)) + } + } else if checks.Pending > 0 { + summary = utils.Yellow("Checks pending") + } else if checks.Passing == checks.Total { + summary = utils.Green("Checks passing") } - } else if checks.Pending > 0 { - summary = utils.Yellow("Checks pending") - } else if checks.Passing == checks.Total { - summary = utils.Green("Checks passing") + fmt.Fprintf(w, " - %s", summary) } - fmt.Fprintf(w, " - %s", summary) - } - if reviews.ChangesRequested { - fmt.Fprintf(w, " - %s", utils.Red("Changes requested")) - } else if reviews.ReviewRequired { - fmt.Fprintf(w, " - %s", utils.Yellow("Review required")) - } else if reviews.Approved { - fmt.Fprintf(w, " - %s", utils.Green("Approved")) + if reviews.ChangesRequested { + fmt.Fprintf(w, " - %s", utils.Red("Changes requested")) + } else if reviews.ReviewRequired { + fmt.Fprintf(w, " - %s", utils.Yellow("Review required")) + } else if reviews.Approved { + fmt.Fprintf(w, " - %s", utils.Green("Approved")) + } + } else { + s := strings.Title(strings.ToLower(pr.State)) + fmt.Fprintf(w, " - %s", prStateColorFunc(s)) } fmt.Fprint(w, "\n") diff --git a/command/pr_test.go b/command/pr_test.go index 395dd4097..e72a7d64e 100644 --- a/command/pr_test.go +++ b/command/pr_test.go @@ -155,6 +155,33 @@ func TestPRStatus_reviewsAndChecks(t *testing.T) { } } +func TestPRStatus_closedMerged(t *testing.T) { + initBlankContext("OWNER/REPO", "blueberries") + http := initFakeHTTP() + http.StubRepoResponse("OWNER", "REPO") + + jsonFile, _ := os.Open("../test/fixtures/prStatusClosedMerged.json") + defer jsonFile.Close() + http.StubResponse(200, jsonFile) + + output, err := RunCommand(prStatusCmd, "pr status") + if err != nil { + t.Errorf("error running command `pr status`: %v", err) + } + + expected := []string{ + "- Checks passing - Changes requested", + "- Closed", + "- Merged", + } + + for _, line := range expected { + if !strings.Contains(output.String(), line) { + t.Errorf("output did not contain %q: %q", line, output.String()) + } + } +} + func TestPRStatus_blankSlate(t *testing.T) { initBlankContext("OWNER/REPO", "blueberries") http := initFakeHTTP() diff --git a/test/fixtures/prStatusChecks.json b/test/fixtures/prStatusChecks.json index 922fe8ce7..55035ae36 100644 --- a/test/fixtures/prStatusChecks.json +++ b/test/fixtures/prStatusChecks.json @@ -13,6 +13,7 @@ "node": { "number": 8, "title": "Strawberries are not actually berries", + "state": "OPEN", "url": "https://github.com/cli/cli/pull/8", "headRefName": "strawberries", "reviewDecision": "CHANGES_REQUESTED", @@ -39,6 +40,7 @@ "node": { "number": 7, "title": "Bananas are berries", + "state": "OPEN", "url": "https://github.com/cli/cli/pull/7", "headRefName": "banananana", "reviewDecision": "APPROVED", @@ -66,6 +68,7 @@ "node": { "number": 6, "title": "Avocado is probably not a berry", + "state": "OPEN", "url": "https://github.com/cli/cli/pull/6", "headRefName": "avo", "reviewDecision": "REVIEW_REQUIRED", diff --git a/test/fixtures/prStatusClosedMerged.json b/test/fixtures/prStatusClosedMerged.json new file mode 100644 index 000000000..25b4391dc --- /dev/null +++ b/test/fixtures/prStatusClosedMerged.json @@ -0,0 +1,66 @@ +{ + "data": { + "repository": { + "pullRequests": { + "totalCount": 1, + "edges": [ + { + "node": { + "number": 8, + "title": "Blueberries are a good fruit", + "state": "OPEN", + "url": "https://github.com/cli/cli/pull/8", + "headRefName": "blueberries", + "reviewDecision": "CHANGES_REQUESTED", + "commits": { + "nodes": [ + { + "commit": { + "statusCheckRollup": { + "contexts": { + "nodes": [ + { + "state": "SUCCESS" + } + ] + } + } + } + } + ] + } + } + } + ] + } + }, + "viewerCreated": { + "totalCount": 1, + "edges": [ + { + "node": { + "number": 8, + "state": "CLOSED", + "title": "Strawberries are not actually berries", + "url": "https://github.com/cli/cli/pull/8", + "headRefName": "strawberries" + } + }, + { + "node": { + "number": 8, + "state": "MERGED", + "title": "Bananas are berries", + "url": "https://github.com/cli/cli/pull/8", + "headRefName": "banananana" + } + } + ] + }, + "reviewRequested": { + "totalCount": 0, + "edges": [] + } + } + } + \ No newline at end of file From 8e7ba907b195bf03f8ccb0ecb4d0595a740c93be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Mon, 9 Mar 2020 18:50:23 -0600 Subject: [PATCH 14/38] Add filter issues by author --- api/queries_issue.go | 12 +++++++++--- api/queries_issue_test.go | 2 +- command/issue.go | 8 +++++++- command/issue_test.go | 4 +++- test/fixtures/issueList.json | 9 +++++++++ 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/api/queries_issue.go b/api/queries_issue.go index 2c755b1d3..e0de92841 100644 --- a/api/queries_issue.go +++ b/api/queries_issue.go @@ -55,6 +55,9 @@ const fragments = ` } totalCount } + author { + login + } } ` @@ -171,7 +174,7 @@ func IssueStatus(client *Client, repo ghrepo.Interface, currentUsername string) return &payload, nil } -func IssueList(client *Client, repo ghrepo.Interface, state string, labels []string, assigneeString string, limit int) ([]Issue, error) { +func IssueList(client *Client, repo ghrepo.Interface, state string, labels []string, assigneeString string, limit int, authorString string) ([]Issue, error) { var states []string switch state { case "open", "": @@ -185,10 +188,10 @@ func IssueList(client *Client, repo ghrepo.Interface, state string, labels []str } query := fragments + ` - query($owner: String!, $repo: String!, $limit: Int, $endCursor: String, $states: [IssueState!] = OPEN, $labels: [String!], $assignee: String) { + query($owner: String!, $repo: String!, $limit: Int, $endCursor: String, $states: [IssueState!] = OPEN, $labels: [String!], $assignee: String, $author: String) { repository(owner: $owner, name: $repo) { hasIssuesEnabled - issues(first: $limit, after: $endCursor, orderBy: {field: CREATED_AT, direction: DESC}, states: $states, labels: $labels, filterBy: {assignee: $assignee}) { + issues(first: $limit, after: $endCursor, orderBy: {field: CREATED_AT, direction: DESC}, states: $states, labels: $labels, filterBy: {assignee: $assignee, createdBy: $author}) { nodes { ...issue } @@ -212,6 +215,9 @@ func IssueList(client *Client, repo ghrepo.Interface, state string, labels []str if assigneeString != "" { variables["assignee"] = assigneeString } + if authorString != "" { + variables["author"] = authorString + } var response struct { Repository struct { diff --git a/api/queries_issue_test.go b/api/queries_issue_test.go index bde6b8696..c9db7ead6 100644 --- a/api/queries_issue_test.go +++ b/api/queries_issue_test.go @@ -38,7 +38,7 @@ func TestIssueList(t *testing.T) { } } } `)) - _, err := IssueList(client, ghrepo.FromFullName("OWNER/REPO"), "open", []string{}, "", 251) + _, err := IssueList(client, ghrepo.FromFullName("OWNER/REPO"), "open", []string{}, "", 251, "") if err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/command/issue.go b/command/issue.go index 4fc6d5ab2..8d83e392b 100644 --- a/command/issue.go +++ b/command/issue.go @@ -37,6 +37,7 @@ func init() { issueListCmd.Flags().StringSliceP("label", "l", nil, "Filter by label") issueListCmd.Flags().StringP("state", "s", "", "Filter by state: {open|closed|all}") issueListCmd.Flags().IntP("limit", "L", 30, "Maximum number of issues to fetch") + issueListCmd.Flags().StringP("author", "A", "", "Filter by author") issueViewCmd.Flags().BoolP("preview", "p", false, "Display preview of issue content") } @@ -109,9 +110,14 @@ func issueList(cmd *cobra.Command, args []string) error { return err } + author, err := cmd.Flags().GetString("author") + if err != nil { + return err + } + 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, author) if err != nil { return err } diff --git a/command/issue_test.go b/command/issue_test.go index f5f8854f2..a4de959e1 100644 --- a/command/issue_test.go +++ b/command/issue_test.go @@ -136,7 +136,7 @@ func TestIssueList_withFlags(t *testing.T) { } } } `)) - output, err := RunCommand(issueListCmd, "issue list -a probablyCher -l web,bug -s open") + output, err := RunCommand(issueListCmd, "issue list -a probablyCher -l web,bug -s open -A foo") if err != nil { t.Errorf("error running command `issue list`: %v", err) } @@ -154,6 +154,7 @@ No issues match your search Assignee string Labels []string States []string + Author string } }{} json.Unmarshal(bodyBytes, &reqBody) @@ -161,6 +162,7 @@ No issues match your search eq(t, reqBody.Variables.Assignee, "probablyCher") eq(t, reqBody.Variables.Labels, []string{"web", "bug"}) eq(t, reqBody.Variables.States, []string{"OPEN"}) + eq(t, reqBody.Variables.Author, "foo") } func TestIssueList_nullAssigneeLabels(t *testing.T) { diff --git a/test/fixtures/issueList.json b/test/fixtures/issueList.json index 40537f12d..7357d3ca4 100644 --- a/test/fixtures/issueList.json +++ b/test/fixtures/issueList.json @@ -15,6 +15,9 @@ } ], "totalCount": 1 + }, + "author": { + "login": "foo" } }, { @@ -28,6 +31,9 @@ } ], "totalCount": 1 + }, + "author": { + "login": "bar" } }, { @@ -41,6 +47,9 @@ } ], "totalCount": 1 + }, + "author": { + "login": "bar" } } ] From a6f06f80847cc084f863bc5f135fac93412ffdb2 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Mon, 9 Mar 2020 19:16:44 -0700 Subject: [PATCH 15/38] feat: update README --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index d731a3cb6..5344db1ea 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,9 @@ And if you spot bugs or have features that you'd really like to see in `gh`, ple Check out the [docs][] for more information. +## Documentation + +Read the official docs at [https://cli.github.com/manual/](https://cli.github.com/manual/) ## Comparison with hub @@ -109,6 +112,16 @@ $ yay -S github-cli Install a prebuilt binary from the [releases page][] +## Setting an Editor + +For macOS and Linux, `gh` will respect the following environment variables, in this order, based on your OS and shell setup: + +1. `GIT_EDITOR` +2. `VISUAL` +3. `EDITOR` + +On Windows, the editor will currently always be Notepad. + ### [Build from source](/source.md) [docs]: https://cli.github.com/manual From 42b40b11791e552d4b465a02fd44f1c759ea2f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Tue, 10 Mar 2020 13:58:06 +0100 Subject: [PATCH 16/38] No longer need to opt into `pe_mobile` API flag `pe_mobile` was required to get access to `reviewDecision` and `statusCheckRollup`, but both are now public in GitHub API. --- command/root.go | 1 - 1 file changed, 1 deletion(-) diff --git a/command/root.go b/command/root.go index 7b0d078ef..dccc6e453 100644 --- a/command/root.go +++ b/command/root.go @@ -136,7 +136,6 @@ var apiClientForContext = func(ctx context.Context) (*api.Client, error) { // antiope-preview: Checks // shadow-cat-preview: Draft pull requests api.AddHeader("Accept", "application/vnd.github.antiope-preview+json, application/vnd.github.shadow-cat-preview"), - api.AddHeader("GraphQL-Features", "pe_mobile"), ) return api.NewClient(opts...), nil From 7c11f7655a2ac4349e765e671216f2e984e3e5af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Tue, 10 Mar 2020 13:59:56 +0100 Subject: [PATCH 17/38] No need to opt in to `shadow-cat` API preview This was needed for draft pull requests, but these APIs were public since Feburary 2020. --- command/root.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/command/root.go b/command/root.go index dccc6e453..cae4bab4e 100644 --- a/command/root.go +++ b/command/root.go @@ -134,8 +134,7 @@ var apiClientForContext = func(ctx context.Context) (*api.Client, error) { api.AddHeader("Authorization", fmt.Sprintf("token %s", token)), api.AddHeader("User-Agent", fmt.Sprintf("GitHub CLI %s", Version)), // antiope-preview: Checks - // shadow-cat-preview: Draft pull requests - api.AddHeader("Accept", "application/vnd.github.antiope-preview+json, application/vnd.github.shadow-cat-preview"), + api.AddHeader("Accept", "application/vnd.github.antiope-preview+json"), ) return api.NewClient(opts...), nil From 2a87dabf59498c12030413d61d784446f5a71f4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Tue, 10 Mar 2020 10:17:22 -0600 Subject: [PATCH 18/38] Remove unused fields --- api/queries_issue.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/api/queries_issue.go b/api/queries_issue.go index e0de92841..f83e43d92 100644 --- a/api/queries_issue.go +++ b/api/queries_issue.go @@ -55,9 +55,6 @@ const fragments = ` } totalCount } - author { - login - } } ` From 7f9cceccbd98116b899d2bb836ec5e176182b8b6 Mon Sep 17 00:00:00 2001 From: Amanda Pinsker Date: Tue, 10 Mar 2020 11:15:47 -0700 Subject: [PATCH 19/38] Make the README title a little less verbose --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d731a3cb6..e9aaef7ec 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# gh - The GitHub CLI tool +# GitHub CLI `gh` is GitHub on the command line, and it's now available in beta. It brings pull requests, issues, and other GitHub concepts to the terminal next to where you are already working with `git` and your code. From f32235aa08df21aa74f2c0a3c4d6f6cc40ea0ca1 Mon Sep 17 00:00:00 2001 From: Billy Griffin <5091167+billygriffin@users.noreply.github.com> Date: Tue, 10 Mar 2020 12:52:01 -0600 Subject: [PATCH 20/38] Move docs to docs folder The docs were just hanging out in the top level repo and we thought it'd be nicer to start to organize them together. --- README.md | 2 +- releasing.md => docs/releasing.md | 0 source.md => docs/source.md | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename releasing.md => docs/releasing.md (100%) rename source.md => docs/source.md (100%) diff --git a/README.md b/README.md index e9aaef7ec..5c6fe6edb 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ $ yay -S github-cli Install a prebuilt binary from the [releases page][] -### [Build from source](/source.md) +### [Build from source](/docs/source.md) [docs]: https://cli.github.com/manual [scoop]: https://scoop.sh diff --git a/releasing.md b/docs/releasing.md similarity index 100% rename from releasing.md rename to docs/releasing.md diff --git a/source.md b/docs/source.md similarity index 100% rename from source.md rename to docs/source.md From 159a697159f5a4f0b7e1e5d68d55d779d24994b0 Mon Sep 17 00:00:00 2001 From: Billy Griffin <5091167+billygriffin@users.noreply.github.com> Date: Tue, 10 Mar 2020 13:22:39 -0600 Subject: [PATCH 21/38] Add README to docs folder --- docs/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/README.md diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..35faf36a6 --- /dev/null +++ b/docs/README.md @@ -0,0 +1 @@ +This folder is used for documentation related to developing `gh`. Docs for `gh` installation and usage are available at [https://cli.github.com/manual](https://cli.github.com/manual). \ No newline at end of file From 0cef66dc674bd9324e51176b197dc36d9603380d Mon Sep 17 00:00:00 2001 From: sibis Date: Tue, 10 Mar 2020 22:33:47 +0530 Subject: [PATCH 22/38] Verify repo before viewing in the browser --- command/repo.go | 25 +++++++++++++++++++++++++ command/repo_test.go | 16 +++++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/command/repo.go b/command/repo.go index 53b2ca8f8..8a4d1cc0e 100644 --- a/command/repo.go +++ b/command/repo.go @@ -388,21 +388,46 @@ func repoView(cmd *cobra.Command, args []string) error { ctx := contextForCommand(cmd) var openURL string + var toView ghrepo.Interface if len(args) == 0 { baseRepo, err := determineBaseRepo(cmd, ctx) if err != nil { return err } openURL = fmt.Sprintf("https://github.com/%s", ghrepo.FullName(baseRepo)) + toView = baseRepo } else { repoArg := args[0] if isURL(repoArg) { openURL = repoArg + parsedURL, err := url.Parse(repoArg) + if err != nil { + return fmt.Errorf("did not understand argument: %w", err) + } + + toView, err = ghrepo.FromURL(parsedURL) + if err != nil { + return fmt.Errorf("did not understand argument: %w", err) + } } else { + toView = ghrepo.FromFullName(repoArg) openURL = fmt.Sprintf("https://github.com/%s", repoArg) } } + apiClient, err := apiClientForContext(ctx) + + if err != nil { + return err + } + + _, err_message := api.GitHubRepo(apiClient, toView) + if err_message != nil { + return err_message + } + fmt.Fprintf(cmd.ErrOrStderr(), "Opening %s in your browser.\n", displayURL(openURL)) return utils.OpenInBrowser(openURL) + + } diff --git a/command/repo_test.go b/command/repo_test.go index da64712b7..3fd3357da 100644 --- a/command/repo_test.go +++ b/command/repo_test.go @@ -579,10 +579,14 @@ func TestRepoCreate_orgWithTeam(t *testing.T) { } } + func TestRepoView(t *testing.T) { initBlankContext("OWNER/REPO", "master") http := initFakeHTTP() http.StubRepoResponse("OWNER", "REPO") + http.StubResponse(200, bytes.NewBufferString(` + { } + `)) var seenCmd *exec.Cmd restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable { @@ -606,13 +610,17 @@ func TestRepoView(t *testing.T) { 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() + http := initFakeHTTP() + http.StubResponse(200, bytes.NewBufferString(` + { } + `)) var seenCmd *exec.Cmd restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable { @@ -642,8 +650,10 @@ func TestRepoView_fullURL(t *testing.T) { initContext = func() context.Context { return ctx } - initFakeHTTP() - + http := initFakeHTTP() + http.StubResponse(200, bytes.NewBufferString(` + { } + `)) var seenCmd *exec.Cmd restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable { seenCmd = cmd From e803470b03318758b9bf8aa37198f786bc998659 Mon Sep 17 00:00:00 2001 From: rista404 Date: Tue, 10 Mar 2020 20:56:45 +0100 Subject: [PATCH 23/38] Print merged/closed in the same line as PR title --- command/pr.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/command/pr.go b/command/pr.go index 7157d4a71..7a1b00047 100644 --- a/command/pr.go +++ b/command/pr.go @@ -405,11 +405,11 @@ func printPrs(w io.Writer, totalCount int, prs ...api.PullRequest) { checks := pr.ChecksStatus() reviews := pr.ReviewStatus() - if pr.State != "OPEN" || checks.Total > 0 || reviews.ChangesRequested || reviews.Approved { - fmt.Fprintf(w, "\n ") - } - if pr.State == "OPEN" { + if checks.Total > 0 || reviews.ChangesRequested || reviews.Approved { + fmt.Fprintf(w, "\n ") + } + if checks.Total > 0 { var summary string if checks.Failing > 0 { From a00a48d0b6cedd9d85b65f6dfa1c5149d19b978f Mon Sep 17 00:00:00 2001 From: Billy Griffin <5091167+billygriffin@users.noreply.github.com> Date: Tue, 10 Mar 2020 15:20:49 -0600 Subject: [PATCH 24/38] Document comparison with hub --- docs/gh-vs-hub.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 docs/gh-vs-hub.md diff --git a/docs/gh-vs-hub.md b/docs/gh-vs-hub.md new file mode 100644 index 000000000..366702b95 --- /dev/null +++ b/docs/gh-vs-hub.md @@ -0,0 +1,27 @@ +# GitHub CLI & `hub` + +GitHub CLI (`gh`) was announced in early 2020 and provides a more seamless way to interact with your GitHub repositories from the command line. We also know that many people are interested in the very similar `hub` project, so we wanted to clarify some potential points of confusion. + +## Why didn’t you just build `gh` on top of `hub`? + +We wrestled with the decision of whether to continue building onto `hub` and adopt it as an official GitHub project. In weighing different possibilities, we decided to start fresh without the constraints of 10 years of design decisions that `hub` has baked in and without the assumption that `hub` can be safely aliased to `git`. We also wanted to be more opinionated and focused on GitHub workflows, and doing this with `hub` had the risk of alienating many `hub` users who love the existing tool and expected it to work in the way they were used to. + +## What’s next for `hub`? + +The GitHub CLI team is focused solely on building out the new tool, `gh`. We aren’t shutting down `hub` or doing anything to change it. It’s an open source project and will continue to exist as long as it’s maintained and keeps receiving contributions. + +## What does it mean that GitHub CLI is official and `hub` is unofficial? + +GitHub CLI is built and maintained by a team of people who work on the tool on behalf of GitHub. When there’s something wrong with it, people can reach out to GitHub support or create an issue in the issue tracker, where an employee at GitHub will respond. + +`hub` is a project whose maintainer also happens to be a GitHub employee. He chooses to maintain `hub` in his spare time, as many of our employees do with open source projects. + +## Should I use `gh` or `hub`? + +We have no interest in forcing anyone to use GitHub CLI instead of `hub`. We think people should use whatever set of tools makes them happiest and most productive working with GitHub. + +If you are set on using a tool that acts as a wrapper for Git itself, `hub` is likely a better choice than `gh`. `hub` currently covers a larger overall surface area of GitHub’s API v3, provides more scripting functionality, and is compatible with GitHub Enterprise (though these are all things that we intend to improve in GitHub CLI). + +If you want a tool that’s more opinionated and intended to help simplify your GitHub workflows from the command line, we hope you’ll use `gh`. And since `gh` is maintained by a team at GitHub, we intend to be responsive to people’s concerns and needs and improve the tool based on how people are using it over time. + +GitHub CLI is not intended to be an exact replacement for `hub` and likely never will be, but our hope is that the vast majority of GitHub users who use the CLI will find more and more value in using `gh` as we build it out over time. From 12ba3f6389821c58c85fd43395315c9e4449bed0 Mon Sep 17 00:00:00 2001 From: Billy Griffin <5091167+billygriffin@users.noreply.github.com> Date: Tue, 10 Mar 2020 15:21:01 -0600 Subject: [PATCH 25/38] Add reference to hub comp in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c6fe6edb..68f54137d 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Check out the [docs][] for more information. For many years, [hub][] was the unofficial GitHub CLI tool. `gh` is a new project for us to explore what an official GitHub CLI tool can look like with a fundamentally different design. While both tools bring GitHub to the terminal, `hub` behaves as a proxy to `git` and `gh` is a standalone -tool. +tool. Check out our [more detailed explanation](/docs/gh-vs-hub.md) to learn more. ## Installation and Upgrading From 21419531e37d522b03024a5ab4977293e239a79c Mon Sep 17 00:00:00 2001 From: Billy Griffin <5091167+billygriffin@users.noreply.github.com> Date: Tue, 10 Mar 2020 15:27:34 -0600 Subject: [PATCH 26/38] Add links to docs --- docs/gh-vs-hub.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/gh-vs-hub.md b/docs/gh-vs-hub.md index 366702b95..cfd6c88bc 100644 --- a/docs/gh-vs-hub.md +++ b/docs/gh-vs-hub.md @@ -1,6 +1,6 @@ # GitHub CLI & `hub` -GitHub CLI (`gh`) was announced in early 2020 and provides a more seamless way to interact with your GitHub repositories from the command line. We also know that many people are interested in the very similar `hub` project, so we wanted to clarify some potential points of confusion. +[GitHub CLI](https://cli.github.com/) (`gh`) was [announced in early 2020](https://github.blog/2020-02-12-supercharge-your-command-line-experience-github-cli-is-now-in-beta/) and provides a more seamless way to interact with your GitHub repositories from the command line. We also know that many people are interested in the very similar [`hub`](https://hub.github.com/) project, so we wanted to clarify some potential points of confusion. ## Why didn’t you just build `gh` on top of `hub`? From 5663a67df4ce43d2cbcfb5837326c065c74cd226 Mon Sep 17 00:00:00 2001 From: Billy Griffin <5091167+billygriffin@users.noreply.github.com> Date: Tue, 10 Mar 2020 15:31:33 -0600 Subject: [PATCH 27/38] Minor language tweak to avoid repetition --- docs/gh-vs-hub.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/gh-vs-hub.md b/docs/gh-vs-hub.md index cfd6c88bc..207a0a956 100644 --- a/docs/gh-vs-hub.md +++ b/docs/gh-vs-hub.md @@ -24,4 +24,4 @@ If you are set on using a tool that acts as a wrapper for Git itself, `hub` is l If you want a tool that’s more opinionated and intended to help simplify your GitHub workflows from the command line, we hope you’ll use `gh`. And since `gh` is maintained by a team at GitHub, we intend to be responsive to people’s concerns and needs and improve the tool based on how people are using it over time. -GitHub CLI is not intended to be an exact replacement for `hub` and likely never will be, but our hope is that the vast majority of GitHub users who use the CLI will find more and more value in using `gh` as we build it out over time. +GitHub CLI is not intended to be an exact replacement for `hub` and likely never will be, but our hope is that the vast majority of GitHub users who use the CLI will find more and more value in using `gh` as we continue to improve it. From f8865e3c23623ca6d791962d58ba1f039e856c61 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Tue, 10 Mar 2020 19:40:40 -0700 Subject: [PATCH 28/38] refactor: remove editor section and one line docs --- README.md | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/README.md b/README.md index 5344db1ea..8170c6c1e 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,9 @@ And if you spot bugs or have features that you'd really like to see in `gh`, ple - `gh repo [view, create, clone, fork]` - `gh help` -Check out the [docs][] for more information. - ## Documentation -Read the official docs at [https://cli.github.com/manual/](https://cli.github.com/manual/) +Read the official docs at [https://cli.github.com/manual/](https://cli.github.com/manual/) for more information. ## Comparison with hub @@ -112,16 +110,6 @@ $ yay -S github-cli Install a prebuilt binary from the [releases page][] -## Setting an Editor - -For macOS and Linux, `gh` will respect the following environment variables, in this order, based on your OS and shell setup: - -1. `GIT_EDITOR` -2. `VISUAL` -3. `EDITOR` - -On Windows, the editor will currently always be Notepad. - ### [Build from source](/source.md) [docs]: https://cli.github.com/manual From ba4d5c5f0b9ff3dd14df8f3d7fb665a4d9022ca0 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Tue, 10 Mar 2020 19:43:18 -0700 Subject: [PATCH 29/38] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8170c6c1e..b37184af7 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ And if you spot bugs or have features that you'd really like to see in `gh`, ple ## Documentation -Read the official docs at [https://cli.github.com/manual/](https://cli.github.com/manual/) for more information. +Read the [official docs](https://cli.github.com/manual/) for more information. ## Comparison with hub From 834755acb6fc11c0d20c96162c5856245e75f297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Wed, 11 Mar 2020 10:13:47 -0600 Subject: [PATCH 30/38] Remove unused stub data --- test/fixtures/issueList.json | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/fixtures/issueList.json b/test/fixtures/issueList.json index 7357d3ca4..40537f12d 100644 --- a/test/fixtures/issueList.json +++ b/test/fixtures/issueList.json @@ -15,9 +15,6 @@ } ], "totalCount": 1 - }, - "author": { - "login": "foo" } }, { @@ -31,9 +28,6 @@ } ], "totalCount": 1 - }, - "author": { - "login": "bar" } }, { @@ -47,9 +41,6 @@ } ], "totalCount": 1 - }, - "author": { - "login": "bar" } } ] From 0fc9dfac37f1f45f318d6d6d432ca75bb7156ce8 Mon Sep 17 00:00:00 2001 From: sibis Date: Thu, 12 Mar 2020 00:44:07 +0530 Subject: [PATCH 31/38] refactor: code cleanup incorporating the PR suggestions and removed blank lines --- command/repo.go | 22 +++++++--------------- command/repo_test.go | 1 - 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/command/repo.go b/command/repo.go index 8a4d1cc0e..122fe402c 100644 --- a/command/repo.go +++ b/command/repo.go @@ -386,20 +386,16 @@ var Confirm = func(prompt string, result *bool) error { func repoView(cmd *cobra.Command, args []string) error { ctx := contextForCommand(cmd) - - var openURL string var toView ghrepo.Interface if len(args) == 0 { - baseRepo, err := determineBaseRepo(cmd, ctx) + var err error + toView, err = determineBaseRepo(cmd, ctx) if err != nil { return err } - openURL = fmt.Sprintf("https://github.com/%s", ghrepo.FullName(baseRepo)) - toView = baseRepo } else { repoArg := args[0] if isURL(repoArg) { - openURL = repoArg parsedURL, err := url.Parse(repoArg) if err != nil { return fmt.Errorf("did not understand argument: %w", err) @@ -411,23 +407,19 @@ func repoView(cmd *cobra.Command, args []string) error { } } else { toView = ghrepo.FromFullName(repoArg) - openURL = fmt.Sprintf("https://github.com/%s", repoArg) } } apiClient, err := apiClientForContext(ctx) - + if err != nil { + return err + } + _, err = api.GitHubRepo(apiClient, toView) if err != nil { return err } - _, err_message := api.GitHubRepo(apiClient, toView) - if err_message != nil { - return err_message - } - + openURL := fmt.Sprintf("https://github.com/%s", ghrepo.FullName(toView)) fmt.Fprintf(cmd.ErrOrStderr(), "Opening %s in your browser.\n", displayURL(openURL)) return utils.OpenInBrowser(openURL) - - } diff --git a/command/repo_test.go b/command/repo_test.go index 3fd3357da..a3ba656c3 100644 --- a/command/repo_test.go +++ b/command/repo_test.go @@ -610,7 +610,6 @@ func TestRepoView(t *testing.T) { eq(t, url, "https://github.com/OWNER/REPO") } - func TestRepoView_ownerRepo(t *testing.T) { ctx := context.NewBlank() ctx.SetBranch("master") From 6db7e89abffdab5f85337abcd876a32c78ab35ad Mon Sep 17 00:00:00 2001 From: Toshiya Doi Date: Fri, 13 Mar 2020 00:24:10 +0900 Subject: [PATCH 32/38] Fix indentations and PR numbers of a test fixture --- test/fixtures/prStatusClosedMerged.json | 127 ++++++++++++------------ 1 file changed, 63 insertions(+), 64 deletions(-) diff --git a/test/fixtures/prStatusClosedMerged.json b/test/fixtures/prStatusClosedMerged.json index 25b4391dc..03e31637e 100644 --- a/test/fixtures/prStatusClosedMerged.json +++ b/test/fixtures/prStatusClosedMerged.json @@ -1,66 +1,65 @@ { - "data": { - "repository": { - "pullRequests": { - "totalCount": 1, - "edges": [ - { - "node": { - "number": 8, - "title": "Blueberries are a good fruit", - "state": "OPEN", - "url": "https://github.com/cli/cli/pull/8", - "headRefName": "blueberries", - "reviewDecision": "CHANGES_REQUESTED", - "commits": { - "nodes": [ - { - "commit": { - "statusCheckRollup": { - "contexts": { - "nodes": [ - { - "state": "SUCCESS" - } - ] - } - } - } - } - ] - } - } - } - ] - } - }, - "viewerCreated": { - "totalCount": 1, - "edges": [ - { - "node": { - "number": 8, - "state": "CLOSED", - "title": "Strawberries are not actually berries", - "url": "https://github.com/cli/cli/pull/8", - "headRefName": "strawberries" - } - }, - { - "node": { - "number": 8, - "state": "MERGED", - "title": "Bananas are berries", - "url": "https://github.com/cli/cli/pull/8", - "headRefName": "banananana" - } - } - ] - }, - "reviewRequested": { - "totalCount": 0, - "edges": [] - } - } + "data": { + "repository": { + "pullRequests": { + "totalCount": 1, + "edges": [ + { + "node": { + "number": 8, + "title": "Blueberries are a good fruit", + "state": "OPEN", + "url": "https://github.com/cli/cli/pull/8", + "headRefName": "blueberries", + "reviewDecision": "CHANGES_REQUESTED", + "commits": { + "nodes": [ + { + "commit": { + "statusCheckRollup": { + "contexts": { + "nodes": [ + { + "state": "SUCCESS" + } + ] + } + } + } + } + ] + } + } + } + ] + } + }, + "viewerCreated": { + "totalCount": 1, + "edges": [ + { + "node": { + "number": 10, + "state": "CLOSED", + "title": "Strawberries are not actually berries", + "url": "https://github.com/cli/cli/pull/10", + "headRefName": "strawberries" + } + }, + { + "node": { + "number": 9, + "state": "MERGED", + "title": "Bananas are berries", + "url": "https://github.com/cli/cli/pull/9", + "headRefName": "banananana" + } + } + ] + }, + "reviewRequested": { + "totalCount": 0, + "edges": [] + } } - \ No newline at end of file +} From 3c4f006eff23d88026e1c7b7944533834fd486ad Mon Sep 17 00:00:00 2001 From: Toshiya Doi Date: Fri, 13 Mar 2020 00:24:44 +0900 Subject: [PATCH 33/38] Add a test for the current branch --- command/pr_test.go | 32 +++++++++++++ test/fixtures/prStatusCurrentBranch.json | 61 ++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 test/fixtures/prStatusCurrentBranch.json diff --git a/command/pr_test.go b/command/pr_test.go index e72a7d64e..f024792e3 100644 --- a/command/pr_test.go +++ b/command/pr_test.go @@ -182,6 +182,38 @@ func TestPRStatus_closedMerged(t *testing.T) { } } +func TestPRStatus_currentBranch_showTheMostRecentPR(t *testing.T) { + initBlankContext("OWNER/REPO", "blueberries") + http := initFakeHTTP() + http.StubRepoResponse("OWNER", "REPO") + + jsonFile, _ := os.Open("../test/fixtures/prStatusCurrentBranch.json") + defer jsonFile.Close() + http.StubResponse(200, jsonFile) + + output, err := RunCommand(prStatusCmd, "pr status") + if err != nil { + t.Errorf("error running command `pr status`: %v", err) + } + + expectedLine := regexp.MustCompile(`#10 Blueberries are certainly a good fruit \[blueberries\]`) + if !expectedLine.MatchString(output.String()) { + t.Errorf("output did not match regexp /%s/\n> output\n%s\n", expectedLine, output) + return + } + + unexpectedLines := []*regexp.Regexp{ + regexp.MustCompile(`#9 Blueberries are a good fruit \[blueberries\] - Merged`), + regexp.MustCompile(`#8 Blueberries are probably a good fruit \[blueberries\] - Closed`), + } + for _, r := range unexpectedLines { + if r.MatchString(output.String()) { + t.Errorf("output unexpectedly match regexp /%s/\n> output\n%s\n", r, output) + return + } + } +} + func TestPRStatus_blankSlate(t *testing.T) { initBlankContext("OWNER/REPO", "blueberries") http := initFakeHTTP() diff --git a/test/fixtures/prStatusCurrentBranch.json b/test/fixtures/prStatusCurrentBranch.json new file mode 100644 index 000000000..d024d48c3 --- /dev/null +++ b/test/fixtures/prStatusCurrentBranch.json @@ -0,0 +1,61 @@ +{ + "data": { + "repository": { + "pullRequests": { + "totalCount": 3, + "edges": [ + { + "node": { + "number": 10, + "title": "Blueberries are certainly a good fruit", + "state": "OPEN", + "url": "https://github.com/PARENT/REPO/pull/10", + "headRefName": "blueberries", + "isDraft": false, + "headRepositoryOwner": { + "login": "OWNER/REPO" + }, + "isCrossRepository": false + } + }, + { + "node": { + "number": 9, + "title": "Blueberries are a good fruit", + "state": "MERGED", + "url": "https://github.com/PARENT/REPO/pull/9", + "headRefName": "blueberries", + "isDraft": false, + "headRepositoryOwner": { + "login": "OWNER/REPO" + }, + "isCrossRepository": false + } + }, + { + "node": { + "number": 8, + "title": "Blueberries are probably a good fruit", + "state": "CLOSED", + "url": "https://github.com/PARENT/REPO/pull/8", + "headRefName": "blueberries", + "isDraft": false, + "headRepositoryOwner": { + "login": "OWNER/REPO" + }, + "isCrossRepository": false + } + } + ] + } + }, + "viewerCreated": { + "totalCount": 0, + "edges": [] + }, + "reviewRequested": { + "totalCount": 0, + "edges": [] + } + } +} From bc27ba2a75f4646ea28170bd333de2cbf1c1c7bb Mon Sep 17 00:00:00 2001 From: Toshiya Doi Date: Fri, 13 Mar 2020 00:25:17 +0900 Subject: [PATCH 34/38] Only show the most recent PR for the current branch --- command/pr.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/pr.go b/command/pr.go index 7a1b00047..94be3897c 100644 --- a/command/pr.go +++ b/command/pr.go @@ -99,7 +99,7 @@ func prStatus(cmd *cobra.Command, args []string) error { printHeader(out, "Current branch") if prPayload.CurrentPRs != nil { - printPrs(out, 0, prPayload.CurrentPRs...) + printPrs(out, 0, prPayload.CurrentPRs[0]) // Assume it is the most recent PR for the current branch } else { message := fmt.Sprintf(" There is no pull request associated with %s", utils.Cyan("["+currentPRHeadRef+"]")) printMessage(out, message) From a7110a70650aff5462543ed3f0aa3472ac69ba68 Mon Sep 17 00:00:00 2001 From: Ian Schmitz Date: Thu, 12 Mar 2020 15:50:34 -0700 Subject: [PATCH 35/38] Update Feodra install instructions Updated instructions to use `dnf` package manager instead of `yum`, which has been the default package manager in Fedora since Fedora 22 (2015). --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b0ef25606..3259510a2 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,14 @@ Install and upgrade: 1. Download the `.deb` file from the [releases page][] 2. `sudo apt install git && sudo dpkg -i gh_*_linux_amd64.deb` install the downloaded file -### Fedora/Centos Linux +### Fedora Linux + +Install and upgrade: + +1. Download the `.rpm` file from the [releases page][] +2. `sudo dnf install gh_*_linux_amd64.rpm` install the downloaded file + +### Centos Linux Install and upgrade: From 4c4aeb5ec614a03f2a66163b1f5157d930ca3f62 Mon Sep 17 00:00:00 2001 From: Toshiya Doi Date: Fri, 13 Mar 2020 09:49:20 +0900 Subject: [PATCH 36/38] Return the latest PR belonging to the current branch --- api/queries_pr.go | 13 ++++++------- command/pr.go | 4 ++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/api/queries_pr.go b/api/queries_pr.go index 34884d16f..ee80c2f7f 100644 --- a/api/queries_pr.go +++ b/api/queries_pr.go @@ -10,7 +10,7 @@ import ( type PullRequestsPayload struct { ViewerCreated PullRequestAndTotalCount ReviewRequested PullRequestAndTotalCount - CurrentPRs []PullRequest + CurrentPR *PullRequest } type PullRequestAndTotalCount struct { @@ -262,13 +262,12 @@ func PullRequests(client *Client, repo ghrepo.Interface, currentPRNumber int, cu reviewRequested = append(reviewRequested, edge.Node) } - var currentPRs []PullRequest - if resp.Repository.PullRequest != nil { - currentPRs = append(currentPRs, *resp.Repository.PullRequest) - } else { + var currentPR = resp.Repository.PullRequest + if currentPR == nil { for _, edge := range resp.Repository.PullRequests.Edges { if edge.Node.HeadLabel() == currentPRHeadRef { - currentPRs = append(currentPRs, edge.Node) + currentPR = &edge.Node + break // Take the most recent PR for the current branch } } } @@ -282,7 +281,7 @@ func PullRequests(client *Client, repo ghrepo.Interface, currentPRNumber int, cu PullRequests: reviewRequested, TotalCount: resp.ReviewRequested.TotalCount, }, - CurrentPRs: currentPRs, + CurrentPR: currentPR, } return &payload, nil diff --git a/command/pr.go b/command/pr.go index 94be3897c..e1c8e796b 100644 --- a/command/pr.go +++ b/command/pr.go @@ -98,8 +98,8 @@ func prStatus(cmd *cobra.Command, args []string) error { fmt.Fprintln(out, "") printHeader(out, "Current branch") - if prPayload.CurrentPRs != nil { - printPrs(out, 0, prPayload.CurrentPRs[0]) // Assume it is the most recent PR for the current branch + if prPayload.CurrentPR != nil { + printPrs(out, 0, *prPayload.CurrentPR) } else { message := fmt.Sprintf(" There is no pull request associated with %s", utils.Cyan("["+currentPRHeadRef+"]")) printMessage(out, message) From 5e3aa31cf1436b06dd4587f900f810fb9e46a37c Mon Sep 17 00:00:00 2001 From: Toshiya Doi Date: Fri, 13 Mar 2020 23:58:41 +0900 Subject: [PATCH 37/38] Fix 'gh issue view' usage description --- command/issue.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/issue.go b/command/issue.go index 8d83e392b..91f2aa2a3 100644 --- a/command/issue.go +++ b/command/issue.go @@ -67,7 +67,7 @@ var issueStatusCmd = &cobra.Command{ RunE: issueStatus, } var issueViewCmd = &cobra.Command{ - Use: "view { | | }", + Use: "view { | }", Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { return FlagError{errors.New("issue number or URL required as argument")} From 102cfdb1a25c7275ac6bb335a8dcf7c9784e01df Mon Sep 17 00:00:00 2001 From: Toshiya Doi Date: Fri, 13 Mar 2020 23:59:18 +0900 Subject: [PATCH 38/38] Rearrange the order of command initialization --- command/issue.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/issue.go b/command/issue.go index 91f2aa2a3..2b85efc75 100644 --- a/command/issue.go +++ b/command/issue.go @@ -23,7 +23,6 @@ import ( func init() { RootCmd.AddCommand(issueCmd) issueCmd.AddCommand(issueStatusCmd) - issueCmd.AddCommand(issueViewCmd) issueCmd.AddCommand(issueCreateCmd) issueCreateCmd.Flags().StringP("title", "t", "", @@ -39,6 +38,7 @@ func init() { issueListCmd.Flags().IntP("limit", "L", 30, "Maximum number of issues to fetch") issueListCmd.Flags().StringP("author", "A", "", "Filter by author") + issueCmd.AddCommand(issueViewCmd) issueViewCmd.Flags().BoolP("preview", "p", false, "Display preview of issue content") }