diff --git a/api/queries.go b/api/queries.go index 78b34b7e7..5560aa268 100644 --- a/api/queries.go +++ b/api/queries.go @@ -401,6 +401,45 @@ func PullRequestsForBranch(client *Client, ghRepo Repo, branch string) ([]PullRe return prs, nil } +func CreatePullRequest(client *Client, ghRepo Repo, params map[string]interface{}) (*PullRequest, error) { + repoID, err := GitHubRepoId(client, ghRepo) + if err != nil { + return nil, err + } + + query := ` + mutation CreatePullRequest($input: CreatePullRequestInput!) { + createPullRequest(input: $input) { + pullRequest { + url + } + } + }` + + inputParams := map[string]interface{}{ + "repositoryId": repoID, + } + for key, val := range params { + inputParams[key] = val + } + variables := map[string]interface{}{ + "input": inputParams, + } + + result := struct { + CreatePullRequest struct { + PullRequest PullRequest + } + }{} + + err = client.GraphQL(query, variables, &result) + if err != nil { + return nil, err + } + + return &result.CreatePullRequest.PullRequest, nil +} + func PullRequestList(client *Client, vars map[string]interface{}, limit int) ([]PullRequest, error) { type response struct { Repository struct { diff --git a/command/pr.go b/command/pr.go index ffd468e31..a70eaab73 100644 --- a/command/pr.go +++ b/command/pr.go @@ -13,6 +13,7 @@ import ( func init() { RootCmd.AddCommand(prCmd) + prCmd.AddCommand(prCreateCmd) prCmd.AddCommand(prListCmd) prCmd.AddCommand(prStatusCmd) prCmd.AddCommand(prViewCmd) diff --git a/command/pr_create.go b/command/pr_create.go new file mode 100644 index 000000000..c8d86f53a --- /dev/null +++ b/command/pr_create.go @@ -0,0 +1,223 @@ +package command + +import ( + "fmt" + "os" + "runtime" + + "github.com/AlecAivazis/survey/v2" + "github.com/github/gh-cli/api" + "github.com/github/gh-cli/context" + "github.com/github/gh-cli/git" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +func prCreate(cmd *cobra.Command, _ []string) error { + ctx := contextForCommand(cmd) + + ucc, err := git.UncommittedChangeCount() + if err != nil { + return err + } + if ucc > 0 { + noun := "change" + if ucc > 1 { + // TODO: use pluralize helper + noun = noun + "s" + } + + cmd.Printf("Warning: %d uncommitted %s\n", ucc, noun) + } + + head, err := ctx.Branch() + if err != nil { + return errors.Wrap(err, "could not determine current branch") + } + + remote, err := guessRemote(ctx) + if err != nil { + return err + } + + if err = git.Push(remote, fmt.Sprintf("HEAD:%s", head)); err != nil { + return fmt.Errorf("was not able to push to remote '%s': %s", remote, err) + } + + title, err := cmd.Flags().GetString("title") + if err != nil { + return errors.Wrap(err, "could not parse title") + } + body, err := cmd.Flags().GetString("body") + if err != nil { + return errors.Wrap(err, "could not parse body") + } + + interactive := title == "" || body == "" + + inProgress := struct { + Body string + Title string + }{} + + if interactive { + confirmed := false + editor := determineEditor() + + for !confirmed { + titleQuestion := &survey.Question{ + Name: "title", + Prompt: &survey.Input{ + Message: "PR Title", + Default: inProgress.Title, + }, + } + bodyQuestion := &survey.Question{ + Name: "body", + Prompt: &survey.Editor{ + Message: fmt.Sprintf("PR Body (%s)", editor), + FileName: "*.md", + Default: inProgress.Body, + AppendDefault: true, + Editor: editor, + }, + } + + qs := []*survey.Question{} + if title == "" { + qs = append(qs, titleQuestion) + } + if body == "" { + qs = append(qs, bodyQuestion) + } + + err := survey.Ask(qs, &inProgress) + if err != nil { + return errors.Wrap(err, "could not prompt") + } + confirmAnswers := struct { + Confirmation string + }{} + confirmQs := []*survey.Question{ + { + Name: "confirmation", + Prompt: &survey.Select{ + Message: "Submit?", + Options: []string{ + "Yes", + "Edit", + "Cancel", + }, + }, + }, + } + + err = survey.Ask(confirmQs, &confirmAnswers) + if err != nil { + return errors.Wrap(err, "could not prompt") + } + + switch confirmAnswers.Confirmation { + case "Yes": + confirmed = true + case "Edit": + continue + case "Cancel": + cmd.Println("Discarding pull request") + return nil + } + } + } + + if title == "" { + title = inProgress.Title + } + if body == "" { + body = inProgress.Body + } + base, err := cmd.Flags().GetString("base") + if err != nil { + return errors.Wrap(err, "could not parse base") + } + if base == "" { + // TODO: use default branch for the repo + base = "master" + } + + client, err := apiClientForContext(ctx) + if err != nil { + return errors.Wrap(err, "could not initialize api client") + } + + repo, err := ctx.BaseRepo() + if err != nil { + return errors.Wrap(err, "could not determine GitHub repo") + } + + isDraft, err := cmd.Flags().GetBool("draft") + if err != nil { + return errors.Wrap(err, "could not parse draft") + } + + params := map[string]interface{}{ + "title": title, + "body": body, + "draft": isDraft, + "baseRefName": base, + "headRefName": head, + } + + pr, err := api.CreatePullRequest(client, repo, params) + if err != nil { + return errors.Wrap(err, "failed to create PR") + } + + fmt.Fprintln(cmd.OutOrStdout(), pr.URL) + return nil +} + +func guessRemote(ctx context.Context) (string, error) { + remotes, err := ctx.Remotes() + if err != nil { + return "", errors.Wrap(err, "could not read git remotes") + } + + // TODO: consolidate logic with fsContext.BaseRepo + // TODO: check if the GH repo that the remote points to is writeable + remote, err := remotes.FindByName("upstream", "github", "origin", "*") + if err != nil { + return "", errors.Wrap(err, "could not determine suitable remote") + } + + return remote.Name, nil +} + +func determineEditor() string { + if runtime.GOOS == "windows" { + return "notepad" + } + if v := os.Getenv("VISUAL"); v != "" { + return v + } + if e := os.Getenv("EDITOR"); e != "" { + return e + } + return "nano" +} + +var prCreateCmd = &cobra.Command{ + Use: "create", + Short: "Create a pull request", + RunE: prCreate, +} + +func init() { + prCreateCmd.Flags().BoolP("draft", "d", false, + "Mark PR as a draft") + prCreateCmd.Flags().StringP("title", "t", "", + "Supply a title. Will prompt for one otherwise.") + prCreateCmd.Flags().StringP("body", "b", "", + "Supply a body. Will prompt for one otherwise.") + prCreateCmd.Flags().StringP("base", "T", "", + "The branch into which you want your code merged") +} diff --git a/command/pr_create_test.go b/command/pr_create_test.go new file mode 100644 index 000000000..191043be3 --- /dev/null +++ b/command/pr_create_test.go @@ -0,0 +1,136 @@ +package command + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "testing" + + "github.com/github/gh-cli/context" + "github.com/github/gh-cli/git" + "github.com/github/gh-cli/test" +) + +func TestPrCreateHelperProcess(*testing.T) { + if test.SkipTestHelperProcess() { + return + } + + args := test.GetTestHelperProcessArgs() + switch args[1] { + case "status": + switch args[0] { + case "clean": + case "dirty": + fmt.Println(" M git/git.go") + default: + fmt.Fprintf(os.Stderr, "unknown scenario: %q", args[0]) + os.Exit(1) + } + case "push": + default: + fmt.Fprintf(os.Stderr, "unknown command: %q", args[1]) + os.Exit(1) + } + os.Exit(0) +} + +func TestPRCreate(t *testing.T) { + ctx := context.NewBlank() + ctx.SetBranch("feature") + ctx.SetRemotes(map[string]string{ + "origin": "OWNER/REPO", + }) + initContext = func() context.Context { + return ctx + } + http := initFakeHTTP() + + http.StubResponse(200, bytes.NewBufferString(` + { "data": { "repository": { + "id": "REPOID" + } } } + `)) + http.StubResponse(200, bytes.NewBufferString(` + { "data": { "createPullRequest": { "pullRequest": { + "URL": "https://github.com/OWNER/REPO/pull/12" + } } } } + `)) + + origGitCommand := git.GitCommand + git.GitCommand = test.StubExecCommand("TestPrCreateHelperProcess", "clean") + defer func() { + git.GitCommand = origGitCommand + }() + + out := bytes.Buffer{} + prCreateCmd.SetOut(&out) + + RootCmd.SetArgs([]string{"pr", "create", "-t", "mytitle", "-b", "mybody"}) + _, err := prCreateCmd.ExecuteC() + eq(t, err, nil) + + bodyBytes, _ := ioutil.ReadAll(http.Requests[1].Body) + reqBody := struct { + Variables struct { + Input struct { + RepositoryID string + Title string + Body string + BaseRefName string + HeadRefName string + } + } + }{} + json.Unmarshal(bodyBytes, &reqBody) + + eq(t, reqBody.Variables.Input.RepositoryID, "REPOID") + eq(t, reqBody.Variables.Input.Title, "mytitle") + eq(t, reqBody.Variables.Input.Body, "mybody") + eq(t, reqBody.Variables.Input.BaseRefName, "master") + eq(t, reqBody.Variables.Input.HeadRefName, "feature") + + eq(t, out.String(), "https://github.com/OWNER/REPO/pull/12\n") +} + +func TestPRCreate_ReportsUncommittedChanges(t *testing.T) { + ctx := context.NewBlank() + ctx.SetBranch("feature") + ctx.SetRemotes(map[string]string{ + "origin": "OWNER/REPO", + }) + initContext = func() context.Context { + return ctx + } + http := initFakeHTTP() + + http.StubResponse(200, bytes.NewBufferString(` + { "data": { "repository": { + "id": "REPOID" + } } } + `)) + http.StubResponse(200, bytes.NewBufferString(` + { "data": { "createPullRequest": { "pullRequest": { + "URL": "https://github.com/OWNER/REPO/pull/12" + } } } } + `)) + + origGitCommand := git.GitCommand + git.GitCommand = test.StubExecCommand("TestPrCreateHelperProcess", "dirty") + defer func() { + git.GitCommand = origGitCommand + }() + + out := bytes.Buffer{} + prCreateCmd.SetOut(&out) + + RootCmd.SetArgs([]string{"pr", "create", "-t", "mytitle", "-b", "mybody"}) + _, err := prCreateCmd.ExecuteC() + eq(t, err, nil) + + eq(t, out.String(), `Warning: 1 uncommitted change +https://github.com/OWNER/REPO/pull/12 +`) +} diff --git a/command/root.go b/command/root.go index b3a95d878..22c0122a1 100644 --- a/command/root.go +++ b/command/root.go @@ -73,7 +73,8 @@ var apiClientForContext = func(ctx context.Context) (*api.Client, error) { api.AddHeader("Authorization", fmt.Sprintf("token %s", token)), api.AddHeader("User-Agent", fmt.Sprintf("GitHub CLI %s", Version)), // antiope-preview: Checks - api.AddHeader("Accept", "application/vnd.github.antiope-preview+json"), + // shadow-cat-preview: Draft pull requests + api.AddHeader("Accept", "application/vnd.github.antiope-preview+json, application/vnd.github.shadow-cat-preview"), } if verbose := os.Getenv("DEBUG"); verbose != "" { opts = append(opts, api.VerboseLog(os.Stderr)) diff --git a/context/blank_context.go b/context/blank_context.go index d5ad2cafe..733ef38a8 100644 --- a/context/blank_context.go +++ b/context/blank_context.go @@ -3,10 +3,12 @@ package context import ( "fmt" "strings" + + "github.com/github/gh-cli/git" ) // NewBlank initializes a blank Context suitable for testing -func NewBlank() Context { +func NewBlank() *blankContext { return &blankContext{} } @@ -16,6 +18,7 @@ type blankContext struct { authLogin string branch string baseRepo GitHubRepository + remotes Remotes } type ghRepo struct { @@ -54,14 +57,36 @@ func (c *blankContext) SetBranch(b string) { } func (c *blankContext) Remotes() (Remotes, error) { - return Remotes{}, nil + if c.remotes == nil { + return nil, fmt.Errorf("remotes were not initialized") + } + return c.remotes, nil +} + +func (c *blankContext) SetRemotes(stubs map[string]string) { + c.remotes = Remotes{} + for remoteName, repo := range stubs { + ownerWithName := strings.SplitN(repo, "/", 2) + c.remotes = append(c.remotes, &Remote{ + Remote: &git.Remote{Name: remoteName}, + Owner: ownerWithName[0], + Repo: ownerWithName[1], + }) + } } func (c *blankContext) BaseRepo() (GitHubRepository, error) { - if c.baseRepo == nil { - return nil, fmt.Errorf("base repo was not initialized") + if c.baseRepo != nil { + return c.baseRepo, nil } - return c.baseRepo, nil + remotes, err := c.Remotes() + if err != nil { + return nil, err + } + if len(remotes) < 1 { + return nil, fmt.Errorf("remotes are empty") + } + return remotes[0], nil } func (c *blankContext) SetBaseRepo(nwo string) { diff --git a/git/git.go b/git/git.go index a2a385f7f..411863b31 100644 --- a/git/git.go +++ b/git/git.go @@ -206,6 +206,34 @@ func LocalBranches() ([]string, error) { return branches, nil } +var GitCommand = func(args ...string) *exec.Cmd { + return exec.Command("git", args...) +} + +func UncommittedChangeCount() (int, error) { + statusCmd := GitCommand("status", "--porcelain") + output, err := utils.PrepareCmd(statusCmd).Output() + if err != nil { + return 0, err + } + lines := strings.Split(string(output), "\n") + + count := 0 + + for _, l := range lines { + if l != "" { + count++ + } + } + + return count, nil +} + +func Push(remote string, ref string) error { + cmd := GitCommand("push", "--set-upstream", remote, ref) + return cmd.Run() +} + func outputLines(output []byte) []string { lines := strings.TrimSuffix(string(output), "\n") return strings.Split(lines, "\n") diff --git a/git/git_test.go b/git/git_test.go new file mode 100644 index 000000000..dc1446c33 --- /dev/null +++ b/git/git_test.go @@ -0,0 +1,57 @@ +package git + +import ( + "fmt" + "os" + "strings" + "testing" + + "github.com/github/gh-cli/test" +) + +func TestGitStatusHelperProcess(*testing.T) { + if test.SkipTestHelperProcess() { + return + } + + args := test.GetTestHelperProcessArgs() + switch args[0] { + case "no changes": + case "one change": + fmt.Println(" M poem.txt") + case "untracked file": + fmt.Println(" M poem.txt") + fmt.Println("?? new.txt") + case "boom": + os.Exit(1) + } + os.Exit(0) +} + +func Test_UncommittedChangeCount(t *testing.T) { + origGitCommand := GitCommand + defer func() { + GitCommand = origGitCommand + }() + + cases := map[string]int{ + "no changes": 0, + "one change": 1, + "untracked file": 2, + } + + for k, v := range cases { + GitCommand = test.StubExecCommand("TestGitStatusHelperProcess", k) + ucc, _ := UncommittedChangeCount() + + if ucc != v { + t.Errorf("got unexpected ucc value: %d for case %s", ucc, k) + } + } + + GitCommand = test.StubExecCommand("TestGitStatusHelperProcess", "boom") + _, err := UncommittedChangeCount() + if !strings.HasSuffix(err.Error(), "git.test: exit status 1") { + t.Errorf("got unexpected error message: %s", err) + } +} diff --git a/go.mod b/go.mod index 9c89ff7b1..f4a0ea38b 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,15 @@ module github.com/github/gh-cli go 1.13 require ( + github.com/AlecAivazis/survey/v2 v2.0.4 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/mattn/go-colorable v0.1.2 github.com/mattn/go-isatty v0.0.9 github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b github.com/mitchellh/go-homedir v1.1.0 + github.com/pkg/errors v0.8.1 github.com/spf13/cobra v0.0.5 - golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 + github.com/stretchr/testify v1.3.0 // indirect + golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 gopkg.in/yaml.v3 v3.0.0-20191010095647-fc94e3f71652 ) diff --git a/go.sum b/go.sum index af7d46f56..b2f2e063e 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,27 @@ +github.com/AlecAivazis/survey/v2 v2.0.4 h1:qzXnJSzXEvmUllWqMBWpZndvT2YfoAUzAMvZUax3L2M= +github.com/AlecAivazis/survey/v2 v2.0.4/go.mod h1:WYBhg6f0y/fNYUuesWQc0PKbJcEliGcYHB9sNT3Bg74= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= +github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= +github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ= +github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -25,6 +34,8 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -36,13 +47,24 @@ github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb6 github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/test/fixtures/createPr.json b/test/fixtures/createPr.json new file mode 100644 index 000000000..47be76948 --- /dev/null +++ b/test/fixtures/createPr.json @@ -0,0 +1,9 @@ +{ + "data": { + "createPullRequest": { + "pullRequest": { + "url": "https://github.com/vilmibm/testing/pull/14" + } + } + } +} diff --git a/test/fixtures/repoId.json b/test/fixtures/repoId.json new file mode 100644 index 000000000..965b39f3d --- /dev/null +++ b/test/fixtures/repoId.json @@ -0,0 +1,7 @@ +{ + "data": { + "repository": { + "id": "a repo id" + } + } +} diff --git a/test/helpers.go b/test/helpers.go index 162befa56..dca930be5 100644 --- a/test/helpers.go +++ b/test/helpers.go @@ -11,6 +11,38 @@ import ( "github.com/spf13/cobra" ) +func GetTestHelperProcessArgs() []string { + args := os.Args + for len(args) > 0 { + if args[0] == "--" { + args = args[1:] + break + } + args = args[1:] + } + return args +} + +func SkipTestHelperProcess() bool { + return os.Getenv("GO_WANT_HELPER_PROCESS") != "1" +} + +func StubExecCommand(testHelper string, desiredOutput string) func(...string) *exec.Cmd { + return func(args ...string) *exec.Cmd { + cs := []string{ + fmt.Sprintf("-test.run=%s", testHelper), + "--", desiredOutput} + cs = append(cs, args...) + env := []string{ + "GO_WANT_HELPER_PROCESS=1", + } + + cmd := exec.Command(os.Args[0], cs...) + cmd.Env = append(env, os.Environ()...) + return cmd + } +} + type TempGitRepo struct { Remote string TearDown func()