From dfd6908cd077489823c090c13063c6940b27952f Mon Sep 17 00:00:00 2001 From: vilmibm Date: Tue, 9 Aug 2022 15:00:01 -0500 Subject: [PATCH 1/8] refactor determineTrackingBranch tests --- pkg/cmd/pr/create/create_test.go | 156 ++++++++++++++++--------------- 1 file changed, 79 insertions(+), 77 deletions(-) diff --git a/pkg/cmd/pr/create/create_test.go b/pkg/cmd/pr/create/create_test.go index 33feafa40..63848520c 100644 --- a/pkg/cmd/pr/create/create_test.go +++ b/pkg/cmd/pr/create/create_test.go @@ -930,99 +930,101 @@ func TestPRCreate_draft(t *testing.T) { assert.Equal(t, "https://github.com/OWNER/REPO/pull/12\n", output.String()) } -func Test_determineTrackingBranch_empty(t *testing.T) { - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "") - cs.Register(`git show-ref --verify -- HEAD`, 0, "abc HEAD") - - remotes := context.Remotes{} - - ref := determineTrackingBranch(remotes, "feature") - if ref != nil { - t.Errorf("expected nil result, got %v", ref) - } -} - -func Test_determineTrackingBranch_noMatch(t *testing.T) { - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "") - cs.Register("git show-ref --verify -- HEAD refs/remotes/origin/feature refs/remotes/upstream/feature", 0, "abc HEAD\nbca refs/remotes/origin/feature") - - remotes := context.Remotes{ - &context.Remote{ - Remote: &git.Remote{Name: "origin"}, - Repo: ghrepo.New("hubot", "Spoon-Knife"), +func Test_determineTrackingBranch(t *testing.T) { + tests := []struct { + name string + cmdStubs func(*run.CommandStubber) + remotes context.Remotes + assert func(ref *git.TrackingRef, t *testing.T) + }{ + { + name: "empty", + cmdStubs: func(cs *run.CommandStubber) { + cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "") + cs.Register(`git show-ref --verify -- HEAD`, 0, "abc HEAD") + }, + assert: func(ref *git.TrackingRef, t *testing.T) { + assert.Nil(t, ref) + }, }, - &context.Remote{ - Remote: &git.Remote{Name: "upstream"}, - Repo: ghrepo.New("octocat", "Spoon-Knife"), + { + name: "no match", + cmdStubs: func(cs *run.CommandStubber) { + cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "") + cs.Register("git show-ref --verify -- HEAD refs/remotes/origin/feature refs/remotes/upstream/feature", 0, "abc HEAD\nbca refs/remotes/origin/feature") + }, + remotes: context.Remotes{ + &context.Remote{ + Remote: &git.Remote{Name: "origin"}, + Repo: ghrepo.New("hubot", "Spoon-Knife"), + }, + &context.Remote{ + Remote: &git.Remote{Name: "upstream"}, + Repo: ghrepo.New("octocat", "Spoon-Knife"), + }, + }, + assert: func(ref *git.TrackingRef, t *testing.T) { + assert.Nil(t, ref) + }, }, - } - - ref := determineTrackingBranch(remotes, "feature") - if ref != nil { - t.Errorf("expected nil result, got %v", ref) - } -} - -func Test_determineTrackingBranch_hasMatch(t *testing.T) { - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "") - cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature refs/remotes/upstream/feature$`, 0, heredoc.Doc(` + { + name: "match", + cmdStubs: func(cs *run.CommandStubber) { + cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "") + cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature refs/remotes/upstream/feature$`, 0, heredoc.Doc(` deadbeef HEAD deadb00f refs/remotes/origin/feature deadbeef refs/remotes/upstream/feature `)) - - remotes := context.Remotes{ - &context.Remote{ - Remote: &git.Remote{Name: "origin"}, - Repo: ghrepo.New("hubot", "Spoon-Knife"), + }, + remotes: context.Remotes{ + &context.Remote{ + Remote: &git.Remote{Name: "origin"}, + Repo: ghrepo.New("hubot", "Spoon-Knife"), + }, + &context.Remote{ + Remote: &git.Remote{Name: "upstream"}, + Repo: ghrepo.New("octocat", "Spoon-Knife"), + }, + }, + assert: func(ref *git.TrackingRef, t *testing.T) { + assert.Equal(t, "upstream", ref.RemoteName) + assert.Equal(t, "feature", ref.BranchName) + }, }, - &context.Remote{ - Remote: &git.Remote{Name: "upstream"}, - Repo: ghrepo.New("octocat", "Spoon-Knife"), - }, - } - - ref := determineTrackingBranch(remotes, "feature") - if ref == nil { - t.Fatal("expected result, got nil") - } - - assert.Equal(t, "upstream", ref.RemoteName) - assert.Equal(t, "feature", ref.BranchName) -} - -func Test_determineTrackingBranch_respectTrackingConfig(t *testing.T) { - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, heredoc.Doc(` + { + name: "respect tracking config", + cmdStubs: func(cs *run.CommandStubber) { + cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, heredoc.Doc(` branch.feature.remote origin branch.feature.merge refs/heads/great-feat `)) - cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/great-feat refs/remotes/origin/feature$`, 0, heredoc.Doc(` + cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/great-feat refs/remotes/origin/feature$`, 0, heredoc.Doc(` deadbeef HEAD deadb00f refs/remotes/origin/feature `)) - - remotes := context.Remotes{ - &context.Remote{ - Remote: &git.Remote{Name: "origin"}, - Repo: ghrepo.New("hubot", "Spoon-Knife"), + }, + remotes: context.Remotes{ + &context.Remote{ + Remote: &git.Remote{Name: "origin"}, + Repo: ghrepo.New("hubot", "Spoon-Knife"), + }, + }, + assert: func(ref *git.TrackingRef, t *testing.T) { + assert.Nil(t, ref) + }, }, } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cs, cmdTeardown := run.Stub() + defer cmdTeardown(t) - ref := determineTrackingBranch(remotes, "feature") - if ref != nil { - t.Errorf("expected nil result, got %v", ref) + tt.cmdStubs(cs) + + ref := determineTrackingBranch(tt.remotes, "feature") + tt.assert(ref, t) + }) } } From 5f418018132c861a4652c04e472c4922241ccc85 Mon Sep 17 00:00:00 2001 From: vilmibm Date: Thu, 4 Aug 2022 17:08:21 -0500 Subject: [PATCH 2/8] modernize the tests for pr create --- pkg/cmd/pr/create/create_test.go | 1360 +++++++++++++++--------------- 1 file changed, 661 insertions(+), 699 deletions(-) diff --git a/pkg/cmd/pr/create/create_test.go b/pkg/cmd/pr/create/create_test.go index 63848520c..47bea2463 100644 --- a/pkg/cmd/pr/create/create_test.go +++ b/pkg/cmd/pr/create/create_test.go @@ -1,10 +1,8 @@ package create import ( - "bytes" "encoding/json" "fmt" - "io" "net/http" "os" "path/filepath" @@ -183,672 +181,484 @@ func TestNewCmdCreate(t *testing.T) { } } -func runCommand(rt http.RoundTripper, remotes context.Remotes, branch string, isTTY bool, cli string) (*test.CmdOut, error) { - return runCommandWithRootDirOverridden(rt, remotes, branch, isTTY, cli, "") -} - -func runCommandWithRootDirOverridden(rt http.RoundTripper, remotes context.Remotes, branch string, isTTY bool, cli string, rootDir string) (*test.CmdOut, error) { - ios, _, stdout, stderr := iostreams.Test() - ios.SetStdoutTTY(isTTY) - ios.SetStdinTTY(isTTY) - ios.SetStderrTTY(isTTY) - - browser := &cmdutil.TestBrowser{} - factory := &cmdutil.Factory{ - IOStreams: ios, - Browser: browser, - HttpClient: func() (*http.Client, error) { - return &http.Client{Transport: rt}, nil - }, - Config: func() (config.Config, error) { - return config.NewBlankConfig(), nil - }, - Remotes: func() (context.Remotes, error) { - if remotes != nil { - return remotes, nil - } - return context.Remotes{ - { - Remote: &git.Remote{ - Name: "origin", - Resolved: "base", - }, - Repo: ghrepo.New("OWNER", "REPO"), - }, - }, nil - }, - Branch: func() (string, error) { - return branch, nil - }, - } - - cmd := NewCmdCreate(factory, func(opts *CreateOptions) error { - opts.RootDirOverride = rootDir - return createRun(opts) - }) - cmd.PersistentFlags().StringP("repo", "R", "", "") - - argv, err := shlex.Split(cli) - if err != nil { - return nil, err - } - cmd.SetArgs(argv) - - cmd.SetIn(&bytes.Buffer{}) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - - _, err = cmd.ExecuteC() - return &test.CmdOut{ - OutBuf: stdout, - ErrBuf: stderr, - BrowsedURL: browser.BrowsedURL(), - }, err -} - -func initFakeHTTP() *httpmock.Registry { - return &httpmock.Registry{} -} - -func TestPRCreate_nontty_web(t *testing.T) { - http := initFakeHTTP() - defer http.Verify(t) - - http.StubRepoInfoResponse("OWNER", "REPO", "master") - - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`git status --porcelain`, 0, "") - cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "") - - output, err := runCommand(http, nil, "feature", false, `--web --head=feature`) - require.NoError(t, err) - - assert.Equal(t, "", output.String()) - assert.Equal(t, "", output.Stderr()) - assert.Equal(t, "https://github.com/OWNER/REPO/compare/master...feature?body=&expand=1", output.BrowsedURL) -} - -func TestPRCreate_recover(t *testing.T) { - http := initFakeHTTP() - defer http.Verify(t) - - http.StubRepoInfoResponse("OWNER", "REPO", "master") - shared.RunCommandFinder("feature", nil, nil) - http.Register( - httpmock.GraphQL(`query RepositoryResolveMetadataIDs\b`), - httpmock.StringResponse(` - { "data": { - "u000": { "login": "jillValentine", "id": "JILLID" }, - "repository": {}, - "organization": {} - } } - `)) - http.Register( - httpmock.GraphQL(`mutation PullRequestCreateRequestReviews\b`), - httpmock.GraphQLMutation(` - { "data": { "requestReviews": { - "clientMutationId": "" - } } } - `, func(inputs map[string]interface{}) { - assert.Equal(t, []interface{}{"JILLID"}, inputs["userIds"]) - })) - http.Register( - httpmock.GraphQL(`mutation PullRequestCreate\b`), - httpmock.GraphQLMutation(` - { "data": { "createPullRequest": { "pullRequest": { - "URL": "https://github.com/OWNER/REPO/pull/12" - } } } } - `, func(input map[string]interface{}) { - assert.Equal(t, "recovered title", input["title"].(string)) - assert.Equal(t, "recovered body", input["body"].(string)) - })) - - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`git status --porcelain`, 0, "") - cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "") - - //nolint:staticcheck // SA1019: prompt.InitAskStubber is deprecated: use NewAskStubber - as, teardown := prompt.InitAskStubber() - defer teardown() - - as.StubPrompt("Title").AnswerDefault() - as.StubPrompt("Body").AnswerDefault() - as.StubPrompt("What's next?").AnswerDefault() - - tmpfile, err := os.CreateTemp(t.TempDir(), "testrecover*") - assert.NoError(t, err) - defer tmpfile.Close() - - state := prShared.IssueMetadataState{ - Title: "recovered title", - Body: "recovered body", - Reviewers: []string{"jillValentine"}, - } - - data, err := json.Marshal(state) - assert.NoError(t, err) - - _, err = tmpfile.Write(data) - assert.NoError(t, err) - - args := fmt.Sprintf("--recover '%s' -Hfeature", tmpfile.Name()) - - output, err := runCommandWithRootDirOverridden(http, nil, "feature", true, args, "") - assert.NoError(t, err) - - assert.Equal(t, "https://github.com/OWNER/REPO/pull/12\n", output.String()) -} - -func TestPRCreate_nontty(t *testing.T) { - http := initFakeHTTP() - defer http.Verify(t) - - http.StubRepoInfoResponse("OWNER", "REPO", "master") - shared.RunCommandFinder("feature", nil, nil) - http.Register( - httpmock.GraphQL(`mutation PullRequestCreate\b`), - httpmock.GraphQLMutation(` - { "data": { "createPullRequest": { "pullRequest": { - "URL": "https://github.com/OWNER/REPO/pull/12" - } } } }`, - func(input map[string]interface{}) { - assert.Equal(t, "REPOID", input["repositoryId"]) - assert.Equal(t, "my title", input["title"]) - assert.Equal(t, "my body", input["body"]) - assert.Equal(t, "master", input["baseRefName"]) - assert.Equal(t, "feature", input["headRefName"]) - }), - ) - - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`git status --porcelain`, 0, "") - - output, err := runCommand(http, nil, "feature", false, `-t "my title" -b "my body" -H feature`) - require.NoError(t, err) - - assert.Equal(t, "", output.Stderr()) - assert.Equal(t, "https://github.com/OWNER/REPO/pull/12\n", output.String()) -} - -func TestPRCreate(t *testing.T) { - http := initFakeHTTP() - defer http.Verify(t) - - http.StubRepoInfoResponse("OWNER", "REPO", "master") - http.StubRepoResponse("OWNER", "REPO") - http.Register( - httpmock.GraphQL(`query UserCurrent\b`), - httpmock.StringResponse(`{"data": {"viewer": {"login": "OWNER"} } }`)) - shared.RunCommandFinder("feature", nil, nil) - http.Register( - httpmock.GraphQL(`mutation PullRequestCreate\b`), - httpmock.GraphQLMutation(` - { "data": { "createPullRequest": { "pullRequest": { - "URL": "https://github.com/OWNER/REPO/pull/12" - } } } } - `, func(input map[string]interface{}) { - assert.Equal(t, "REPOID", input["repositoryId"].(string)) - assert.Equal(t, "my title", input["title"].(string)) - assert.Equal(t, "my body", input["body"].(string)) - assert.Equal(t, "master", input["baseRefName"].(string)) - assert.Equal(t, "feature", input["headRefName"].(string)) - assert.Equal(t, false, input["draft"].(bool)) - })) - - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`git status --porcelain`, 0, "") - cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "") - cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "") - cs.Register(`git push --set-upstream origin HEAD:feature`, 0, "") - - //nolint:staticcheck // SA1019: prompt.InitAskStubber is deprecated: use NewAskStubber - ask, cleanupAsk := prompt.InitAskStubber() - defer cleanupAsk() - - ask.StubPrompt("Where should we push the 'feature' branch?").AnswerDefault() - - output, err := runCommand(http, nil, "feature", true, `-t "my title" -b "my body"`) - require.NoError(t, err) - - assert.Equal(t, "https://github.com/OWNER/REPO/pull/12\n", output.String()) - assert.Equal(t, "\nCreating pull request for feature into master in OWNER/REPO\n\n", output.Stderr()) -} - -func TestPRCreate_NoMaintainerModify(t *testing.T) { - // TODO update this copypasta - http := initFakeHTTP() - defer http.Verify(t) - - http.StubRepoInfoResponse("OWNER", "REPO", "master") - http.StubRepoResponse("OWNER", "REPO") - http.Register( - httpmock.GraphQL(`query UserCurrent\b`), - httpmock.StringResponse(`{"data": {"viewer": {"login": "OWNER"} } }`)) - shared.RunCommandFinder("feature", nil, nil) - http.Register( - httpmock.GraphQL(`mutation PullRequestCreate\b`), - httpmock.GraphQLMutation(` - { "data": { "createPullRequest": { "pullRequest": { - "URL": "https://github.com/OWNER/REPO/pull/12" - } } } } - `, func(input map[string]interface{}) { - assert.Equal(t, false, input["maintainerCanModify"].(bool)) - assert.Equal(t, "REPOID", input["repositoryId"].(string)) - assert.Equal(t, "my title", input["title"].(string)) - assert.Equal(t, "my body", input["body"].(string)) - assert.Equal(t, "master", input["baseRefName"].(string)) - assert.Equal(t, "feature", input["headRefName"].(string)) - })) - - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "") - cs.Register(`git status --porcelain`, 0, "") - cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "") - cs.Register(`git push --set-upstream origin HEAD:feature`, 0, "") - - //nolint:staticcheck // SA1019: prompt.InitAskStubber is deprecated: use NewAskStubber - ask, cleanupAsk := prompt.InitAskStubber() - defer cleanupAsk() - - ask.StubPrompt("Where should we push the 'feature' branch?").AnswerDefault() - - output, err := runCommand(http, nil, "feature", true, `-t "my title" -b "my body" --no-maintainer-edit`) - require.NoError(t, err) - - assert.Equal(t, "https://github.com/OWNER/REPO/pull/12\n", output.String()) - assert.Equal(t, "\nCreating pull request for feature into master in OWNER/REPO\n\n", output.Stderr()) -} - -func TestPRCreate_createFork(t *testing.T) { - http := initFakeHTTP() - defer http.Verify(t) - - http.StubRepoInfoResponse("OWNER", "REPO", "master") - http.StubRepoResponse("OWNER", "REPO") - http.Register( - httpmock.GraphQL(`query UserCurrent\b`), - httpmock.StringResponse(`{"data": {"viewer": {"login": "monalisa"} } }`)) - shared.RunCommandFinder("feature", nil, nil) - http.Register( - httpmock.REST("POST", "repos/OWNER/REPO/forks"), - httpmock.StatusStringResponse(201, ` - { "node_id": "NODEID", - "name": "REPO", - "owner": {"login": "monalisa"} - } - `)) - http.Register( - httpmock.GraphQL(`mutation PullRequestCreate\b`), - httpmock.GraphQLMutation(` - { "data": { "createPullRequest": { "pullRequest": { - "URL": "https://github.com/OWNER/REPO/pull/12" - } } } } - `, func(input map[string]interface{}) { - assert.Equal(t, "REPOID", input["repositoryId"].(string)) - assert.Equal(t, "master", input["baseRefName"].(string)) - assert.Equal(t, "monalisa:feature", input["headRefName"].(string)) - })) - - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "") - cs.Register(`git status --porcelain`, 0, "") - cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "") - cs.Register(`git remote add -f fork https://github.com/monalisa/REPO.git`, 0, "") - cs.Register(`git push --set-upstream fork HEAD:feature`, 0, "") - - //nolint:staticcheck // SA1019: prompt.InitAskStubber is deprecated: use NewAskStubber - ask, cleanupAsk := prompt.InitAskStubber() - defer cleanupAsk() - - ask.StubPrompt("Where should we push the 'feature' branch?"). - AssertOptions([]string{"OWNER/REPO", "Create a fork of OWNER/REPO", "Skip pushing the branch", "Cancel"}). - AnswerWith("Create a fork of OWNER/REPO") - - output, err := runCommand(http, nil, "feature", true, `-t title -b body`) - require.NoError(t, err) - - assert.Equal(t, "https://github.com/OWNER/REPO/pull/12\n", output.String()) -} - -func TestPRCreate_pushedToNonBaseRepo(t *testing.T) { - remotes := context.Remotes{ +func Test_createRun(t *testing.T) { + tests := []struct { + name string + setup func(*CreateOptions, *testing.T) func() + cmdStubs func(*run.CommandStubber) + askStubs func(*prompt.AskStubber) // TODO eventually migrate to PrompterMock + httpStubs func(*httpmock.Registry, *testing.T) + expectedOut string + expectedErrOut string + expectedBrowse string + wantErr string + tty bool + }{ { - Remote: &git.Remote{ - Name: "upstream", - Resolved: "base", + name: "nontty web", + setup: func(opts *CreateOptions, t *testing.T) func() { + opts.WebMode = true + opts.HeadBranch = "feature" + return func() {} }, - Repo: ghrepo.New("OWNER", "REPO"), + cmdStubs: func(cs *run.CommandStubber) { + cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "") + }, + expectedBrowse: "https://github.com/OWNER/REPO/compare/master...feature?body=&expand=1", }, { - Remote: &git.Remote{ - Name: "origin", - Resolved: "base", + name: "nontty", + httpStubs: func(reg *httpmock.Registry, t *testing.T) { + reg.Register( + httpmock.GraphQL(`mutation PullRequestCreate\b`), + httpmock.GraphQLMutation(` + { "data": { "createPullRequest": { "pullRequest": { + "URL": "https://github.com/OWNER/REPO/pull/12" + } } } }`, + func(input map[string]interface{}) { + assert.Equal(t, "REPOID", input["repositoryId"]) + assert.Equal(t, "my title", input["title"]) + assert.Equal(t, "my body", input["body"]) + assert.Equal(t, "master", input["baseRefName"]) + assert.Equal(t, "feature", input["headRefName"]) + })) }, - Repo: ghrepo.New("monalisa", "REPO"), + setup: func(opts *CreateOptions, t *testing.T) func() { + opts.TitleProvided = true + opts.BodyProvided = true + opts.Title = "my title" + opts.Body = "my body" + opts.HeadBranch = "feature" + return func() {} + }, + expectedOut: "https://github.com/OWNER/REPO/pull/12\n", }, - } - - http := initFakeHTTP() - defer http.Verify(t) - - http.StubRepoInfoResponse("OWNER", "REPO", "master") - shared.RunCommandFinder("feature", nil, nil) - http.Register( - httpmock.GraphQL(`mutation PullRequestCreate\b`), - httpmock.GraphQLMutation(` - { "data": { "createPullRequest": { "pullRequest": { - "URL": "https://github.com/OWNER/REPO/pull/12" - } } } } - `, func(input map[string]interface{}) { - assert.Equal(t, "REPOID", input["repositoryId"].(string)) - assert.Equal(t, "master", input["baseRefName"].(string)) - assert.Equal(t, "monalisa:feature", input["headRefName"].(string)) - })) - - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register("git status", 0, "") - cs.Register(`git config --get-regexp \^branch\\\.feature\\\.`, 1, "") // determineTrackingBranch - cs.Register("git show-ref --verify", 0, heredoc.Doc(` + { + name: "survey", + tty: true, + setup: func(opts *CreateOptions, t *testing.T) func() { + opts.TitleProvided = true + opts.BodyProvided = true + opts.Title = "my title" + opts.Body = "my body" + return func() {} + }, + httpStubs: func(reg *httpmock.Registry, t *testing.T) { + reg.StubRepoResponse("OWNER", "REPO") + reg.Register( + httpmock.GraphQL(`query UserCurrent\b`), + httpmock.StringResponse(`{"data": {"viewer": {"login": "OWNER"} } }`)) + reg.Register( + httpmock.GraphQL(`mutation PullRequestCreate\b`), + httpmock.GraphQLMutation(` + { "data": { "createPullRequest": { "pullRequest": { + "URL": "https://github.com/OWNER/REPO/pull/12" + } } } }`, func(input map[string]interface{}) { + assert.Equal(t, "REPOID", input["repositoryId"].(string)) + assert.Equal(t, "my title", input["title"].(string)) + assert.Equal(t, "my body", input["body"].(string)) + assert.Equal(t, "master", input["baseRefName"].(string)) + assert.Equal(t, "feature", input["headRefName"].(string)) + assert.Equal(t, false, input["draft"].(bool)) + })) + }, + cmdStubs: func(cs *run.CommandStubber) { + cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "") + cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "") + cs.Register(`git push --set-upstream origin HEAD:feature`, 0, "") + }, + askStubs: func(as *prompt.AskStubber) { + as.StubPrompt("Where should we push the 'feature' branch?").AnswerDefault() + }, + expectedOut: "https://github.com/OWNER/REPO/pull/12\n", + expectedErrOut: "\nCreating pull request for feature into master in OWNER/REPO\n\n", + }, + { + name: "no maintainer modify", + tty: true, + setup: func(opts *CreateOptions, t *testing.T) func() { + opts.TitleProvided = true + opts.BodyProvided = true + opts.Title = "my title" + opts.Body = "my body" + return func() {} + }, + httpStubs: func(reg *httpmock.Registry, t *testing.T) { + reg.StubRepoResponse("OWNER", "REPO") + reg.Register( + httpmock.GraphQL(`query UserCurrent\b`), + httpmock.StringResponse(`{"data": {"viewer": {"login": "OWNER"} } }`)) + reg.Register( + httpmock.GraphQL(`mutation PullRequestCreate\b`), + httpmock.GraphQLMutation(` + { "data": { "createPullRequest": { "pullRequest": { + "URL": "https://github.com/OWNER/REPO/pull/12" + } } } } + `, func(input map[string]interface{}) { + assert.Equal(t, false, input["maintainerCanModify"].(bool)) + assert.Equal(t, "REPOID", input["repositoryId"].(string)) + assert.Equal(t, "my title", input["title"].(string)) + assert.Equal(t, "my body", input["body"].(string)) + assert.Equal(t, "master", input["baseRefName"].(string)) + assert.Equal(t, "feature", input["headRefName"].(string)) + })) + }, + cmdStubs: func(cs *run.CommandStubber) { + cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "") + cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "") + cs.Register(`git push --set-upstream origin HEAD:feature`, 0, "") + }, + askStubs: func(as *prompt.AskStubber) { + as.StubPrompt("Where should we push the 'feature' branch?").AnswerDefault() + }, + expectedOut: "https://github.com/OWNER/REPO/pull/12\n", + expectedErrOut: "\nCreating pull request for feature into master in OWNER/REPO\n\n", + }, + { + name: "create fork", + tty: true, + setup: func(opts *CreateOptions, t *testing.T) func() { + opts.TitleProvided = true + opts.BodyProvided = true + opts.Title = "title" + opts.Body = "body" + return func() {} + }, + httpStubs: func(reg *httpmock.Registry, t *testing.T) { + reg.StubRepoResponse("OWNER", "REPO") + reg.Register( + httpmock.GraphQL(`query UserCurrent\b`), + httpmock.StringResponse(`{"data": {"viewer": {"login": "monalisa"} } }`)) + reg.Register( + httpmock.REST("POST", "repos/OWNER/REPO/forks"), + httpmock.StatusStringResponse(201, ` + { "node_id": "NODEID", + "name": "REPO", + "owner": {"login": "monalisa"} + }`)) + reg.Register( + httpmock.GraphQL(`mutation PullRequestCreate\b`), + httpmock.GraphQLMutation(` + { "data": { "createPullRequest": { "pullRequest": { + "URL": "https://github.com/OWNER/REPO/pull/12" + }}}}`, func(input map[string]interface{}) { + assert.Equal(t, "REPOID", input["repositoryId"].(string)) + assert.Equal(t, "master", input["baseRefName"].(string)) + assert.Equal(t, "monalisa:feature", input["headRefName"].(string)) + })) + }, + cmdStubs: func(cs *run.CommandStubber) { + cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "") + cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "") + cs.Register(`git remote add -f fork https://github.com/monalisa/REPO.git`, 0, "") + cs.Register(`git push --set-upstream fork HEAD:feature`, 0, "") + }, + askStubs: func(as *prompt.AskStubber) { + as.StubPrompt("Where should we push the 'feature' branch?"). + AssertOptions([]string{"OWNER/REPO", "Create a fork of OWNER/REPO", "Skip pushing the branch", "Cancel"}). + AnswerWith("Create a fork of OWNER/REPO") + }, + expectedOut: "https://github.com/OWNER/REPO/pull/12\n", + expectedErrOut: "\nCreating pull request for monalisa:feature into master in OWNER/REPO\n\n", + }, + { + name: "pushed to non base repo", + tty: true, + setup: func(opts *CreateOptions, t *testing.T) func() { + opts.TitleProvided = true + opts.BodyProvided = true + opts.Title = "title" + opts.Body = "body" + opts.Remotes = func() (context.Remotes, error) { + return context.Remotes{ + { + Remote: &git.Remote{ + Name: "upstream", + Resolved: "base", + }, + Repo: ghrepo.New("OWNER", "REPO"), + }, + { + Remote: &git.Remote{ + Name: "origin", + Resolved: "base", + }, + Repo: ghrepo.New("monalisa", "REPO"), + }, + }, nil + } + return func() {} + }, + httpStubs: func(reg *httpmock.Registry, t *testing.T) { + reg.Register( + httpmock.GraphQL(`mutation PullRequestCreate\b`), + httpmock.GraphQLMutation(` + { "data": { "createPullRequest": { "pullRequest": { + "URL": "https://github.com/OWNER/REPO/pull/12" + } } } }`, func(input map[string]interface{}) { + assert.Equal(t, "REPOID", input["repositoryId"].(string)) + assert.Equal(t, "master", input["baseRefName"].(string)) + assert.Equal(t, "monalisa:feature", input["headRefName"].(string)) + })) + }, + cmdStubs: func(cs *run.CommandStubber) { + cs.Register(`git config --get-regexp \^branch\\\.feature\\\.`, 1, "") // determineTrackingBranch + cs.Register("git show-ref --verify", 0, heredoc.Doc(` deadbeef HEAD deadb00f refs/remotes/upstream/feature - deadbeef refs/remotes/origin/feature - `)) // determineTrackingBranch - - //nolint:staticcheck // SA1019: prompt.InitAskStubber is deprecated: use NewAskStubber - _, cleanupAsk := prompt.InitAskStubber() - defer cleanupAsk() - - output, err := runCommand(http, remotes, "feature", true, `-t title -b body`) - require.NoError(t, err) - - assert.Equal(t, "\nCreating pull request for monalisa:feature into master in OWNER/REPO\n\n", output.Stderr()) - assert.Equal(t, "https://github.com/OWNER/REPO/pull/12\n", output.String()) -} - -func TestPRCreate_pushedToDifferentBranchName(t *testing.T) { - http := initFakeHTTP() - defer http.Verify(t) - - http.StubRepoInfoResponse("OWNER", "REPO", "master") - shared.RunCommandFinder("feature", nil, nil) - http.Register( - httpmock.GraphQL(`mutation PullRequestCreate\b`), - httpmock.GraphQLMutation(` + deadbeef refs/remotes/origin/feature`)) // determineTrackingBranch + }, + expectedOut: "https://github.com/OWNER/REPO/pull/12\n", + expectedErrOut: "\nCreating pull request for monalisa:feature into master in OWNER/REPO\n\n", + }, + { + name: "pushed to different branch name", + tty: true, + setup: func(opts *CreateOptions, t *testing.T) func() { + opts.TitleProvided = true + opts.BodyProvided = true + opts.Title = "title" + opts.Body = "body" + return func() {} + }, + httpStubs: func(reg *httpmock.Registry, t *testing.T) { + reg.Register( + httpmock.GraphQL(`mutation PullRequestCreate\b`), + httpmock.GraphQLMutation(` { "data": { "createPullRequest": { "pullRequest": { "URL": "https://github.com/OWNER/REPO/pull/12" } } } } `, func(input map[string]interface{}) { - assert.Equal(t, "REPOID", input["repositoryId"].(string)) - assert.Equal(t, "master", input["baseRefName"].(string)) - assert.Equal(t, "my-feat2", input["headRefName"].(string)) - })) - - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register("git status", 0, "") - cs.Register(`git config --get-regexp \^branch\\\.feature\\\.`, 0, heredoc.Doc(` + assert.Equal(t, "REPOID", input["repositoryId"].(string)) + assert.Equal(t, "master", input["baseRefName"].(string)) + assert.Equal(t, "my-feat2", input["headRefName"].(string)) + })) + }, + cmdStubs: func(cs *run.CommandStubber) { + cs.Register(`git config --get-regexp \^branch\\\.feature\\\.`, 0, heredoc.Doc(` branch.feature.remote origin branch.feature.merge refs/heads/my-feat2 `)) // determineTrackingBranch - cs.Register("git show-ref --verify", 0, heredoc.Doc(` + cs.Register("git show-ref --verify", 0, heredoc.Doc(` deadbeef HEAD deadbeef refs/remotes/origin/my-feat2 `)) // determineTrackingBranch - - //nolint:staticcheck // SA1019: prompt.InitAskStubber is deprecated: use NewAskStubber - _, cleanupAsk := prompt.InitAskStubber() - defer cleanupAsk() - - output, err := runCommand(http, nil, "feature", true, `-t title -b body`) - require.NoError(t, err) - - assert.Equal(t, "\nCreating pull request for my-feat2 into master in OWNER/REPO\n\n", output.Stderr()) - assert.Equal(t, "https://github.com/OWNER/REPO/pull/12\n", output.String()) -} - -func TestPRCreate_nonLegacyTemplate(t *testing.T) { - http := initFakeHTTP() - defer http.Verify(t) - - http.StubRepoInfoResponse("OWNER", "REPO", "master") - shared.RunCommandFinder("feature", nil, nil) - http.Register( - httpmock.GraphQL(`query PullRequestTemplates\b`), - httpmock.StringResponse(` - { "data": { "repository": { "pullRequestTemplates": [ - { "filename": "template1", - "body": "this is a bug" }, - { "filename": "template2", - "body": "this is a enhancement" } - ] } } }`), - ) - http.Register( - httpmock.GraphQL(`mutation PullRequestCreate\b`), - httpmock.GraphQLMutation(` - { "data": { "createPullRequest": { "pullRequest": { - "URL": "https://github.com/OWNER/REPO/pull/12" - } } } } - `, func(input map[string]interface{}) { - assert.Equal(t, "my title", input["title"].(string)) - assert.Equal(t, "- commit 1\n- commit 0\n\nthis is a bug", input["body"].(string)) - })) - - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "1234567890,commit 0\n2345678901,commit 1") - cs.Register(`git status --porcelain`, 0, "") - - as := prompt.NewAskStubber(t) - - as.StubPrompt("Choose a template"). - AssertOptions([]string{"template1", "template2", "Open a blank pull request"}). - AnswerWith("template1") - as.StubPrompt("Body").AnswerDefault() - as.StubPrompt("What's next?"). - AssertOptions([]string{"Submit", "Submit as draft", "Continue in browser", "Add metadata", "Cancel"}). - AnswerDefault() - - output, err := runCommandWithRootDirOverridden(http, nil, "feature", true, `-t "my title" -H feature`, "./fixtures/repoWithNonLegacyPRTemplates") - require.NoError(t, err) - - assert.Equal(t, "https://github.com/OWNER/REPO/pull/12\n", output.String()) -} - -func TestPRCreate_metadata(t *testing.T) { - http := initFakeHTTP() - defer http.Verify(t) - - http.StubRepoInfoResponse("OWNER", "REPO", "master") - shared.RunCommandFinder("feature", nil, nil) - http.Register( - httpmock.GraphQL(`query RepositoryResolveMetadataIDs\b`), - httpmock.StringResponse(` - { "data": { - "u000": { "login": "MonaLisa", "id": "MONAID" }, - "u001": { "login": "hubot", "id": "HUBOTID" }, - "repository": { - "l000": { "name": "bug", "id": "BUGID" }, - "l001": { "name": "TODO", "id": "TODOID" } }, - "organization": { - "t000": { "slug": "core", "id": "COREID" }, - "t001": { "slug": "robots", "id": "ROBOTID" } - } - } } - `)) - http.Register( - httpmock.GraphQL(`query RepositoryMilestoneList\b`), - httpmock.StringResponse(` - { "data": { "repository": { "milestones": { - "nodes": [ - { "title": "GA", "id": "GAID" }, - { "title": "Big One.oh", "id": "BIGONEID" } - ], - "pageInfo": { "hasNextPage": false } - } } } } - `)) - http.Register( - httpmock.GraphQL(`query RepositoryProjectList\b`), - httpmock.StringResponse(` - { "data": { "repository": { "projects": { - "nodes": [ - { "name": "Cleanup", "id": "CLEANUPID" }, - { "name": "Roadmap", "id": "ROADMAPID" } - ], - "pageInfo": { "hasNextPage": false } - } } } } - `)) - http.Register( - httpmock.GraphQL(`query OrganizationProjectList\b`), - httpmock.StringResponse(` - { "data": { "organization": { "projects": { - "nodes": [], - "pageInfo": { "hasNextPage": false } - } } } } - `)) - http.Register( - httpmock.GraphQL(`mutation PullRequestCreate\b`), - httpmock.GraphQLMutation(` - { "data": { "createPullRequest": { "pullRequest": { - "id": "NEWPULLID", - "URL": "https://github.com/OWNER/REPO/pull/12" - } } } } - `, func(inputs map[string]interface{}) { - assert.Equal(t, "TITLE", inputs["title"]) - assert.Equal(t, "BODY", inputs["body"]) - if v, ok := inputs["assigneeIds"]; ok { - t.Errorf("did not expect assigneeIds: %v", v) - } - if v, ok := inputs["userIds"]; ok { - t.Errorf("did not expect userIds: %v", v) - } - })) - http.Register( - httpmock.GraphQL(`mutation PullRequestCreateMetadata\b`), - httpmock.GraphQLMutation(` - { "data": { "updatePullRequest": { - "clientMutationId": "" - } } } - `, func(inputs map[string]interface{}) { - assert.Equal(t, "NEWPULLID", inputs["pullRequestId"]) - assert.Equal(t, []interface{}{"MONAID"}, inputs["assigneeIds"]) - assert.Equal(t, []interface{}{"BUGID", "TODOID"}, inputs["labelIds"]) - assert.Equal(t, []interface{}{"ROADMAPID"}, inputs["projectIds"]) - assert.Equal(t, "BIGONEID", inputs["milestoneId"]) - })) - http.Register( - httpmock.GraphQL(`mutation PullRequestCreateRequestReviews\b`), - httpmock.GraphQLMutation(` - { "data": { "requestReviews": { - "clientMutationId": "" - } } } - `, func(inputs map[string]interface{}) { - assert.Equal(t, "NEWPULLID", inputs["pullRequestId"]) - assert.Equal(t, []interface{}{"HUBOTID", "MONAID"}, inputs["userIds"]) - assert.Equal(t, []interface{}{"COREID", "ROBOTID"}, inputs["teamIds"]) - assert.Equal(t, true, inputs["union"]) - })) - - output, err := runCommand(http, nil, "feature", true, `-t TITLE -b BODY -H feature -a monalisa -l bug -l todo -p roadmap -m 'big one.oh' -r hubot -r monalisa -r /core -r /robots`) - assert.NoError(t, err) - - assert.Equal(t, "https://github.com/OWNER/REPO/pull/12\n", output.String()) -} - -func TestPRCreate_alreadyExists(t *testing.T) { - http := initFakeHTTP() - defer http.Verify(t) - - http.StubRepoInfoResponse("OWNER", "REPO", "master") - shared.RunCommandFinder("feature", &api.PullRequest{URL: "https://github.com/OWNER/REPO/pull/123"}, ghrepo.New("OWNER", "REPO")) - - _, err := runCommand(http, nil, "feature", true, `-t title -b body -H feature`) - assert.EqualError(t, err, "a pull request for branch \"feature\" into branch \"master\" already exists:\nhttps://github.com/OWNER/REPO/pull/123") -} - -func TestPRCreate_web(t *testing.T) { - http := initFakeHTTP() - defer http.Verify(t) - - http.StubRepoInfoResponse("OWNER", "REPO", "master") - http.StubRepoResponse("OWNER", "REPO") - http.Register( - httpmock.GraphQL(`query UserCurrent\b`), - httpmock.StringResponse(`{"data": {"viewer": {"login": "OWNER"} } }`)) - - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "") - cs.Register(`git status --porcelain`, 0, "") - cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "") - cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "") - cs.Register(`git push --set-upstream origin HEAD:feature`, 0, "") - - //nolint:staticcheck // SA1019: prompt.InitAskStubber is deprecated: use NewAskStubber - ask, cleanupAsk := prompt.InitAskStubber() - defer cleanupAsk() - - ask.StubPrompt("Where should we push the 'feature' branch?"). - AssertOptions([]string{"OWNER/REPO", "Skip pushing the branch", "Cancel"}). - AnswerDefault() - - output, err := runCommand(http, nil, "feature", true, `--web`) - require.NoError(t, err) - - assert.Equal(t, "", output.String()) - assert.Equal(t, "Opening github.com/OWNER/REPO/compare/master...feature in your browser.\n", output.Stderr()) - assert.Equal(t, "https://github.com/OWNER/REPO/compare/master...feature?body=&expand=1", output.BrowsedURL) -} - -func TestPRCreate_webLongURL(t *testing.T) { - longBodyFile := filepath.Join(t.TempDir(), "long-body.txt") - err := os.WriteFile(longBodyFile, make([]byte, 9216), 0600) - require.NoError(t, err) - - http := initFakeHTTP() - defer http.Verify(t) - - http.StubRepoInfoResponse("OWNER", "REPO", "master") - - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`git status --porcelain`, 0, "") - cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "") - - _, err = runCommand(http, nil, "feature", false, fmt.Sprintf("--body-file '%s' --web --head=feature", longBodyFile)) - require.EqualError(t, err, "cannot open in browser: maximum URL length exceeded") -} - -func TestPRCreate_webProject(t *testing.T) { - http := initFakeHTTP() - defer http.Verify(t) - - http.StubRepoInfoResponse("OWNER", "REPO", "master") - http.StubRepoResponse("OWNER", "REPO") - http.Register( - httpmock.GraphQL(`query UserCurrent\b`), - httpmock.StringResponse(`{"data": {"viewer": {"login": "OWNER"} } }`)) - http.Register( - httpmock.GraphQL(`query RepositoryProjectList\b`), - httpmock.StringResponse(` + expectedOut: "https://github.com/OWNER/REPO/pull/12\n", + expectedErrOut: "\nCreating pull request for my-feat2 into master in OWNER/REPO\n\n", + }, + { + name: "non legacy template", + tty: true, + setup: func(opts *CreateOptions, t *testing.T) func() { + opts.TitleProvided = true + opts.Title = "my title" + opts.HeadBranch = "feature" + opts.RootDirOverride = "./fixtures/repoWithNonLegacyPRTemplates" + return func() {} + }, + httpStubs: func(reg *httpmock.Registry, t *testing.T) { + reg.Register( + httpmock.GraphQL(`query PullRequestTemplates\b`), + httpmock.StringResponse(` + { "data": { "repository": { "pullRequestTemplates": [ + { "filename": "template1", + "body": "this is a bug" }, + { "filename": "template2", + "body": "this is a enhancement" } + ] } } }`)) + reg.Register( + httpmock.GraphQL(`mutation PullRequestCreate\b`), + httpmock.GraphQLMutation(` + { "data": { "createPullRequest": { "pullRequest": { + "URL": "https://github.com/OWNER/REPO/pull/12" + } } } } + `, func(input map[string]interface{}) { + assert.Equal(t, "my title", input["title"].(string)) + assert.Equal(t, "- commit 1\n- commit 0\n\nthis is a bug", input["body"].(string)) + })) + }, + cmdStubs: func(cs *run.CommandStubber) { + cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "1234567890,commit 0\n2345678901,commit 1") + }, + askStubs: func(as *prompt.AskStubber) { + as.StubPrompt("Choose a template"). + AssertOptions([]string{"template1", "template2", "Open a blank pull request"}). + AnswerWith("template1") + as.StubPrompt("Body").AnswerDefault() + as.StubPrompt("What's next?"). + AssertOptions([]string{"Submit", "Submit as draft", "Continue in browser", "Add metadata", "Cancel"}). + AnswerDefault() + }, + expectedOut: "https://github.com/OWNER/REPO/pull/12\n", + expectedErrOut: "\nCreating pull request for feature into master in OWNER/REPO\n\n", + }, + { + name: "metadata", + tty: true, + setup: func(opts *CreateOptions, t *testing.T) func() { + opts.TitleProvided = true + opts.Title = "TITLE" + opts.BodyProvided = true + opts.Body = "BODY" + opts.HeadBranch = "feature" + opts.Assignees = []string{"monalisa"} + opts.Labels = []string{"bug", "todo"} + opts.Projects = []string{"roadmap"} + opts.Reviewers = []string{"hubot", "monalisa", "/core", "/robots"} + opts.Milestone = "big one.oh" + return func() {} + }, + httpStubs: func(reg *httpmock.Registry, t *testing.T) { + reg.Register( + httpmock.GraphQL(`query RepositoryResolveMetadataIDs\b`), + httpmock.StringResponse(` + { "data": { + "u000": { "login": "MonaLisa", "id": "MONAID" }, + "u001": { "login": "hubot", "id": "HUBOTID" }, + "repository": { + "l000": { "name": "bug", "id": "BUGID" }, + "l001": { "name": "TODO", "id": "TODOID" } + }, + "organization": { + "t000": { "slug": "core", "id": "COREID" }, + "t001": { "slug": "robots", "id": "ROBOTID" } + } + } } + `)) + reg.Register( + httpmock.GraphQL(`query RepositoryMilestoneList\b`), + httpmock.StringResponse(` + { "data": { "repository": { "milestones": { + "nodes": [ + { "title": "GA", "id": "GAID" }, + { "title": "Big One.oh", "id": "BIGONEID" } + ], + "pageInfo": { "hasNextPage": false } + } } } } + `)) + reg.Register( + httpmock.GraphQL(`query RepositoryProjectList\b`), + httpmock.StringResponse(` + { "data": { "repository": { "projects": { + "nodes": [ + { "name": "Cleanup", "id": "CLEANUPID" }, + { "name": "Roadmap", "id": "ROADMAPID" } + ], + "pageInfo": { "hasNextPage": false } + } } } } + `)) + reg.Register( + httpmock.GraphQL(`query OrganizationProjectList\b`), + httpmock.StringResponse(` + { "data": { "organization": { "projects": { + "nodes": [], + "pageInfo": { "hasNextPage": false } + } } } } + `)) + reg.Register( + httpmock.GraphQL(`mutation PullRequestCreate\b`), + httpmock.GraphQLMutation(` + { "data": { "createPullRequest": { "pullRequest": { + "id": "NEWPULLID", + "URL": "https://github.com/OWNER/REPO/pull/12" + } } } } + `, func(inputs map[string]interface{}) { + assert.Equal(t, "TITLE", inputs["title"]) + assert.Equal(t, "BODY", inputs["body"]) + if v, ok := inputs["assigneeIds"]; ok { + t.Errorf("did not expect assigneeIds: %v", v) + } + if v, ok := inputs["userIds"]; ok { + t.Errorf("did not expect userIds: %v", v) + } + })) + reg.Register( + httpmock.GraphQL(`mutation PullRequestCreateMetadata\b`), + httpmock.GraphQLMutation(` + { "data": { "updatePullRequest": { + "clientMutationId": "" + } } } + `, func(inputs map[string]interface{}) { + assert.Equal(t, "NEWPULLID", inputs["pullRequestId"]) + assert.Equal(t, []interface{}{"MONAID"}, inputs["assigneeIds"]) + assert.Equal(t, []interface{}{"BUGID", "TODOID"}, inputs["labelIds"]) + assert.Equal(t, []interface{}{"ROADMAPID"}, inputs["projectIds"]) + assert.Equal(t, "BIGONEID", inputs["milestoneId"]) + })) + reg.Register( + httpmock.GraphQL(`mutation PullRequestCreateRequestReviews\b`), + httpmock.GraphQLMutation(` + { "data": { "requestReviews": { + "clientMutationId": "" + } } } + `, func(inputs map[string]interface{}) { + assert.Equal(t, "NEWPULLID", inputs["pullRequestId"]) + assert.Equal(t, []interface{}{"HUBOTID", "MONAID"}, inputs["userIds"]) + assert.Equal(t, []interface{}{"COREID", "ROBOTID"}, inputs["teamIds"]) + assert.Equal(t, true, inputs["union"]) + })) + }, + expectedOut: "https://github.com/OWNER/REPO/pull/12\n", + expectedErrOut: "\nCreating pull request for feature into master in OWNER/REPO\n\n", + }, + { + name: "already exists", + tty: true, + setup: func(opts *CreateOptions, t *testing.T) func() { + opts.TitleProvided = true + opts.BodyProvided = true + opts.Title = "title" + opts.Body = "body" + opts.HeadBranch = "feature" + opts.Finder = shared.NewMockFinder("feature", &api.PullRequest{URL: "https://github.com/OWNER/REPO/pull/123"}, ghrepo.New("OWNER", "REPO")) + return func() {} + }, + wantErr: "a pull request for branch \"feature\" into branch \"master\" already exists:\nhttps://github.com/OWNER/REPO/pull/123", + }, + { + name: "web", + tty: true, + setup: func(opts *CreateOptions, t *testing.T) func() { + opts.WebMode = true + return func() {} + }, + httpStubs: func(reg *httpmock.Registry, t *testing.T) { + reg.StubRepoResponse("OWNER", "REPO") + reg.Register( + httpmock.GraphQL(`query UserCurrent\b`), + httpmock.StringResponse(`{"data": {"viewer": {"login": "OWNER"} } }`)) + }, + cmdStubs: func(cs *run.CommandStubber) { + cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "") + cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "") + cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "") + cs.Register(`git push --set-upstream origin HEAD:feature`, 0, "") + }, + askStubs: func(as *prompt.AskStubber) { + as.StubPrompt("Where should we push the 'feature' branch?"). + AssertOptions([]string{"OWNER/REPO", "Skip pushing the branch", "Cancel"}). + AnswerDefault() + }, + expectedErrOut: "Opening github.com/OWNER/REPO/compare/master...feature in your browser.\n", + expectedBrowse: "https://github.com/OWNER/REPO/compare/master...feature?body=&expand=1", + }, + { + name: "web project", + tty: true, + setup: func(opts *CreateOptions, t *testing.T) func() { + opts.WebMode = true + opts.Projects = []string{"Triage"} + return func() {} + }, + httpStubs: func(reg *httpmock.Registry, t *testing.T) { + reg.StubRepoResponse("OWNER", "REPO") + reg.Register( + httpmock.GraphQL(`query UserCurrent\b`), + httpmock.StringResponse(`{"data": {"viewer": {"login": "OWNER"} } }`)) + reg.Register( + httpmock.GraphQL(`query RepositoryProjectList\b`), + httpmock.StringResponse(` { "data": { "repository": { "projects": { "nodes": [ { "name": "Cleanup", "id": "CLEANUPID", "resourcePath": "/OWNER/REPO/projects/1" } @@ -856,9 +666,9 @@ func TestPRCreate_webProject(t *testing.T) { "pageInfo": { "hasNextPage": false } } } } } `)) - http.Register( - httpmock.GraphQL(`query OrganizationProjectList\b`), - httpmock.StringResponse(` + reg.Register( + httpmock.GraphQL(`query OrganizationProjectList\b`), + httpmock.StringResponse(` { "data": { "organization": { "projects": { "nodes": [ { "name": "Triage", "id": "TRIAGEID", "resourcePath": "/orgs/ORG/projects/1" } @@ -866,68 +676,220 @@ func TestPRCreate_webProject(t *testing.T) { "pageInfo": { "hasNextPage": false } } } } } `)) + }, + cmdStubs: func(cs *run.CommandStubber) { + cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "") + cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "") + cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "") + cs.Register(`git push --set-upstream origin HEAD:feature`, 0, "") - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`git config --get-regexp.+branch\\\.feature\\\.`, 0, "") - cs.Register(`git status --porcelain`, 0, "") - cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "") - cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "") - cs.Register(`git push --set-upstream origin HEAD:feature`, 0, "") - - //nolint:staticcheck // SA1019: prompt.InitAskStubber is deprecated: use NewAskStubber - ask, cleanupAsk := prompt.InitAskStubber() - defer cleanupAsk() - - ask.StubPrompt("Where should we push the 'feature' branch?").AnswerDefault() - - output, err := runCommand(http, nil, "feature", true, `--web -p Triage`) - require.NoError(t, err) - - assert.Equal(t, "", output.String()) - assert.Equal(t, "Opening github.com/OWNER/REPO/compare/master...feature in your browser.\n", output.Stderr()) - assert.Equal(t, "https://github.com/OWNER/REPO/compare/master...feature?body=&expand=1&projects=ORG%2F1", output.BrowsedURL) -} - -func TestPRCreate_draft(t *testing.T) { - http := initFakeHTTP() - defer http.Verify(t) - - http.StubRepoInfoResponse("OWNER", "REPO", "master") - shared.RunCommandFinder("feature", nil, nil) - http.Register( - httpmock.GraphQL(`query PullRequestTemplates\b`), - httpmock.StringResponse(` + }, + askStubs: func(as *prompt.AskStubber) { + as.StubPrompt("Where should we push the 'feature' branch?").AnswerDefault() + }, + expectedErrOut: "Opening github.com/OWNER/REPO/compare/master...feature in your browser.\n", + expectedBrowse: "https://github.com/OWNER/REPO/compare/master...feature?body=&expand=1&projects=ORG%2F1", + }, + { + name: "draft", + tty: true, + setup: func(opts *CreateOptions, t *testing.T) func() { + opts.TitleProvided = true + opts.Title = "my title" + opts.HeadBranch = "feature" + return func() {} + }, + httpStubs: func(reg *httpmock.Registry, t *testing.T) { + reg.Register( + httpmock.GraphQL(`query PullRequestTemplates\b`), + httpmock.StringResponse(` { "data": { "repository": { "pullRequestTemplates": [ { "filename": "template1", "body": "this is a bug" }, { "filename": "template2", "body": "this is a enhancement" } ] } } }`), - ) - http.Register( - httpmock.GraphQL(`mutation PullRequestCreate\b`), - httpmock.GraphQLMutation(` + ) + reg.Register( + httpmock.GraphQL(`mutation PullRequestCreate\b`), + httpmock.GraphQLMutation(` { "data": { "createPullRequest": { "pullRequest": { "URL": "https://github.com/OWNER/REPO/pull/12" } } } } `, func(input map[string]interface{}) { - assert.Equal(t, true, input["draft"].(bool)) - })) + assert.Equal(t, true, input["draft"].(bool)) + })) + }, + cmdStubs: func(cs *run.CommandStubber) { + cs.Register(`git -c log.ShowSignature=false log --pretty=format:%H,%s --cherry origin/master...feature`, 0, "") + cs.Register(`git rev-parse --show-toplevel`, 0, "") + }, + askStubs: func(as *prompt.AskStubber) { + as.StubPrompt("Choose a template").AnswerDefault() + as.StubPrompt("Body").AnswerDefault() + as.StubPrompt("What's next?"). + AssertOptions([]string{"Submit", "Submit as draft", "Continue in browser", "Add metadata", "Cancel"}). + AnswerWith("Submit as draft") + }, + expectedOut: "https://github.com/OWNER/REPO/pull/12\n", + expectedErrOut: "\nCreating pull request for feature into master in OWNER/REPO\n\n", + }, + { + name: "recover", + tty: true, + httpStubs: func(reg *httpmock.Registry, t *testing.T) { + reg.Register( + httpmock.GraphQL(`query RepositoryResolveMetadataIDs\b`), + httpmock.StringResponse(` + { "data": { + "u000": { "login": "jillValentine", "id": "JILLID" }, + "repository": {}, + "organization": {} + } } + `)) + reg.Register( + httpmock.GraphQL(`mutation PullRequestCreateRequestReviews\b`), + httpmock.GraphQLMutation(` + { "data": { "requestReviews": { + "clientMutationId": "" + } } } + `, func(inputs map[string]interface{}) { + assert.Equal(t, []interface{}{"JILLID"}, inputs["userIds"]) + })) + reg.Register( + httpmock.GraphQL(`mutation PullRequestCreate\b`), + httpmock.GraphQLMutation(` + { "data": { "createPullRequest": { "pullRequest": { + "URL": "https://github.com/OWNER/REPO/pull/12" + } } } } + `, func(input map[string]interface{}) { + assert.Equal(t, "recovered title", input["title"].(string)) + assert.Equal(t, "recovered body", input["body"].(string)) + })) + }, + cmdStubs: func(cs *run.CommandStubber) { + cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "") + }, + askStubs: func(as *prompt.AskStubber) { + as.StubPrompt("Title").AnswerDefault() + as.StubPrompt("Body").AnswerDefault() + as.StubPrompt("What's next?").AnswerDefault() + }, + setup: func(opts *CreateOptions, t *testing.T) func() { + tmpfile, err := os.CreateTemp(t.TempDir(), "testrecover*") + assert.NoError(t, err) + state := prShared.IssueMetadataState{ + Title: "recovered title", + Body: "recovered body", + Reviewers: []string{"jillValentine"}, + } + data, err := json.Marshal(state) + assert.NoError(t, err) + _, err = tmpfile.Write(data) + assert.NoError(t, err) - as := prompt.NewAskStubber(t) + opts.RecoverFile = tmpfile.Name() + opts.HeadBranch = "feature" + return func() { tmpfile.Close() } + }, + expectedOut: "https://github.com/OWNER/REPO/pull/12\n", + expectedErrOut: "\nCreating pull request for feature into master in OWNER/REPO\n\n", + }, + { + name: "web long URL", + cmdStubs: func(cs *run.CommandStubber) { + cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "") + }, + setup: func(opts *CreateOptions, t *testing.T) func() { + longBody := make([]byte, 9216) + opts.Body = string(longBody) + opts.BodyProvided = true + opts.WebMode = true + opts.HeadBranch = "feature" + return func() {} + }, + wantErr: "cannot open in browser: maximum URL length exceeded", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + branch := "feature" - as.StubPrompt("Choose a template").AnswerDefault() - as.StubPrompt("Body").AnswerDefault() - as.StubPrompt("What's next?"). - AssertOptions([]string{"Submit", "Submit as draft", "Continue in browser", "Add metadata", "Cancel"}). - AnswerWith("Submit as draft") + reg := &httpmock.Registry{} + reg.StubRepoInfoResponse("OWNER", "REPO", "master") + defer reg.Verify(t) + if tt.httpStubs != nil { + tt.httpStubs(reg, t) + } - output, err := runCommand(http, nil, "feature", true, `-t "my title" -H feature`) - require.NoError(t, err) + //nolint:staticcheck // SA1019: prompt.InitAskStubber is deprecated: use NewAskStubber + ask, cleanupAsk := prompt.InitAskStubber() + defer cleanupAsk() + if tt.askStubs != nil { + tt.askStubs(ask) + } - assert.Equal(t, "https://github.com/OWNER/REPO/pull/12\n", output.String()) + cs, cmdTeardown := run.Stub() + defer cmdTeardown(t) + cs.Register(`git status --porcelain`, 0, "") + + if tt.cmdStubs != nil { + tt.cmdStubs(cs) + } + + opts := CreateOptions{} + + ios, _, stdout, stderr := iostreams.Test() + // TODO do i need to bother with this + ios.SetStdoutTTY(tt.tty) + ios.SetStdinTTY(tt.tty) + ios.SetStderrTTY(tt.tty) + browser := &cmdutil.TestBrowser{} + opts.IO = ios + opts.Browser = browser + opts.HttpClient = func() (*http.Client, error) { + return &http.Client{Transport: reg}, nil + } + opts.Config = func() (config.Config, error) { + return config.NewBlankConfig(), nil + } + opts.Remotes = func() (context.Remotes, error) { + return context.Remotes{ + { + Remote: &git.Remote{ + Name: "origin", + Resolved: "base", + }, + Repo: ghrepo.New("OWNER", "REPO"), + }, + }, nil + } + opts.Branch = func() (string, error) { + return branch, nil + } + opts.Finder = shared.NewMockFinder(branch, nil, nil) + cleanSetup := func() {} + if tt.setup != nil { + cleanSetup = tt.setup(&opts, t) + } + defer cleanSetup() + + err := createRun(&opts) + output := &test.CmdOut{ + OutBuf: stdout, + ErrBuf: stderr, + BrowsedURL: browser.BrowsedURL(), + } + if tt.wantErr != "" { + assert.EqualError(t, err, tt.wantErr) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedOut, output.String()) + assert.Equal(t, tt.expectedErrOut, output.Stderr()) + assert.Equal(t, tt.expectedBrowse, output.BrowsedURL) + } + }) + } } func Test_determineTrackingBranch(t *testing.T) { From 9f1f74c30a2a0d4fee52351169d40e79eaa160e6 Mon Sep 17 00:00:00 2001 From: Eljo George Date: Fri, 12 Aug 2022 21:00:57 +0000 Subject: [PATCH 3/8] SSH help text to suggest the new features syntax --- pkg/cmd/codespace/ssh.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/cmd/codespace/ssh.go b/pkg/cmd/codespace/ssh.go index 58007fd8d..a88cec2cf 100644 --- a/pkg/cmd/codespace/ssh.go +++ b/pkg/cmd/codespace/ssh.go @@ -66,11 +66,13 @@ func newSSHCmd(app *App) *cobra.Command { Note that the codespace you are connecting to must have an SSH server pre-installed. If the docker image being used for the codespace does not have an SSH server, - install it in your Dockerfile or you can try adding the following snippet - in your devcontainer.json: - + install it in your Dockerfile or, for codespaces that use Debian-based images, + you can add the following to your devcontainer.json: + "features": { - "sshd": "latest" + "ghcr.io/devcontainers/features/sshd:1": { + "version": "latest" + } } `), Example: heredoc.Doc(` From 54e5673bd68dc0b88d940068e9353e62fefe9935 Mon Sep 17 00:00:00 2001 From: Eljo George Date: Fri, 12 Aug 2022 21:04:21 +0000 Subject: [PATCH 4/8] remove blank spaces --- pkg/cmd/codespace/ssh.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/codespace/ssh.go b/pkg/cmd/codespace/ssh.go index a88cec2cf..af8e9fa94 100644 --- a/pkg/cmd/codespace/ssh.go +++ b/pkg/cmd/codespace/ssh.go @@ -68,7 +68,7 @@ func newSSHCmd(app *App) *cobra.Command { If the docker image being used for the codespace does not have an SSH server, install it in your Dockerfile or, for codespaces that use Debian-based images, you can add the following to your devcontainer.json: - + "features": { "ghcr.io/devcontainers/features/sshd:1": { "version": "latest" From a447c078cfa3a293fe1225d18ef572dccf2f4a63 Mon Sep 17 00:00:00 2001 From: Yuta Iwama Date: Mon, 15 Aug 2022 20:43:11 +0900 Subject: [PATCH 5/8] Enable browsing to commit page (#5729) --- pkg/cmd/browse/browse.go | 15 +++++++++++++++ pkg/cmd/browse/browse_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/pkg/cmd/browse/browse.go b/pkg/cmd/browse/browse.go index 7d29f9723..5129e45a9 100644 --- a/pkg/cmd/browse/browse.go +++ b/pkg/cmd/browse/browse.go @@ -6,6 +6,7 @@ import ( "net/url" "path" "path/filepath" + "regexp" "strconv" "strings" @@ -62,6 +63,9 @@ func NewCmdBrowse(f *cmdutil.Factory, runF func(*BrowseOptions) error) *cobra.Co $ gh browse 217 #=> Open issue or pull request 217 + $ gh browse 77507cd94ccafcf568f8560cfecde965fcfa63 + #=> Open commit page + $ gh browse --settings #=> Open repository settings @@ -169,6 +173,10 @@ func parseSection(baseRepo ghrepo.Interface, opts *BrowseOptions) (string, error return fmt.Sprintf("issues/%s", opts.SelectorArg), nil } + if isCommit(opts.SelectorArg) { + return fmt.Sprintf("commit/%s", opts.SelectorArg), nil + } + filePath, rangeStart, rangeEnd, err := parseFile(*opts, opts.SelectorArg) if err != nil { return "", err @@ -252,6 +260,13 @@ func isNumber(arg string) bool { return err == nil } +// sha1 and sha256 are supported +var commitHash = regexp.MustCompile(`\A[a-f0-9]{7,64}\z`) + +func isCommit(arg string) bool { + return commitHash.MatchString(arg) +} + // gitClient is used to implement functions that can be performed on both local and remote git repositories type gitClient interface { LastCommit() (*git.Commit, error) diff --git a/pkg/cmd/browse/browse_test.go b/pkg/cmd/browse/browse_test.go index 576818431..380a6fc1c 100644 --- a/pkg/cmd/browse/browse_test.go +++ b/pkg/cmd/browse/browse_test.go @@ -414,6 +414,35 @@ func Test_runBrowse(t *testing.T) { expectedURL: "https://github.com/bchadwic/test/blob/branch/with%20spaces%3F/%3F=hello%20world/%20%2A?plain=1#L23-L44", wantsErr: false, }, + { + name: "commit hash in selector arg", + opts: BrowseOptions{ + SelectorArg: "77507cd94ccafcf568f8560cfecde965fcfa63e7", + }, + baseRepo: ghrepo.New("bchadwic", "test"), + expectedURL: "https://github.com/bchadwic/test/commit/77507cd94ccafcf568f8560cfecde965fcfa63e7", + wantsErr: false, + }, + { + name: "short commit hash in selector arg", + opts: BrowseOptions{ + SelectorArg: "6e3689d5", + }, + baseRepo: ghrepo.New("bchadwic", "test"), + expectedURL: "https://github.com/bchadwic/test/commit/6e3689d5", + wantsErr: false, + }, + + { + name: "commit hash with extension", + opts: BrowseOptions{ + SelectorArg: "77507cd94ccafcf568f8560cfecde965fcfa63e7.txt", + Branch: "trunk", + }, + baseRepo: ghrepo.New("bchadwic", "test"), + expectedURL: "https://github.com/bchadwic/test/tree/trunk/77507cd94ccafcf568f8560cfecde965fcfa63e7.txt", + wantsErr: false, + }, } for _, tt := range tests { From 38b097577be71646dff651d66c4949f443cc3b3f Mon Sep 17 00:00:00 2001 From: Luis Filipe Pessoa Date: Mon, 15 Aug 2022 09:00:05 -0500 Subject: [PATCH 6/8] Allow use of environmental variables in --jq expression (#6075) --- pkg/export/filter.go | 13 ++++++++++++- pkg/export/filter_test.go | 23 +++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/pkg/export/filter.go b/pkg/export/filter.go index 6a5e0e82e..8480e22f8 100644 --- a/pkg/export/filter.go +++ b/pkg/export/filter.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "io" + "os" "github.com/itchyny/gojq" ) @@ -14,6 +15,16 @@ func FilterJSON(w io.Writer, input io.Reader, queryStr string) error { return err } + code, err := gojq.Compile( + query, + gojq.WithEnvironLoader(func() []string { + return os.Environ() + })) + + if err != nil { + return err + } + jsonData, err := io.ReadAll(input) if err != nil { return err @@ -25,7 +36,7 @@ func FilterJSON(w io.Writer, input io.Reader, queryStr string) error { return err } - iter := query.Run(responseData) + iter := code.Run(responseData) for { v, ok := iter.Next() if !ok { diff --git a/pkg/export/filter_test.go b/pkg/export/filter_test.go index b432f4ec1..2c449f750 100644 --- a/pkg/export/filter_test.go +++ b/pkg/export/filter_test.go @@ -10,6 +10,8 @@ import ( ) func Test_filterJSON(t *testing.T) { + t.Setenv("CODE", "code_c") + type args struct { json io.Reader query string @@ -69,6 +71,27 @@ func Test_filterJSON(t *testing.T) { Alas, tis' the end ,feature `), }, + { + name: "with env var", + args: args{ + json: strings.NewReader(heredoc.Doc(`[ + { + "title": "code_a", + "labels": [{"name":"bug"}, {"name":"help wanted"}] + }, + { + "title": "code_b", + "labels": [] + }, + { + "title": "code_c", + "labels": [{}, {"name":"feature"}] + } + ]`)), + query: `.[]| select(.title == env.CODE) | .labels`, + }, + wantW: "[{},{\"name\":\"feature\"}]\n", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 205c16abe63f1d7758dd59a4fc382d37df4fcbd8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 17:59:59 +0300 Subject: [PATCH 7/8] Bump github.com/mattn/go-isatty from 0.0.14 to 0.0.16 (#6077) Bumps [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) from 0.0.14 to 0.0.16. - [Release notes](https://github.com/mattn/go-isatty/releases) - [Commits](https://github.com/mattn/go-isatty/compare/v0.0.14...v0.0.16) --- updated-dependencies: - dependency-name: github.com/mattn/go-isatty dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index db5966e07..9708cb92b 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/joho/godotenv v1.4.0 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/mattn/go-colorable v0.1.12 - github.com/mattn/go-isatty v0.0.14 + github.com/mattn/go-isatty v0.0.16 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d github.com/muesli/reflow v0.3.0 github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0 @@ -39,7 +39,7 @@ require ( github.com/stretchr/testify v1.7.5 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a + golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 golang.org/x/text v0.3.7 gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index a1deeab61..cf181542c 100644 --- a/go.sum +++ b/go.sum @@ -188,8 +188,9 @@ github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZb github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 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-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= @@ -369,8 +370,9 @@ golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= From 203a00b69cd69b11de81ccbd994b9a7829dc909d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 18:08:55 +0300 Subject: [PATCH 8/8] Bump github.com/mattn/go-colorable from 0.1.12 to 0.1.13 (#6078) Bumps [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable) from 0.1.12 to 0.1.13. - [Release notes](https://github.com/mattn/go-colorable/releases) - [Commits](https://github.com/mattn/go-colorable/compare/v0.1.12...v0.1.13) --- updated-dependencies: - dependency-name: github.com/mattn/go-colorable dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 9708cb92b..bf7c8c646 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/itchyny/gojq v0.12.8 github.com/joho/godotenv v1.4.0 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 - github.com/mattn/go-colorable v0.1.12 + github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.16 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d github.com/muesli/reflow v0.3.0 diff --git a/go.sum b/go.sum index cf181542c..7d9632582 100644 --- a/go.sum +++ b/go.sum @@ -184,8 +184,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= @@ -367,7 +367,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w 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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=