From e0897fd8e8ba9ec370b1710fe21719aa457516a5 Mon Sep 17 00:00:00 2001 From: Adarsh K Kumar Date: Thu, 7 Oct 2021 23:09:21 +0530 Subject: [PATCH 01/22] #2720 | Add patch flag to pull-request diff command --- api/queries_pr.go | 8 ++++++-- pkg/cmd/pr/diff/diff.go | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/api/queries_pr.go b/api/queries_pr.go index 388446f42..7a9c8f129 100644 --- a/api/queries_pr.go +++ b/api/queries_pr.go @@ -267,7 +267,7 @@ func (pr *PullRequest) DisplayableReviews() PullRequestReviews { return PullRequestReviews{Nodes: published, TotalCount: len(published)} } -func (c Client) PullRequestDiff(baseRepo ghrepo.Interface, prNumber int) (io.ReadCloser, error) { +func (c Client) PullRequestDiff(baseRepo ghrepo.Interface, prNumber int, patch bool) (io.ReadCloser, error) { url := fmt.Sprintf("%srepos/%s/pulls/%d", ghinstance.RESTPrefix(baseRepo.RepoHost()), ghrepo.FullName(baseRepo), prNumber) req, err := http.NewRequest("GET", url, nil) @@ -275,7 +275,11 @@ func (c Client) PullRequestDiff(baseRepo ghrepo.Interface, prNumber int) (io.Rea return nil, err } - req.Header.Set("Accept", "application/vnd.github.v3.diff; charset=utf-8") + if patch { + req.Header.Set("Accept", "application/vnd.github.v3.patch") + } else { + req.Header.Set("Accept", "application/vnd.github.v3.diff; charset=utf-8") + } resp, err := c.http.Do(req) if err != nil { diff --git a/pkg/cmd/pr/diff/diff.go b/pkg/cmd/pr/diff/diff.go index 5f8f43d46..35dd732d1 100644 --- a/pkg/cmd/pr/diff/diff.go +++ b/pkg/cmd/pr/diff/diff.go @@ -25,6 +25,7 @@ type DiffOptions struct { SelectorArg string UseColor string + Patch bool } func NewCmdDiff(f *cmdutil.Factory, runF func(*DiffOptions) error) *cobra.Command { @@ -70,6 +71,7 @@ func NewCmdDiff(f *cmdutil.Factory, runF func(*DiffOptions) error) *cobra.Comman } cmd.Flags().StringVar(&opts.UseColor, "color", "auto", "Use color in diff output: {always|never|auto}") + cmd.Flags().BoolVar(&opts.Patch, "patch", false, "Display diff in patch format") return cmd } @@ -90,7 +92,7 @@ func diffRun(opts *DiffOptions) error { } apiClient := api.NewClientFromHTTP(httpClient) - diff, err := apiClient.PullRequestDiff(baseRepo, pr.Number) + diff, err := apiClient.PullRequestDiff(baseRepo, pr.Number, opts.Patch) if err != nil { return fmt.Errorf("could not find pull request diff: %w", err) } From 79832c1b04ad0d0a01897c1f5992bbefde44d584 Mon Sep 17 00:00:00 2001 From: Luciano Zago Date: Fri, 8 Oct 2021 06:14:49 -0300 Subject: [PATCH 02/22] Add instructions to gh installation via spack (#4412) --- README.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 71630a5cc..1259738e2 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ If anything feels off, or if you feel that some functionality is missing, please ### macOS -`gh` is available via [Homebrew][], [MacPorts][], [Conda][], and as a downloadable binary from the [releases page][]. +`gh` is available via [Homebrew][], [MacPorts][], [Conda][], [Spack][], and as a downloadable binary from the [releases page][]. #### Homebrew @@ -41,15 +41,21 @@ If anything feels off, or if you feel that some functionality is missing, please Additional Conda installation options available on the [gh-feedstock page](https://github.com/conda-forge/gh-feedstock#installing-gh). +#### Spack + +| Install: | Upgrade: | +| ------------------ | ---------------------------------------- | +| `spack install gh` | `spack uninstall gh && spack install gh` | + ### Linux & BSD -`gh` is available via [Homebrew](#homebrew), [Conda](#Conda), and as downloadable binaries from the [releases page][]. +`gh` is available via [Homebrew](#homebrew), [Conda](#conda), [Spack](#spack), and as downloadable binaries from the [releases page][]. For instructions on specific distributions and package managers, see [Linux & BSD installation](./docs/install_linux.md). ### Windows -`gh` is available via [WinGet][], [scoop][], [Chocolatey][], [Conda](#Conda), and as downloadable MSI. +`gh` is available via [WinGet][], [scoop][], [Chocolatey][], [Conda](#conda), and as downloadable MSI. #### WinGet @@ -99,6 +105,7 @@ tool. Check out our [more detailed explanation][gh-vs-hub] to learn more. [scoop]: https://scoop.sh [Chocolatey]: https://chocolatey.org [Conda]: https://docs.conda.io/en/latest/ +[Spack]: https://spack.io [releases page]: https://github.com/cli/cli/releases/latest [hub]: https://github.com/github/hub [contributing]: ./.github/CONTRIBUTING.md From e4c8aa3b2bb50ee2f41f307eacf5b2026ef6911e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Fri, 8 Oct 2021 13:53:19 +0200 Subject: [PATCH 03/22] Add tests for `pr diff --patch` --- api/queries_pr.go | 30 ------------------- pkg/cmd/pr/diff/diff.go | 35 ++++++++++++++++++++-- pkg/cmd/pr/diff/diff_test.go | 56 +++++++++++++++++++++++++++++++----- 3 files changed, 82 insertions(+), 39 deletions(-) diff --git a/api/queries_pr.go b/api/queries_pr.go index 7a9c8f129..744b4c9e2 100644 --- a/api/queries_pr.go +++ b/api/queries_pr.go @@ -2,9 +2,7 @@ package api import ( "context" - "errors" "fmt" - "io" "net/http" "strings" "time" @@ -267,34 +265,6 @@ func (pr *PullRequest) DisplayableReviews() PullRequestReviews { return PullRequestReviews{Nodes: published, TotalCount: len(published)} } -func (c Client) PullRequestDiff(baseRepo ghrepo.Interface, prNumber int, patch bool) (io.ReadCloser, error) { - url := fmt.Sprintf("%srepos/%s/pulls/%d", - ghinstance.RESTPrefix(baseRepo.RepoHost()), ghrepo.FullName(baseRepo), prNumber) - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return nil, err - } - - if patch { - req.Header.Set("Accept", "application/vnd.github.v3.patch") - } else { - req.Header.Set("Accept", "application/vnd.github.v3.diff; charset=utf-8") - } - - resp, err := c.http.Do(req) - if err != nil { - return nil, err - } - - if resp.StatusCode == 404 { - return nil, errors.New("pull request not found") - } else if resp.StatusCode != 200 { - return nil, HandleHTTPError(resp) - } - - return resp.Body, nil -} - type pullRequestFeature struct { HasReviewDecision bool HasStatusCheckRollup bool diff --git a/pkg/cmd/pr/diff/diff.go b/pkg/cmd/pr/diff/diff.go index 35dd732d1..d89fe868a 100644 --- a/pkg/cmd/pr/diff/diff.go +++ b/pkg/cmd/pr/diff/diff.go @@ -11,6 +11,8 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/ghinstance" + "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmd/pr/shared" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/iostreams" @@ -90,9 +92,8 @@ func diffRun(opts *DiffOptions) error { if err != nil { return err } - apiClient := api.NewClientFromHTTP(httpClient) - diff, err := apiClient.PullRequestDiff(baseRepo, pr.Number, opts.Patch) + diff, err := fetchDiff(httpClient, baseRepo, pr.Number, opts.Patch) if err != nil { return fmt.Errorf("could not find pull request diff: %w", err) } @@ -134,6 +135,36 @@ func diffRun(opts *DiffOptions) error { return nil } +func fetchDiff(httpClient *http.Client, baseRepo ghrepo.Interface, prNumber int, asPatch bool) (io.ReadCloser, error) { + url := fmt.Sprintf( + "%srepos/%s/pulls/%d", + ghinstance.RESTPrefix(baseRepo.RepoHost()), + ghrepo.FullName(baseRepo), + prNumber, + ) + acceptType := "application/vnd.github.v3.diff" + if asPatch { + acceptType = "application/vnd.github.v3.patch" + } + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + + req.Header.Set("Accept", acceptType) + + resp, err := httpClient.Do(req) + if err != nil { + return nil, err + } + if resp.StatusCode != 200 { + return nil, api.HandleHTTPError(resp) + } + + return resp.Body, nil +} + var diffHeaderPrefixes = []string{"+++", "---", "diff", "index"} func isHeaderLine(dl string) bool { diff --git a/pkg/cmd/pr/diff/diff_test.go b/pkg/cmd/pr/diff/diff_test.go index adf45a123..67ee822d9 100644 --- a/pkg/cmd/pr/diff/diff_test.go +++ b/pkg/cmd/pr/diff/diff_test.go @@ -4,6 +4,7 @@ import ( "bytes" "io/ioutil" "net/http" + "strings" "testing" "github.com/cli/cli/v2/api" @@ -140,26 +141,67 @@ func runCommand(rt http.RoundTripper, remotes context.Remotes, isTTY bool, cli s }, err } -func TestPRDiff_notty(t *testing.T) { - http := &httpmock.Registry{} - defer http.Verify(t) +func TestPRDiff_notty_diff(t *testing.T) { + httpReg := &httpmock.Registry{} + defer httpReg.Verify(t) shared.RunCommandFinder("", &api.PullRequest{Number: 123}, ghrepo.New("OWNER", "REPO")) - http.Register( + var gotAccept string + httpReg.Register( httpmock.REST("GET", "repos/OWNER/REPO/pulls/123"), - httpmock.StringResponse(testDiff)) + func(req *http.Request) (*http.Response, error) { + gotAccept = req.Header.Get("Accept") + return &http.Response{ + StatusCode: 200, + Request: req, + Body: ioutil.NopCloser(strings.NewReader(testDiff)), + }, nil + }) - output, err := runCommand(http, nil, false, "") + output, err := runCommand(httpReg, nil, false, "") if err != nil { t.Fatalf("unexpected error: %s", err) } if diff := cmp.Diff(testDiff, output.String()); diff != "" { t.Errorf("command output did not match:\n%s", diff) } + if gotAccept != "application/vnd.github.v3.diff" { + t.Errorf("unexpected Accept header: %s", gotAccept) + } } -func TestPRDiff_tty(t *testing.T) { +func TestPRDiff_notty_patch(t *testing.T) { + httpReg := &httpmock.Registry{} + defer httpReg.Verify(t) + + shared.RunCommandFinder("", &api.PullRequest{Number: 123}, ghrepo.New("OWNER", "REPO")) + + var gotAccept string + httpReg.Register( + httpmock.REST("GET", "repos/OWNER/REPO/pulls/123"), + func(req *http.Request) (*http.Response, error) { + gotAccept = req.Header.Get("Accept") + return &http.Response{ + StatusCode: 200, + Request: req, + Body: ioutil.NopCloser(strings.NewReader(testDiff)), + }, nil + }) + + output, err := runCommand(httpReg, nil, false, "--patch") + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + if diff := cmp.Diff(testDiff, output.String()); diff != "" { + t.Errorf("command output did not match:\n%s", diff) + } + if gotAccept != "application/vnd.github.v3.patch" { + t.Errorf("unexpected Accept header: %s", gotAccept) + } +} + +func TestPRDiff_tty_diff(t *testing.T) { http := &httpmock.Registry{} defer http.Verify(t) From 706dede7acfee08c2b4f09d9ab64f81bdb197d72 Mon Sep 17 00:00:00 2001 From: flying-cow <42328488+neil465@users.noreply.github.com> Date: Sun, 10 Oct 2021 19:41:30 -0500 Subject: [PATCH 04/22] Enable dependabot to get security updates and if needed version updates on dependencies https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically Having knowledge about vulnerabilities of the dependencies helps the project owners decide on their dependencies security posture to make decisions. If the project decides to get updates only on security updates and not on any version updates then setting these options would not open any PR 's open-pull-requests-limit: 0 --- .github/dependabot.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..4bdfc347a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: +- package-ecosystem: gomod + directory: "/" + schedule: + interval: "daily" +- package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" From 98fa94cc6b15c1793dfb9aa6ee6601ee699d3f1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 08:53:52 +0000 Subject: [PATCH 05/22] Bump github.com/google/go-cmp from 0.5.5 to 0.5.6 Bumps [github.com/google/go-cmp](https://github.com/google/go-cmp) from 0.5.5 to 0.5.6. - [Release notes](https://github.com/google/go-cmp/releases) - [Commits](https://github.com/google/go-cmp/compare/v0.5.5...v0.5.6) --- updated-dependencies: - dependency-name: github.com/google/go-cmp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 89885a50a..5f5baf58d 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/creack/pty v1.1.13 github.com/fatih/camelcase v1.0.0 github.com/gabriel-vasile/mimetype v1.1.2 - github.com/google/go-cmp v0.5.5 + github.com/google/go-cmp v0.5.6 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/gorilla/websocket v1.4.2 github.com/hashicorp/go-version v1.2.1 diff --git a/go.sum b/go.sum index 190c0edb7..2ca226a18 100644 --- a/go.sum +++ b/go.sum @@ -160,8 +160,9 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= From b3947a7a94fe4c25c466ef419a64b332b49e4f97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 08:54:15 +0000 Subject: [PATCH 06/22] Bump github.com/briandowns/spinner from 1.11.1 to 1.16.0 Bumps [github.com/briandowns/spinner](https://github.com/briandowns/spinner) from 1.11.1 to 1.16.0. - [Release notes](https://github.com/briandowns/spinner/releases) - [Commits](https://github.com/briandowns/spinner/compare/v1.11.1...v1.16.0) --- updated-dependencies: - dependency-name: github.com/briandowns/spinner dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 89885a50a..dc1aa062b 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.16 require ( github.com/AlecAivazis/survey/v2 v2.3.2 github.com/MakeNowJust/heredoc v1.0.0 - github.com/briandowns/spinner v1.11.1 + github.com/briandowns/spinner v1.16.0 github.com/charmbracelet/glamour v0.3.0 github.com/cli/browser v1.1.0 github.com/cli/oauth v0.8.0 diff --git a/go.sum b/go.sum index 190c0edb7..a0e9710c6 100644 --- a/go.sum +++ b/go.sum @@ -62,8 +62,8 @@ github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuP github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/briandowns/spinner v1.11.1 h1:OixPqDEcX3juo5AjQZAnFPbeUA0jvkp2qzB5gOZJ/L0= -github.com/briandowns/spinner v1.11.1/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ= +github.com/briandowns/spinner v1.16.0 h1:DFmp6hEaIx2QXXuqSJmtfSBSAjRmpGiKG6ip2Wm/yOs= +github.com/briandowns/spinner v1.16.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/charmbracelet/glamour v0.3.0 h1:3H+ZrKlSg8s+WU6V7eF2eRVYt8lCueffbi7r2+ffGkc= github.com/charmbracelet/glamour v0.3.0/go.mod h1:TzF0koPZhqq0YVBNL100cPHznAAjVj7fksX2RInwjGw= From e472f46083fecdca5a40f1c7578cd0ab31e4b0b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 08:54:27 +0000 Subject: [PATCH 07/22] Bump github.com/muesli/termenv from 0.8.1 to 0.9.0 Bumps [github.com/muesli/termenv](https://github.com/muesli/termenv) from 0.8.1 to 0.9.0. - [Release notes](https://github.com/muesli/termenv/releases) - [Commits](https://github.com/muesli/termenv/compare/v0.8.1...v0.9.0) --- updated-dependencies: - dependency-name: github.com/muesli/termenv dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 89885a50a..135698bc6 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/mattn/go-isatty v0.0.13 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d github.com/muesli/reflow v0.2.1-0.20210502190812-c80126ec2ad5 - github.com/muesli/termenv v0.8.1 + github.com/muesli/termenv v0.9.0 github.com/muhammadmuzzammil1998/jsonc v0.0.0-20201229145248-615b0916ca38 github.com/olekukonko/tablewriter v0.0.5 github.com/opentracing/opentracing-go v1.1.0 diff --git a/go.sum b/go.sum index 190c0edb7..cb5c19ac0 100644 --- a/go.sum +++ b/go.sum @@ -256,8 +256,9 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= @@ -278,8 +279,9 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/muesli/reflow v0.2.0/go.mod h1:qT22vjVmM9MIUeLgsVYe/Ye7eZlbv9dZjL3dVhUqLX8= github.com/muesli/reflow v0.2.1-0.20210502190812-c80126ec2ad5 h1:T+Fc6qGlSfM+z0JPlp+n5rijvlg6C6JYFSNaqnCifDU= github.com/muesli/reflow v0.2.1-0.20210502190812-c80126ec2ad5/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= -github.com/muesli/termenv v0.8.1 h1:9q230czSP3DHVpkaPDXGp0TOfAwyjyYwXlUCQxQSaBk= github.com/muesli/termenv v0.8.1/go.mod h1:kzt/D/4a88RoheZmwfqorY3A+tnsSMA9HJC/fQSFKo0= +github.com/muesli/termenv v0.9.0 h1:wnbOaGz+LUR3jNT0zOzinPnyDaCZUQRZj9GxK8eRVl8= +github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw= github.com/muhammadmuzzammil1998/jsonc v0.0.0-20201229145248-615b0916ca38 h1:0FrBxrkJ0hVembTb/e4EU5Ml6vLcOusAqymmYISg5Uo= github.com/muhammadmuzzammil1998/jsonc v0.0.0-20201229145248-615b0916ca38/go.mod h1:saF2fIVw4banK0H4+/EuqfFLpRnoy5S+ECwTOCcRcSU= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= From dabaa5ad7ddde85b455ea21c94bc11388dfaed83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Mon, 11 Oct 2021 11:17:48 +0200 Subject: [PATCH 08/22] CodeQL-Dependabot compatibility Configure the CodeQL workflow to avoid running for pushes on all pull requests because that causes problems with Dependabot PRs. https://github.com/cli/cli/pull/4475/checks?check_run_id=3857074760 --- .github/workflows/codeql.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9e3848c26..d46c0bcf3 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,7 +2,11 @@ name: Code Scanning on: push: + branches: [trunk] pull_request: + branches: [trunk] + paths-ignore: + - '**/*.md' schedule: - cron: "0 0 * * 0" From 2819deb15b6770fc2dd6f24fc6fa8ef89956ce86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Mon, 11 Oct 2021 11:33:44 +0200 Subject: [PATCH 09/22] Avoid applying human-oriented PR automation to PRs from bots --- .github/workflows/prauto.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/prauto.yml b/.github/workflows/prauto.yml index 20a88b31e..58930656a 100644 --- a/.github/workflows/prauto.yml +++ b/.github/workflows/prauto.yml @@ -15,6 +15,7 @@ jobs: PRNUM: ${{ github.event.pull_request.number }} PRHEAD: ${{ github.event.pull_request.head.label }} PRAUTHOR: ${{ github.event.pull_request.user.login }} + PR_AUTHOR_TYPE: ${{ github.event.pull_request.user.type }} if: "!github.event.pull_request.draft" run: | commentPR () { @@ -42,7 +43,7 @@ jobs: ' -f colID="$(colID "Needs review")" -f prID="$PRID" } - if gh api orgs/cli/public_members/$PRAUTHOR --silent 2>/dev/null + if [ "$PR_AUTHOR_TYPE" = "Bot" ] || gh api orgs/cli/public_members/$PRAUTHOR --silent 2>/dev/null then if ! errtext="$(addToBoard 2>&1)" then From 3018770846358d6dc552109fb56975d9a8c1e253 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 09:43:40 +0000 Subject: [PATCH 10/22] Bump github.com/mattn/go-isatty from 0.0.13 to 0.0.14 Bumps [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) from 0.0.13 to 0.0.14. - [Release notes](https://github.com/mattn/go-isatty/releases) - [Commits](https://github.com/mattn/go-isatty/compare/v0.0.13...v0.0.14) --- updated-dependencies: - dependency-name: github.com/mattn/go-isatty dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 3fdce55ae..ef18b8095 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/itchyny/gojq v0.12.4 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/mattn/go-colorable v0.1.8 - github.com/mattn/go-isatty v0.0.13 + github.com/mattn/go-isatty v0.0.14 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d github.com/muesli/reflow v0.2.1-0.20210502190812-c80126ec2ad5 github.com/muesli/termenv v0.9.0 @@ -39,7 +39,7 @@ require ( github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 + golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c golang.org/x/term v0.0.0-20210503060354-a79de5458b56 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) diff --git a/go.sum b/go.sum index ead6f7452..727fad368 100644 --- a/go.sum +++ b/go.sum @@ -254,8 +254,9 @@ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= @@ -504,8 +505,9 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= From a131644d858c2204ac740e6f617d65d82f43fe1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 14:25:14 +0000 Subject: [PATCH 11/22] Bump github.com/mattn/go-colorable from 0.1.8 to 0.1.11 Bumps [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable) from 0.1.8 to 0.1.11. - [Release notes](https://github.com/mattn/go-colorable/releases) - [Commits](https://github.com/mattn/go-colorable/compare/v0.1.8...v0.1.11) --- updated-dependencies: - dependency-name: github.com/mattn/go-colorable dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 55b1d6890..02b19396a 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/henvic/httpretty v0.0.6 github.com/itchyny/gojq v0.12.4 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 - github.com/mattn/go-colorable v0.1.8 + github.com/mattn/go-colorable v0.1.11 github.com/mattn/go-isatty v0.0.14 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d github.com/muesli/reflow v0.2.1-0.20210502190812-c80126ec2ad5 @@ -39,7 +39,7 @@ require ( github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 golang.org/x/term v0.0.0-20210503060354-a79de5458b56 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) diff --git a/go.sum b/go.sum index e12e095f1..52dbdc590 100644 --- a/go.sum +++ b/go.sum @@ -249,8 +249,8 @@ github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPK github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= @@ -506,8 +506,9 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= From 668d7dc8ffe086e5f7bf3ed6c043ebc328fbba66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 14:25:26 +0000 Subject: [PATCH 12/22] Bump github.com/cpuguy83/go-md2man/v2 from 2.0.0 to 2.0.1 Bumps [github.com/cpuguy83/go-md2man/v2](https://github.com/cpuguy83/go-md2man) from 2.0.0 to 2.0.1. - [Release notes](https://github.com/cpuguy83/go-md2man/releases) - [Commits](https://github.com/cpuguy83/go-md2man/compare/v2.0.0...v2.0.1) --- updated-dependencies: - dependency-name: github.com/cpuguy83/go-md2man/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 55b1d6890..abd2c54f9 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/cli/browser v1.1.0 github.com/cli/oauth v0.8.0 github.com/cli/safeexec v1.0.0 - github.com/cpuguy83/go-md2man/v2 v2.0.0 + github.com/cpuguy83/go-md2man/v2 v2.0.1 github.com/creack/pty v1.1.13 github.com/fatih/camelcase v1.0.0 github.com/gabriel-vasile/mimetype v1.1.2 diff --git a/go.sum b/go.sum index e12e095f1..70520fa2d 100644 --- a/go.sum +++ b/go.sum @@ -87,8 +87,9 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.13 h1:rTPnd/xocYRjutMfqide2zle1u96upp1gm6eUHKi7us= github.com/creack/pty v1.1.13/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= @@ -304,15 +305,15 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/githubv4 v0.0.0-20200928013246-d292edc3691b h1:0/ecDXh/HTHRtSDSFnD2/Ta1yQ5J76ZspVY4u0/jGFk= github.com/shurcooL/githubv4 v0.0.0-20200928013246-d292edc3691b/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= From c20a5c28327965600e78356a7c86241a016f0c22 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 14:25:33 +0000 Subject: [PATCH 13/22] Bump github.com/hashicorp/go-version from 1.2.1 to 1.3.0 Bumps [github.com/hashicorp/go-version](https://github.com/hashicorp/go-version) from 1.2.1 to 1.3.0. - [Release notes](https://github.com/hashicorp/go-version/releases) - [Changelog](https://github.com/hashicorp/go-version/blob/master/CHANGELOG.md) - [Commits](https://github.com/hashicorp/go-version/compare/v1.2.1...v1.3.0) --- updated-dependencies: - dependency-name: github.com/hashicorp/go-version dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 55b1d6890..f085f4c78 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/google/go-cmp v0.5.6 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/gorilla/websocket v1.4.2 - github.com/hashicorp/go-version v1.2.1 + github.com/hashicorp/go-version v1.3.0 github.com/henvic/httpretty v0.0.6 github.com/itchyny/gojq v0.12.4 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 diff --git a/go.sum b/go.sum index e12e095f1..8399f148f 100644 --- a/go.sum +++ b/go.sum @@ -204,8 +204,8 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= -github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= +github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= From 32c17b2d9538c7005acfe8c99c5a40775e3f888d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 15:29:21 +0000 Subject: [PATCH 14/22] Bump github.com/itchyny/gojq from 0.12.4 to 0.12.5 Bumps [github.com/itchyny/gojq](https://github.com/itchyny/gojq) from 0.12.4 to 0.12.5. - [Release notes](https://github.com/itchyny/gojq/releases) - [Changelog](https://github.com/itchyny/gojq/blob/main/CHANGELOG.md) - [Commits](https://github.com/itchyny/gojq/compare/v0.12.4...v0.12.5) --- updated-dependencies: - dependency-name: github.com/itchyny/gojq dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 02b19396a..5afda3f85 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/gorilla/websocket v1.4.2 github.com/hashicorp/go-version v1.2.1 github.com/henvic/httpretty v0.0.6 - github.com/itchyny/gojq v0.12.4 + github.com/itchyny/gojq v0.12.5 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/mattn/go-colorable v0.1.11 github.com/mattn/go-isatty v0.0.14 diff --git a/go.sum b/go.sum index 52dbdc590..fb62e04e7 100644 --- a/go.sum +++ b/go.sum @@ -223,8 +223,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1: github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/itchyny/go-flags v1.5.0/go.mod h1:lenkYuCobuxLBAd/HGFE4LRoW8D3B6iXRQfWYJ+MNbA= -github.com/itchyny/gojq v0.12.4 h1:8zgOZWMejEWCLjbF/1mWY7hY7QEARm7dtuhC6Bp4R8o= -github.com/itchyny/gojq v0.12.4/go.mod h1:EQUSKgW/YaOxmXpAwGiowFDO4i2Rmtk5+9dFyeiymAg= +github.com/itchyny/gojq v0.12.5 h1:6SJ1BQ1VAwJAlIvLSIZmqHP/RUEq3qfVWvsRxrqhsD0= +github.com/itchyny/gojq v0.12.5/go.mod h1:3e1hZXv+Kwvdp6V9HXpVrvddiHVApi5EDZwS+zLFeiE= github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU= github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -504,9 +504,9 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= From 8cc77bd61ee6918217377b286f70afcc9ad5bbe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Mon, 11 Oct 2021 18:05:52 +0200 Subject: [PATCH 15/22] Fix test expectation for man contents https://github.com/cpuguy83/go-md2man/pull/74 --- internal/docs/man_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/docs/man_test.go b/internal/docs/man_test.go index d72ea7214..dd1df2e9b 100644 --- a/internal/docs/man_test.go +++ b/internal/docs/man_test.go @@ -101,7 +101,7 @@ func TestGenManSeeAlso(t *testing.T) { if err := assertNextLineEquals(scanner, ".PP"); err != nil { t.Fatalf("First line after SEE ALSO wasn't break-indent: %v", err) } - if err := assertNextLineEquals(scanner, `\fBroot\-bbb(1)\fP, \fBroot\-ccc(1)\fP`); err != nil { + if err := assertNextLineEquals(scanner, `\fBroot-bbb(1)\fP, \fBroot-ccc(1)\fP`); err != nil { t.Fatalf("Second line after SEE ALSO wasn't correct: %v", err) } } From adc3072303450db0643c7eec9ab00f941aca1cb8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 16:24:32 +0000 Subject: [PATCH 16/22] Bump github.com/creack/pty from 1.1.13 to 1.1.16 Bumps [github.com/creack/pty](https://github.com/creack/pty) from 1.1.13 to 1.1.16. - [Release notes](https://github.com/creack/pty/releases) - [Commits](https://github.com/creack/pty/compare/v1.1.13...v1.1.16) --- updated-dependencies: - dependency-name: github.com/creack/pty dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 89b2dd75d..27448fcf8 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/cli/oauth v0.8.0 github.com/cli/safeexec v1.0.0 github.com/cpuguy83/go-md2man/v2 v2.0.1 - github.com/creack/pty v1.1.13 + github.com/creack/pty v1.1.16 github.com/fatih/camelcase v1.0.0 github.com/gabriel-vasile/mimetype v1.1.2 github.com/google/go-cmp v0.5.6 diff --git a/go.sum b/go.sum index 54b62fc41..58b2ef1f7 100644 --- a/go.sum +++ b/go.sum @@ -90,8 +90,8 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.13 h1:rTPnd/xocYRjutMfqide2zle1u96upp1gm6eUHKi7us= -github.com/creack/pty v1.1.13/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/creack/pty v1.1.16 h1:vfetlOf3A+9YKggibynnX9mnFjuSVvkRj+IWpcTSLEQ= +github.com/creack/pty v1.1.16/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From cedbbe3c2ae569f0f7a011b83930a1930145ee5a Mon Sep 17 00:00:00 2001 From: Mateusz Urbanek Date: Mon, 11 Oct 2021 19:32:40 +0100 Subject: [PATCH 17/22] Add limit flag to codespace list (#4453) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mislav Marohnić --- internal/codespaces/api/api.go | 97 ++++++++++++++++------------- internal/codespaces/api/api_test.go | 59 ++++++++---------- pkg/cmd/codespace/common.go | 4 +- pkg/cmd/codespace/delete.go | 2 +- pkg/cmd/codespace/delete_test.go | 2 +- pkg/cmd/codespace/list.go | 13 +++- pkg/cmd/codespace/mock_api.go | 22 ++++--- 7 files changed, 108 insertions(+), 91 deletions(-) diff --git a/internal/codespaces/api/api.go b/internal/codespaces/api/api.go index 531b2b3f8..1dff0fba9 100644 --- a/internal/codespaces/api/api.go +++ b/internal/codespaces/api/api.go @@ -34,10 +34,13 @@ import ( "fmt" "io/ioutil" "net/http" + "net/url" + "regexp" "strconv" "strings" "time" + "github.com/cli/cli/v2/api" "github.com/opentracing/opentracing-go" ) @@ -190,62 +193,70 @@ type CodespaceEnvironmentConnection struct { HostPublicKeys []string `json:"hostPublicKeys"` } -// codespacesListResponse is the response body for the `/user/codespaces` endpoint -type getCodespacesListResponse struct { - Codespaces []*Codespace `json:"codespaces"` - TotalCount int `json:"total_count"` -} +// ListCodespaces returns a list of codespaces for the user. Pass a negative limit to request all pages from +// the API until all codespaces have been fetched. +func (a *API) ListCodespaces(ctx context.Context, limit int) (codespaces []*Codespace, err error) { + perPage := 100 + if limit > 0 && limit < 100 { + perPage = limit + } -// ListCodespaces returns a list of codespaces for the user. -// It consumes all pages returned by the API until all codespaces have been fetched. -func (a *API) ListCodespaces(ctx context.Context) (codespaces []*Codespace, err error) { - per_page := 100 - for page := 1; ; page++ { - response, err := a.fetchCodespaces(ctx, page, per_page) + listURL := fmt.Sprintf("%s/user/codespaces?per_page=%d", a.githubAPI, perPage) + for { + req, err := http.NewRequest(http.MethodGet, listURL, nil) if err != nil { - return nil, err + return nil, fmt.Errorf("error creating request: %w", err) } + a.setHeaders(req) + + resp, err := a.do(ctx, req, "/user/codespaces") + if err != nil { + return nil, fmt.Errorf("error making request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, api.HandleHTTPError(resp) + } + + var response struct { + Codespaces []*Codespace `json:"codespaces"` + } + dec := json.NewDecoder(resp.Body) + if err := dec.Decode(&response); err != nil { + return nil, fmt.Errorf("error unmarshaling response: %w", err) + } + + nextURL := findNextPage(resp.Header.Get("Link")) codespaces = append(codespaces, response.Codespaces...) - if page*per_page >= response.TotalCount { + + if nextURL == "" || (limit > 0 && len(codespaces) >= limit) { break } + + if newPerPage := limit - len(codespaces); limit > 0 && newPerPage < 100 { + u, _ := url.Parse(nextURL) + q := u.Query() + q.Set("per_page", strconv.Itoa(newPerPage)) + u.RawQuery = q.Encode() + listURL = u.String() + } else { + listURL = nextURL + } } return codespaces, nil } -func (a *API) fetchCodespaces(ctx context.Context, page int, per_page int) (response *getCodespacesListResponse, err error) { - req, err := http.NewRequest( - http.MethodGet, a.githubAPI+"/user/codespaces", nil, - ) - if err != nil { - return nil, fmt.Errorf("error creating request: %w", err) - } - a.setHeaders(req) - q := req.URL.Query() - q.Add("page", strconv.Itoa(page)) - q.Add("per_page", strconv.Itoa(per_page)) +var linkRE = regexp.MustCompile(`<([^>]+)>;\s*rel="([^"]+)"`) - req.URL.RawQuery = q.Encode() - resp, err := a.do(ctx, req, "/user/codespaces") - if err != nil { - return nil, fmt.Errorf("error making request: %w", err) +func findNextPage(linkValue string) string { + for _, m := range linkRE.FindAllStringSubmatch(linkValue, -1) { + if len(m) > 2 && m[2] == "next" { + return m[1] + } } - defer resp.Body.Close() - - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("error reading response body: %w", err) - } - - if resp.StatusCode != http.StatusOK { - return nil, jsonErrorResponse(b) - } - - if err := json.Unmarshal(b, &response); err != nil { - return nil, fmt.Errorf("error unmarshaling response: %w", err) - } - return response, nil + return "" } // GetCodespace returns the user codespace based on the provided name. diff --git a/internal/codespaces/api/api_test.go b/internal/codespaces/api/api_test.go index a9d81e442..e8748fa9a 100644 --- a/internal/codespaces/api/api_test.go +++ b/internal/codespaces/api/api_test.go @@ -44,13 +44,19 @@ func createFakeListEndpointServer(t *testing.T, initalTotal int, finalTotal int) TotalCount: finalTotal, } - if page == 1 { + switch page { + case 1: response.Codespaces = generateCodespaceList(0, per_page) response.TotalCount = initalTotal - } else if page == 2 { + w.Header().Set("Link", fmt.Sprintf(`; rel="last", ; rel="next"`, r.Host, per_page)) + case 2: response.Codespaces = generateCodespaceList(per_page, per_page*2) response.TotalCount = finalTotal - } else { + w.Header().Set("Link", fmt.Sprintf(`; rel="next"`, r.Host, per_page)) + case 3: + response.Codespaces = generateCodespaceList(per_page*2, per_page*3-per_page/2) + response.TotalCount = finalTotal + default: t.Fatal("Should not check extra page") } @@ -59,7 +65,7 @@ func createFakeListEndpointServer(t *testing.T, initalTotal int, finalTotal int) })) } -func TestListCodespaces(t *testing.T) { +func TestListCodespaces_limited(t *testing.T) { svr := createFakeListEndpointServer(t, 200, 200) defer svr.Close() @@ -69,25 +75,24 @@ func TestListCodespaces(t *testing.T) { token: "faketoken", } ctx := context.TODO() - codespaces, err := api.ListCodespaces(ctx) + codespaces, err := api.ListCodespaces(ctx, 200) if err != nil { t.Fatal(err) } - if len(codespaces) != 200 { - t.Fatalf("expected 100 codespace, got %d", len(codespaces)) - } + if len(codespaces) != 200 { + t.Fatalf("expected 200 codespace, got %d", len(codespaces)) + } if codespaces[0].Name != "codespace-0" { t.Fatalf("expected codespace-0, got %s", codespaces[0].Name) } - if codespaces[199].Name != "codespace-199" { t.Fatalf("expected codespace-199, got %s", codespaces[0].Name) } } -func TestMidIterationDeletion(t *testing.T) { - svr := createFakeListEndpointServer(t, 200, 199) +func TestListCodespaces_unlimited(t *testing.T) { + svr := createFakeListEndpointServer(t, 200, 200) defer svr.Close() api := API{ @@ -96,30 +101,18 @@ func TestMidIterationDeletion(t *testing.T) { token: "faketoken", } ctx := context.TODO() - codespaces, err := api.ListCodespaces(ctx) + codespaces, err := api.ListCodespaces(ctx, -1) if err != nil { t.Fatal(err) } - if len(codespaces) != 200 { - t.Fatalf("expected 200 codespace, got %d", len(codespaces)) - } -} - -func TestMidIterationAddition(t *testing.T) { - svr := createFakeListEndpointServer(t, 199, 200) - defer svr.Close() - - api := API{ - githubAPI: svr.URL, - client: &http.Client{}, - token: "faketoken", - } - ctx := context.TODO() - codespaces, err := api.ListCodespaces(ctx) - if err != nil { - t.Fatal(err) - } - if len(codespaces) != 200 { - t.Fatalf("expected 200 codespace, got %d", len(codespaces)) + + if len(codespaces) != 250 { + t.Fatalf("expected 250 codespace, got %d", len(codespaces)) + } + if codespaces[0].Name != "codespace-0" { + t.Fatalf("expected codespace-0, got %s", codespaces[0].Name) + } + if codespaces[249].Name != "codespace-249" { + t.Fatalf("expected codespace-249, got %s", codespaces[0].Name) } } diff --git a/pkg/cmd/codespace/common.go b/pkg/cmd/codespace/common.go index d304f8d0d..b93b9a8d6 100644 --- a/pkg/cmd/codespace/common.go +++ b/pkg/cmd/codespace/common.go @@ -35,7 +35,7 @@ func NewApp(logger *output.Logger, apiClient apiClient) *App { type apiClient interface { GetUser(ctx context.Context) (*api.User, error) GetCodespace(ctx context.Context, name string, includeConnection bool) (*api.Codespace, error) - ListCodespaces(ctx context.Context) ([]*api.Codespace, error) + ListCodespaces(ctx context.Context, limit int) ([]*api.Codespace, error) DeleteCodespace(ctx context.Context, name string) error StartCodespace(ctx context.Context, name string) error CreateCodespace(ctx context.Context, params *api.CreateCodespaceParams) (*api.Codespace, error) @@ -49,7 +49,7 @@ type apiClient interface { var errNoCodespaces = errors.New("you have no codespaces") func chooseCodespace(ctx context.Context, apiClient apiClient) (*api.Codespace, error) { - codespaces, err := apiClient.ListCodespaces(ctx) + codespaces, err := apiClient.ListCodespaces(ctx, -1) if err != nil { return nil, fmt.Errorf("error getting codespaces: %w", err) } diff --git a/pkg/cmd/codespace/delete.go b/pkg/cmd/codespace/delete.go index 2d526dca3..3b1d5e445 100644 --- a/pkg/cmd/codespace/delete.go +++ b/pkg/cmd/codespace/delete.go @@ -62,7 +62,7 @@ func (a *App) Delete(ctx context.Context, opts deleteOptions) (err error) { var codespaces []*api.Codespace nameFilter := opts.codespaceName if nameFilter == "" { - codespaces, err = a.apiClient.ListCodespaces(ctx) + codespaces, err = a.apiClient.ListCodespaces(ctx, -1) if err != nil { return fmt.Errorf("error getting codespaces: %w", err) } diff --git a/pkg/cmd/codespace/delete_test.go b/pkg/cmd/codespace/delete_test.go index 14896098a..0839c9ef4 100644 --- a/pkg/cmd/codespace/delete_test.go +++ b/pkg/cmd/codespace/delete_test.go @@ -164,7 +164,7 @@ func TestDelete(t *testing.T) { }, } if tt.opts.codespaceName == "" { - apiMock.ListCodespacesFunc = func(_ context.Context) ([]*api.Codespace, error) { + apiMock.ListCodespacesFunc = func(_ context.Context, num int) ([]*api.Codespace, error) { return tt.codespaces, nil } } else { diff --git a/pkg/cmd/codespace/list.go b/pkg/cmd/codespace/list.go index 4bc649bf7..2dbbbc7c7 100644 --- a/pkg/cmd/codespace/list.go +++ b/pkg/cmd/codespace/list.go @@ -6,28 +6,35 @@ import ( "os" "github.com/cli/cli/v2/pkg/cmd/codespace/output" + "github.com/cli/cli/v2/pkg/cmdutil" "github.com/spf13/cobra" ) func newListCmd(app *App) *cobra.Command { var asJSON bool + var limit int listCmd := &cobra.Command{ Use: "list", Short: "List your codespaces", Args: noArgsConstraint, RunE: func(cmd *cobra.Command, args []string) error { - return app.List(cmd.Context(), asJSON) + if limit < 1 { + return &cmdutil.FlagError{Err: fmt.Errorf("invalid limit: %v", limit)} + } + + return app.List(cmd.Context(), asJSON, limit) }, } listCmd.Flags().BoolVar(&asJSON, "json", false, "Output as JSON") + listCmd.Flags().IntVarP(&limit, "limit", "L", 30, "Maximum number of codespaces to list") return listCmd } -func (a *App) List(ctx context.Context, asJSON bool) error { - codespaces, err := a.apiClient.ListCodespaces(ctx) +func (a *App) List(ctx context.Context, asJSON bool, limit int) error { + codespaces, err := a.apiClient.ListCodespaces(ctx, limit) if err != nil { return fmt.Errorf("error getting codespaces: %w", err) } diff --git a/pkg/cmd/codespace/mock_api.go b/pkg/cmd/codespace/mock_api.go index df9b6f3b2..d46d793f9 100644 --- a/pkg/cmd/codespace/mock_api.go +++ b/pkg/cmd/codespace/mock_api.go @@ -43,7 +43,7 @@ import ( // GetUserFunc: func(ctx context.Context) (*api.User, error) { // panic("mock out the GetUser method") // }, -// ListCodespacesFunc: func(ctx context.Context) ([]*api.Codespace, error) { +// ListCodespacesFunc: func(ctx context.Context, limit int) ([]*api.Codespace, error) { // panic("mock out the ListCodespaces method") // }, // StartCodespaceFunc: func(ctx context.Context, name string) error { @@ -84,7 +84,7 @@ type apiClientMock struct { GetUserFunc func(ctx context.Context) (*api.User, error) // ListCodespacesFunc mocks the ListCodespaces method. - ListCodespacesFunc func(ctx context.Context) ([]*api.Codespace, error) + ListCodespacesFunc func(ctx context.Context, limit int) ([]*api.Codespace, error) // StartCodespaceFunc mocks the StartCodespace method. StartCodespaceFunc func(ctx context.Context, name string) error @@ -162,6 +162,8 @@ type apiClientMock struct { ListCodespaces []struct { // Ctx is the ctx argument value. Ctx context.Context + // Limit is the limit argument value. + Limit int } // StartCodespace holds details about calls to the StartCodespace method. StartCodespace []struct { @@ -508,29 +510,33 @@ func (mock *apiClientMock) GetUserCalls() []struct { } // ListCodespaces calls ListCodespacesFunc. -func (mock *apiClientMock) ListCodespaces(ctx context.Context) ([]*api.Codespace, error) { +func (mock *apiClientMock) ListCodespaces(ctx context.Context, limit int) ([]*api.Codespace, error) { if mock.ListCodespacesFunc == nil { panic("apiClientMock.ListCodespacesFunc: method is nil but apiClient.ListCodespaces was just called") } callInfo := struct { - Ctx context.Context + Ctx context.Context + Limit int }{ - Ctx: ctx, + Ctx: ctx, + Limit: limit, } mock.lockListCodespaces.Lock() mock.calls.ListCodespaces = append(mock.calls.ListCodespaces, callInfo) mock.lockListCodespaces.Unlock() - return mock.ListCodespacesFunc(ctx) + return mock.ListCodespacesFunc(ctx, limit) } // ListCodespacesCalls gets all the calls that were made to ListCodespaces. // Check the length with: // len(mockedapiClient.ListCodespacesCalls()) func (mock *apiClientMock) ListCodespacesCalls() []struct { - Ctx context.Context + Ctx context.Context + Limit int } { var calls []struct { - Ctx context.Context + Ctx context.Context + Limit int } mock.lockListCodespaces.RLock() calls = mock.calls.ListCodespaces From ec554822b81d3f26095ef6b4eff10ce7cc2f8174 Mon Sep 17 00:00:00 2001 From: Parth <76231594+pxrth9@users.noreply.github.com> Date: Tue, 12 Oct 2021 06:48:40 -0400 Subject: [PATCH 18/22] Add repo archive command (#4410) Co-authored-by: meiji163 --- api/queries_repo.go | 19 +++ pkg/cmd/repo/archive/archive.go | 99 ++++++++++++++++ pkg/cmd/repo/archive/archive_test.go | 165 +++++++++++++++++++++++++++ pkg/cmd/repo/archive/http.go | 32 ++++++ pkg/cmd/repo/repo.go | 2 + pkg/cmd/repo/view/http.go | 19 --- pkg/cmd/repo/view/view.go | 2 +- 7 files changed, 318 insertions(+), 20 deletions(-) create mode 100644 pkg/cmd/repo/archive/archive.go create mode 100644 pkg/cmd/repo/archive/archive_test.go create mode 100644 pkg/cmd/repo/archive/http.go diff --git a/api/queries_repo.go b/api/queries_repo.go index b1e41370d..c9d8b46f9 100644 --- a/api/queries_repo.go +++ b/api/queries_repo.go @@ -230,6 +230,25 @@ func (r Repository) ViewerCanTriage() bool { } } +func FetchRepository(client *Client, repo ghrepo.Interface, fields []string) (*Repository, error) { + query := fmt.Sprintf(`query RepositoryInfo($owner: String!, $name: String!) { + repository(owner: $owner, name: $name) {%s} + }`, RepositoryGraphQL(fields)) + + variables := map[string]interface{}{ + "owner": repo.RepoOwner(), + "name": repo.RepoName(), + } + + var result struct { + Repository Repository + } + if err := client.GraphQL(repo.RepoHost(), query, variables, &result); err != nil { + return nil, err + } + return InitRepoHostname(&result.Repository, repo.RepoHost()), nil +} + func GitHubRepo(client *Client, repo ghrepo.Interface) (*Repository, error) { query := ` fragment repo on Repository { diff --git a/pkg/cmd/repo/archive/archive.go b/pkg/cmd/repo/archive/archive.go new file mode 100644 index 000000000..c4d6be2bb --- /dev/null +++ b/pkg/cmd/repo/archive/archive.go @@ -0,0 +1,99 @@ +package archive + +import ( + "fmt" + "net/http" + "strings" + + "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/ghinstance" + "github.com/cli/cli/v2/internal/ghrepo" + "github.com/cli/cli/v2/pkg/cmdutil" + + "github.com/cli/cli/v2/pkg/iostreams" + "github.com/spf13/cobra" +) + +type ArchiveOptions struct { + HttpClient func() (*http.Client, error) + IO *iostreams.IOStreams + RepoArg string +} + +func NewCmdArchive(f *cmdutil.Factory, runF func(*ArchiveOptions) error) *cobra.Command { + opts := &ArchiveOptions{ + IO: f.IOStreams, + HttpClient: f.HttpClient, + } + + cmd := &cobra.Command{ + DisableFlagsInUseLine: true, + + Use: "archive ", + Short: "Archive a repository", + Long: "Archive a GitHub repository.", + Args: cmdutil.ExactArgs(1, "cannot archive: repository argument required"), + RunE: func(cmd *cobra.Command, args []string) error { + opts.RepoArg = args[0] + if runF != nil { + return runF(opts) + } + return archiveRun(opts) + }, + } + + return cmd +} + +func archiveRun(opts *ArchiveOptions) error { + cs := opts.IO.ColorScheme() + httpClient, err := opts.HttpClient() + if err != nil { + return err + } + apiClient := api.NewClientFromHTTP(httpClient) + + var toArchive ghrepo.Interface + + archiveURL := opts.RepoArg + if !strings.Contains(archiveURL, "/") { + currentUser, err := api.CurrentLoginName(apiClient, ghinstance.Default()) + if err != nil { + return err + } + archiveURL = currentUser + "/" + archiveURL + } + toArchive, err = ghrepo.FromFullName(archiveURL) + if err != nil { + return fmt.Errorf("argument error: %w", err) + } + + fields := []string{"name", "owner", "isArchived", "id"} + repo, err := api.FetchRepository(apiClient, toArchive, fields) + if err != nil { + return err + } + + fullName := ghrepo.FullName(toArchive) + if repo.IsArchived { + fmt.Fprintf(opts.IO.ErrOut, + "%s Repository %s is already archived\n", + cs.WarningIcon(), + fullName) + return nil + } + + err = archiveRepo(httpClient, repo) + if err != nil { + return fmt.Errorf("API called failed: %w", err) + } + + if opts.IO.IsStdoutTTY() { + fmt.Fprintf(opts.IO.Out, + "%s Archived repository %s\n", + cs.SuccessIcon(), + fullName) + } + + return nil +} diff --git a/pkg/cmd/repo/archive/archive_test.go b/pkg/cmd/repo/archive/archive_test.go new file mode 100644 index 000000000..6ed5ecb8f --- /dev/null +++ b/pkg/cmd/repo/archive/archive_test.go @@ -0,0 +1,165 @@ +package archive + +import ( + "bytes" + "net/http" + "testing" + + "github.com/cli/cli/v2/pkg/cmdutil" + "github.com/cli/cli/v2/pkg/httpmock" + "github.com/cli/cli/v2/pkg/iostreams" + "github.com/google/shlex" + "github.com/stretchr/testify/assert" +) + +// probably redundant +func TestNewCmdArchive(t *testing.T) { + tests := []struct { + name string + input string + tty bool + output ArchiveOptions + wantErr bool + errMsg string + }{ + { + name: "valid repo", + input: "cli/cli", + tty: true, + output: ArchiveOptions{ + RepoArg: "cli/cli", + }, + }, + { + name: "no argument", + input: "", + wantErr: true, + tty: true, + output: ArchiveOptions{ + RepoArg: "", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + io, _, _, _ := iostreams.Test() + io.SetStdinTTY(tt.tty) + io.SetStdoutTTY(tt.tty) + f := &cmdutil.Factory{ + IOStreams: io, + } + argv, err := shlex.Split(tt.input) + assert.NoError(t, err) + var gotOpts *ArchiveOptions + cmd := NewCmdArchive(f, func(opts *ArchiveOptions) error { + gotOpts = opts + return nil + }) + cmd.SetArgs(argv) + cmd.SetIn(&bytes.Buffer{}) + cmd.SetOut(&bytes.Buffer{}) + cmd.SetErr(&bytes.Buffer{}) + + _, err = cmd.ExecuteC() + if tt.wantErr { + assert.Error(t, err) + return + } + assert.NoError(t, err) + assert.Equal(t, tt.output.RepoArg, gotOpts.RepoArg) + }) + } +} + +func Test_ArchiveRun(t *testing.T) { + tests := []struct { + name string + opts ArchiveOptions + httpStubs func(*httpmock.Registry) + isTTY bool + wantStdout string + wantStderr string + }{ + { + name: "unarchived repo tty", + opts: ArchiveOptions{RepoArg: "OWNER/REPO"}, + wantStdout: "✓ Archived repository OWNER/REPO\n", + isTTY: true, + httpStubs: func(reg *httpmock.Registry) { + reg.Register( + httpmock.GraphQL(`query RepositoryInfo\b`), + httpmock.StringResponse(`{ "data": { "repository": { + "id": "THE-ID", + "isArchived": false} } }`)) + reg.Register( + httpmock.GraphQL(`mutation ArchiveRepository\b`), + httpmock.StringResponse(`{}`)) + }, + }, + { + name: "unarchived repo notty", + opts: ArchiveOptions{RepoArg: "OWNER/REPO"}, + isTTY: false, + httpStubs: func(reg *httpmock.Registry) { + reg.Register( + httpmock.GraphQL(`query RepositoryInfo\b`), + httpmock.StringResponse(`{ "data": { "repository": { + "id": "THE-ID", + "isArchived": false} } }`)) + reg.Register( + httpmock.GraphQL(`mutation ArchiveRepository\b`), + httpmock.StringResponse(`{}`)) + }, + }, + { + name: "archived repo tty", + opts: ArchiveOptions{RepoArg: "OWNER/REPO"}, + wantStderr: "! Repository OWNER/REPO is already archived\n", + isTTY: true, + httpStubs: func(reg *httpmock.Registry) { + reg.Register( + httpmock.GraphQL(`query RepositoryInfo\b`), + httpmock.StringResponse(`{ "data": { "repository": { + "id": "THE-ID", + "isArchived": true } } }`)) + }, + }, + { + name: "archived repo notty", + opts: ArchiveOptions{RepoArg: "OWNER/REPO"}, + isTTY: false, + wantStderr: "! Repository OWNER/REPO is already archived\n", + httpStubs: func(reg *httpmock.Registry) { + reg.Register( + httpmock.GraphQL(`query RepositoryInfo\b`), + httpmock.StringResponse(`{ "data": { "repository": { + "id": "THE-ID", + "isArchived": true } } }`)) + }, + }, + } + + for _, tt := range tests { + reg := &httpmock.Registry{} + if tt.httpStubs != nil { + tt.httpStubs(reg) + } + tt.opts.HttpClient = func() (*http.Client, error) { + return &http.Client{Transport: reg}, nil + } + + io, _, stdout, stderr := iostreams.Test() + tt.opts.IO = io + + t.Run(tt.name, func(t *testing.T) { + defer reg.Verify(t) + io.SetStdoutTTY(tt.isTTY) + io.SetStderrTTY(tt.isTTY) + + err := archiveRun(&tt.opts) + assert.NoError(t, err) + assert.Equal(t, tt.wantStdout, stdout.String()) + assert.Equal(t, tt.wantStderr, stderr.String()) + }) + } +} diff --git a/pkg/cmd/repo/archive/http.go b/pkg/cmd/repo/archive/http.go new file mode 100644 index 000000000..814fbcd33 --- /dev/null +++ b/pkg/cmd/repo/archive/http.go @@ -0,0 +1,32 @@ +package archive + +import ( + "context" + "net/http" + + "github.com/cli/cli/v2/api" + "github.com/cli/cli/v2/internal/ghinstance" + "github.com/shurcooL/githubv4" + "github.com/shurcooL/graphql" +) + +func archiveRepo(client *http.Client, repo *api.Repository) error { + var mutation struct { + ArchiveRepository struct { + Repository struct { + ID string + } + } `graphql:"archiveRepository(input: $input)"` + } + + variables := map[string]interface{}{ + "input": githubv4.ArchiveRepositoryInput{ + RepositoryID: repo.ID, + }, + } + + host := repo.RepoHost() + gql := graphql.NewClient(ghinstance.GraphQLEndpoint(host), client) + err := gql.MutateNamed(context.Background(), "ArchiveRepository", &mutation, variables) + return err +} diff --git a/pkg/cmd/repo/repo.go b/pkg/cmd/repo/repo.go index 252c322e9..11c36b012 100644 --- a/pkg/cmd/repo/repo.go +++ b/pkg/cmd/repo/repo.go @@ -2,6 +2,7 @@ package repo import ( "github.com/MakeNowJust/heredoc" + repoArchiveCmd "github.com/cli/cli/v2/pkg/cmd/repo/archive" repoCloneCmd "github.com/cli/cli/v2/pkg/cmd/repo/clone" repoCreateCmd "github.com/cli/cli/v2/pkg/cmd/repo/create" creditsCmd "github.com/cli/cli/v2/pkg/cmd/repo/credits" @@ -42,6 +43,7 @@ func NewCmdRepo(f *cmdutil.Factory) *cobra.Command { cmd.AddCommand(repoSyncCmd.NewCmdSync(f, nil)) cmd.AddCommand(creditsCmd.NewCmdRepoCredits(f, nil)) cmd.AddCommand(gardenCmd.NewCmdGarden(f, nil)) + cmd.AddCommand(repoArchiveCmd.NewCmdArchive(f, nil)) return cmd } diff --git a/pkg/cmd/repo/view/http.go b/pkg/cmd/repo/view/http.go index dee1df677..fe2584a90 100644 --- a/pkg/cmd/repo/view/http.go +++ b/pkg/cmd/repo/view/http.go @@ -12,25 +12,6 @@ import ( var NotFoundError = errors.New("not found") -func fetchRepository(apiClient *api.Client, repo ghrepo.Interface, fields []string) (*api.Repository, error) { - query := fmt.Sprintf(`query RepositoryInfo($owner: String!, $name: String!) { - repository(owner: $owner, name: $name) {%s} - }`, api.RepositoryGraphQL(fields)) - - variables := map[string]interface{}{ - "owner": repo.RepoOwner(), - "name": repo.RepoName(), - } - - var result struct { - Repository api.Repository - } - if err := apiClient.GraphQL(repo.RepoHost(), query, variables, &result); err != nil { - return nil, err - } - return api.InitRepoHostname(&result.Repository, repo.RepoHost()), nil -} - type RepoReadme struct { Filename string Content string diff --git a/pkg/cmd/repo/view/view.go b/pkg/cmd/repo/view/view.go index 38524f1a2..ef8a7dfa2 100644 --- a/pkg/cmd/repo/view/view.go +++ b/pkg/cmd/repo/view/view.go @@ -111,7 +111,7 @@ func viewRun(opts *ViewOptions) error { fields = opts.Exporter.Fields() } - repo, err := fetchRepository(apiClient, toView, fields) + repo, err := api.FetchRepository(apiClient, toView, fields) if err != nil { return err } From 5fd9f68c85072f5c6356ea605a07dbacd75422ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Oct 2021 14:35:16 +0000 Subject: [PATCH 19/22] Bump github.com/gabriel-vasile/mimetype from 1.1.2 to 1.4.0 Bumps [github.com/gabriel-vasile/mimetype](https://github.com/gabriel-vasile/mimetype) from 1.1.2 to 1.4.0. - [Release notes](https://github.com/gabriel-vasile/mimetype/releases) - [Commits](https://github.com/gabriel-vasile/mimetype/compare/v1.1.2...v1.4.0) --- updated-dependencies: - dependency-name: github.com/gabriel-vasile/mimetype dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index af02d1453..73e9d82f7 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.1 github.com/creack/pty v1.1.16 github.com/fatih/camelcase v1.0.0 - github.com/gabriel-vasile/mimetype v1.1.2 + github.com/gabriel-vasile/mimetype v1.4.0 github.com/google/go-cmp v0.5.6 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/gorilla/websocket v1.4.2 diff --git a/go.sum b/go.sum index 21e24508c..a6210c532 100644 --- a/go.sum +++ b/go.sum @@ -111,8 +111,8 @@ github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwo github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/gabriel-vasile/mimetype v1.1.2 h1:gaPnPcNor5aZSVCJVSGipcpbgMWiAAj9z182ocSGbHU= -github.com/gabriel-vasile/mimetype v1.1.2/go.mod h1:6CDPel/o/3/s4+bp6kIbsWATq8pmgOisOPG40CJa6To= +github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro= +github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -433,8 +433,9 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 h1:Ugb8sMTWuWRC3+sz5WeN/4kejDx9BvIwnPUiJBjJE+8= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -504,6 +505,7 @@ golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -519,8 +521,9 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From ed3427974c1746c15f7873cd89688beea897855b Mon Sep 17 00:00:00 2001 From: Sam Coe Date: Thu, 30 Sep 2021 08:10:42 -0700 Subject: [PATCH 20/22] Use concurrency to check for extension updates --- pkg/cmd/extension/command_test.go | 4 +- pkg/cmd/extension/extension.go | 25 +++- pkg/cmd/extension/manager.go | 222 ++++++++++++++++++------------ 3 files changed, 158 insertions(+), 93 deletions(-) diff --git a/pkg/cmd/extension/command_test.go b/pkg/cmd/extension/command_test.go index 789b57b91..f1529a63d 100644 --- a/pkg/cmd/extension/command_test.go +++ b/pkg/cmd/extension/command_test.go @@ -214,8 +214,8 @@ func TestNewCmdExtension(t *testing.T) { args: []string{"list"}, managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) { em.ListFunc = func(bool) []extensions.Extension { - ex1 := &Extension{path: "cli/gh-test", url: "https://github.com/cli/gh-test", updateAvailable: false} - ex2 := &Extension{path: "cli/gh-test2", url: "https://github.com/cli/gh-test2", updateAvailable: true} + ex1 := &Extension{path: "cli/gh-test", url: "https://github.com/cli/gh-test", currentVersion: "1", latestVersion: "1"} + ex2 := &Extension{path: "cli/gh-test2", url: "https://github.com/cli/gh-test2", currentVersion: "1", latestVersion: "2"} return []extensions.Extension{ex1, ex2} } return func(t *testing.T) { diff --git a/pkg/cmd/extension/extension.go b/pkg/cmd/extension/extension.go index 39d81c52a..ead9204cd 100644 --- a/pkg/cmd/extension/extension.go +++ b/pkg/cmd/extension/extension.go @@ -7,11 +7,20 @@ import ( const manifestName = "manifest.yml" +type ExtensionKind int + +const ( + GitKind ExtensionKind = iota + BinaryKind +) + type Extension struct { - path string - url string - isLocal bool - updateAvailable bool + path string + url string + isLocal bool + currentVersion string + latestVersion string + kind ExtensionKind } func (e *Extension) Name() string { @@ -31,5 +40,11 @@ func (e *Extension) IsLocal() bool { } func (e *Extension) UpdateAvailable() bool { - return e.updateAvailable + if e.isLocal || + e.currentVersion == "" || + e.latestVersion == "" || + e.currentVersion == e.latestVersion { + return false + } + return true } diff --git a/pkg/cmd/extension/manager.go b/pkg/cmd/extension/manager.go index f238db10b..68071ced0 100644 --- a/pkg/cmd/extension/manager.go +++ b/pkg/cmd/extension/manager.go @@ -14,6 +14,7 @@ import ( "path/filepath" "runtime" "strings" + "sync" "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" @@ -103,111 +104,127 @@ func (m *Manager) Dispatch(args []string, stdin io.Reader, stdout, stderr io.Wri func (m *Manager) List(includeMetadata bool) []extensions.Extension { exts, _ := m.list(includeMetadata) - return exts + r := make([]extensions.Extension, len(exts)) + for i, v := range exts { + val := v + r[i] = &val + } + return r } -func (m *Manager) list(includeMetadata bool) ([]extensions.Extension, error) { +func (m *Manager) list(includeMetadata bool) ([]Extension, error) { dir := m.installDir() entries, err := ioutil.ReadDir(dir) if err != nil { return nil, err } - var results []extensions.Extension + var results []Extension for _, f := range entries { if !strings.HasPrefix(f.Name(), "gh-") { continue } - ext, err := m.parseExtensionDir(f, includeMetadata) - if err != nil { - return nil, err + var ext Extension + var err error + if f.IsDir() { + ext, err = m.parseExtensionDir(f) + if err != nil { + return nil, err + } + results = append(results, ext) + } else { + ext, err = m.parseExtensionFile(f) + if err != nil { + return nil, err + } + results = append(results, ext) } - results = append(results, ext) + } + + if includeMetadata { + m.populateLatestVersions(results) } return results, nil } -func (m *Manager) parseExtensionDir(fi fs.FileInfo, includeMetadata bool) (*Extension, error) { - id := m.installDir() - if _, err := os.Stat(filepath.Join(id, fi.Name(), manifestName)); err == nil { - return m.parseBinaryExtensionDir(fi, includeMetadata) - } - - return m.parseGitExtensionDir(fi, includeMetadata) -} - -func (m *Manager) parseBinaryExtensionDir(fi fs.FileInfo, includeMetadata bool) (*Extension, error) { +func (m *Manager) parseExtensionFile(fi fs.FileInfo) (Extension, error) { + ext := Extension{isLocal: true} id := m.installDir() exePath := filepath.Join(id, fi.Name(), fi.Name()) + if !isSymlink(fi.Mode()) { + // if this is a regular file, its contents is the local directory of the extension + p, err := readPathFromFile(filepath.Join(id, fi.Name())) + if err != nil { + return ext, err + } + exePath = filepath.Join(p, fi.Name()) + } + ext.path = exePath + return ext, nil +} + +func (m *Manager) parseExtensionDir(fi fs.FileInfo) (Extension, error) { + id := m.installDir() + if _, err := os.Stat(filepath.Join(id, fi.Name(), manifestName)); err == nil { + return m.parseBinaryExtensionDir(fi) + } + + return m.parseGitExtensionDir(fi) +} + +func (m *Manager) parseBinaryExtensionDir(fi fs.FileInfo) (Extension, error) { + id := m.installDir() + exePath := filepath.Join(id, fi.Name(), fi.Name()) + ext := Extension{path: exePath, kind: BinaryKind} manifestPath := filepath.Join(id, fi.Name(), manifestName) manifest, err := os.ReadFile(manifestPath) if err != nil { - return nil, fmt.Errorf("could not open %s for reading: %w", manifestPath, err) + return ext, fmt.Errorf("could not open %s for reading: %w", manifestPath, err) } - var bm binManifest err = yaml.Unmarshal(manifest, &bm) if err != nil { - return nil, fmt.Errorf("could not parse %s: %w", manifestPath, err) + return ext, fmt.Errorf("could not parse %s: %w", manifestPath, err) } - repo := ghrepo.NewWithHost(bm.Owner, bm.Name, bm.Host) - - var remoteURL string - var updateAvailable bool - - if includeMetadata { - remoteURL = ghrepo.GenerateRepoURL(repo, "") - var r *release - r, err = fetchLatestRelease(m.client, repo) - if err != nil { - return nil, fmt.Errorf("failed to get release info for %s: %w", ghrepo.FullName(repo), err) - } - if bm.Tag != r.Tag { - updateAvailable = true - } - } - - return &Extension{ - path: exePath, - url: remoteURL, - updateAvailable: updateAvailable, - }, nil + remoteURL := ghrepo.GenerateRepoURL(repo, "") + ext.url = remoteURL + ext.currentVersion = bm.Tag + return ext, nil } -func (m *Manager) parseGitExtensionDir(fi fs.FileInfo, includeMetadata bool) (*Extension, error) { - // TODO untangle local from this since local might be binary or git +func (m *Manager) parseGitExtensionDir(fi fs.FileInfo) (Extension, error) { id := m.installDir() - var remoteUrl string - updateAvailable := false - isLocal := false exePath := filepath.Join(id, fi.Name(), fi.Name()) - if fi.IsDir() { - if includeMetadata { - remoteUrl = m.getRemoteUrl(fi.Name()) - updateAvailable = m.checkUpdateAvailable(fi.Name()) - } - } else { - isLocal = true - if !isSymlink(fi.Mode()) { - // if this is a regular file, its contents is the local directory of the extension - p, err := readPathFromFile(filepath.Join(id, fi.Name())) - if err != nil { - return nil, err - } - exePath = filepath.Join(p, fi.Name()) - } - } - - return &Extension{ - path: exePath, - url: remoteUrl, - isLocal: isLocal, - updateAvailable: updateAvailable, + remoteUrl := m.getRemoteUrl(fi.Name()) + currentVersion := m.getCurrentVersion(fi.Name()) + return Extension{ + path: exePath, + url: remoteUrl, + isLocal: false, + currentVersion: currentVersion, + kind: GitKind, }, nil } +// getCurrentVersion determines the current version for non-local git extensions. +func (m *Manager) getCurrentVersion(extension string) string { + gitExe, err := m.lookPath("git") + if err != nil { + return "" + } + dir := m.installDir() + gitDir := "--git-dir=" + filepath.Join(dir, extension, ".git") + cmd := m.newCommand(gitExe, gitDir, "rev-parse", "HEAD") + localSha, err := cmd.Output() + if err != nil { + return "" + } + return string(bytes.TrimSpace(localSha)) +} + +// getRemoteUrl determines the remote URL for non-local git extensions. func (m *Manager) getRemoteUrl(extension string) string { gitExe, err := m.lookPath("git") if err != nil { @@ -223,26 +240,59 @@ func (m *Manager) getRemoteUrl(extension string) string { return strings.TrimSpace(string(url)) } -func (m *Manager) checkUpdateAvailable(extension string) bool { - gitExe, err := m.lookPath("git") - if err != nil { - return false +func (m *Manager) populateLatestVersions(exts []Extension) { + size := len(exts) + type result struct { + index int + version string } - dir := m.installDir() - gitDir := "--git-dir=" + filepath.Join(dir, extension, ".git") - cmd := m.newCommand(gitExe, gitDir, "ls-remote", "origin", "HEAD") - lsRemote, err := cmd.Output() - if err != nil { - return false + ch := make(chan result, size) + var wg sync.WaitGroup + wg.Add(size) + for idx, ext := range exts { + go func(i int, e Extension) { + defer wg.Done() + version, _ := m.getLatestVersion(e) + ch <- result{index: i, version: version} + }(idx, ext) } - remoteSha := bytes.SplitN(lsRemote, []byte("\t"), 2)[0] - cmd = m.newCommand(gitExe, gitDir, "rev-parse", "HEAD") - localSha, err := cmd.Output() - if err != nil { - return false + wg.Wait() + close(ch) + for r := range ch { + ext := &exts[r.index] + ext.latestVersion = r.version + } +} + +func (m *Manager) getLatestVersion(ext Extension) (string, error) { + if ext.isLocal { + return "", fmt.Errorf("unable to get latest version for local extensions") + } + if ext.kind == GitKind { + gitExe, err := m.lookPath("git") + if err != nil { + return "", err + } + extDir := filepath.Dir(ext.path) + gitDir := "--git-dir=" + filepath.Join(extDir, ".git") + cmd := m.newCommand(gitExe, gitDir, "ls-remote", "origin", "HEAD") + lsRemote, err := cmd.Output() + if err != nil { + return "", err + } + remoteSha := bytes.SplitN(lsRemote, []byte("\t"), 2)[0] + return string(remoteSha), nil + } else { + repo, err := ghrepo.FromFullName(ext.url) + if err != nil { + return "", err + } + r, err := fetchLatestRelease(m.client, repo) + if err != nil { + return "", err + } + return r.Tag, nil } - localSha = bytes.TrimSpace(localSha) - return !bytes.Equal(remoteSha, localSha) } func (m *Manager) InstallLocal(dir string) error { From 9302e68c92eeb2687c169c9a84d574df2e5e7053 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Wed, 13 Oct 2021 07:00:55 -0400 Subject: [PATCH 21/22] Include HTTP status code in error --- internal/codespaces/api/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/codespaces/api/api.go b/internal/codespaces/api/api.go index 1dff0fba9..1fcae02c5 100644 --- a/internal/codespaces/api/api.go +++ b/internal/codespaces/api/api.go @@ -341,7 +341,7 @@ func (a *API) StartCodespace(ctx context.Context, codespaceName string) error { if len(b) > 100 { b = append(b[:97], "..."...) } - return fmt.Errorf("failed to start codespace: %s", b) + return fmt.Errorf("failed to start codespace: %s (%s)", b, resp.Status) } return nil From 77a86e8611eeb9c7681dda49a7d36ed9ce520d67 Mon Sep 17 00:00:00 2001 From: Jose Garcia Date: Wed, 13 Oct 2021 08:07:59 -0400 Subject: [PATCH 22/22] Fix for API response body change - Connection is now embedded within the top level of the Codespace payload instead of inside the environment. --- internal/codespaces/api/api.go | 8 ++++---- internal/codespaces/codespaces.go | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/internal/codespaces/api/api.go b/internal/codespaces/api/api.go index 1dff0fba9..e4be5b0c4 100644 --- a/internal/codespaces/api/api.go +++ b/internal/codespaces/api/api.go @@ -161,14 +161,14 @@ type Codespace struct { RepositoryNWO string `json:"repository_nwo"` OwnerLogin string `json:"owner_login"` Environment CodespaceEnvironment `json:"environment"` + Connection CodespaceConnection `json:"connection"` } const CodespaceStateProvisioned = "provisioned" type CodespaceEnvironment struct { - State string `json:"state"` - Connection CodespaceEnvironmentConnection `json:"connection"` - GitStatus CodespaceEnvironmentGitStatus `json:"gitStatus"` + State string `json:"state"` + GitStatus CodespaceEnvironmentGitStatus `json:"gitStatus"` } type CodespaceEnvironmentGitStatus struct { @@ -185,7 +185,7 @@ const ( CodespaceEnvironmentStateAvailable = "Available" ) -type CodespaceEnvironmentConnection struct { +type CodespaceConnection struct { SessionID string `json:"sessionId"` SessionToken string `json:"sessionToken"` RelayEndpoint string `json:"relayEndpoint"` diff --git a/internal/codespaces/codespaces.go b/internal/codespaces/codespaces.go index ab013409b..c9831a961 100644 --- a/internal/codespaces/codespaces.go +++ b/internal/codespaces/codespaces.go @@ -16,10 +16,10 @@ type logger interface { } func connectionReady(codespace *api.Codespace) bool { - return codespace.Environment.Connection.SessionID != "" && - codespace.Environment.Connection.SessionToken != "" && - codespace.Environment.Connection.RelayEndpoint != "" && - codespace.Environment.Connection.RelaySAS != "" && + return codespace.Connection.SessionID != "" && + codespace.Connection.SessionToken != "" && + codespace.Connection.RelayEndpoint != "" && + codespace.Connection.RelaySAS != "" && codespace.Environment.State == api.CodespaceEnvironmentStateAvailable } @@ -67,10 +67,10 @@ func ConnectToLiveshare(ctx context.Context, log logger, apiClient apiClient, co log.Println("Connecting to your codespace...") return liveshare.Connect(ctx, liveshare.Options{ - SessionID: codespace.Environment.Connection.SessionID, - SessionToken: codespace.Environment.Connection.SessionToken, - RelaySAS: codespace.Environment.Connection.RelaySAS, - RelayEndpoint: codespace.Environment.Connection.RelayEndpoint, - HostPublicKeys: codespace.Environment.Connection.HostPublicKeys, + SessionID: codespace.Connection.SessionID, + SessionToken: codespace.Connection.SessionToken, + RelaySAS: codespace.Connection.RelaySAS, + RelayEndpoint: codespace.Connection.RelayEndpoint, + HostPublicKeys: codespace.Connection.HostPublicKeys, }) }