diff --git a/pkg/cmd/repo/create/create.go b/pkg/cmd/repo/create/create.go index c8b79bc21..b8d8c764e 100644 --- a/pkg/cmd/repo/create/create.go +++ b/pkg/cmd/repo/create/create.go @@ -45,10 +45,6 @@ type CreateOptions struct { DisableIssues bool DisableWiki bool Interactive bool - - ConfirmSubmit bool - EnableIssues bool - EnableWiki bool } func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Command { @@ -58,6 +54,9 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co Config: f.Config, } + var enableIssues bool + var enableWiki bool + cmd := &cobra.Command{ Use: "create []", Short: "Create a new repository", @@ -135,10 +134,10 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co } if cmd.Flags().Changed("enable-issues") { - opts.DisableIssues = !opts.EnableIssues + opts.DisableIssues = !enableIssues } if cmd.Flags().Changed("enable-wiki") { - opts.DisableWiki = !opts.EnableWiki + opts.DisableWiki = !enableWiki } if opts.Template != "" && (opts.Homepage != "" || opts.Team != "" || opts.DisableIssues || opts.DisableWiki) { return cmdutil.FlagErrorf("the `--template` option is not supported with `--homepage`, `--team`, `--disable-issues`, or `--disable-wiki`") @@ -168,9 +167,9 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co cmd.Flags().BoolVar(&opts.DisableWiki, "disable-wiki", false, "Disable wiki in the new repository") // deprecated flags - cmd.Flags().BoolVarP(&opts.ConfirmSubmit, "confirm", "y", false, "Skip the confirmation prompt") - cmd.Flags().BoolVar(&opts.EnableIssues, "enable-issues", true, "Enable issues in the new repository") - cmd.Flags().BoolVar(&opts.EnableWiki, "enable-wiki", true, "Enable wiki in the new repository") + cmd.Flags().BoolP("confirm", "y", false, "Skip the confirmation prompt") + cmd.Flags().BoolVar(&enableIssues, "enable-issues", true, "Enable issues in the new repository") + cmd.Flags().BoolVar(&enableWiki, "enable-wiki", true, "Enable wiki in the new repository") _ = cmd.Flags().MarkDeprecated("confirm", "Pass any argument to skip confirmation prompt") _ = cmd.Flags().MarkDeprecated("enable-issues", "Disable issues with `--disable-issues`") @@ -224,31 +223,27 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co } func createRun(opts *CreateOptions) error { - var fromScratch bool - modeOptions := []string{ - "Create a new repository on GitHub from scratch", - "Push an existing local repository to GitHub"} + fromScratch := opts.Source == "" if opts.Interactive { - var createMode string - createModePrompt := &survey.Select{ + var selectedMode string + modeOptions := []string{ + "Create a new repository on GitHub from scratch", + "Push an existing local repository to GitHub", + } + if err := prompt.SurveyAskOne(&survey.Select{ Message: "What would you like to do?", Options: modeOptions, - } - err := prompt.SurveyAskOne(createModePrompt, &createMode) - if err != nil { + }, &selectedMode); err != nil { return err } - fromScratch = createMode == modeOptions[0] - } else { - fromScratch = opts.Source == "" + fromScratch = selectedMode == modeOptions[0] } if fromScratch { return createFromScratch(opts) - } else { - return createFromLocal(opts) } + return createFromLocal(opts) } // create new repo on remote host @@ -282,6 +277,10 @@ func createFromScratch(opts *CreateOptions) error { if err != nil { return err } + + if err := confirmSubmission(opts.Name, opts.Visibility); err != nil { + return err + } } if strings.Contains(opts.Name, "/") { @@ -339,16 +338,6 @@ func createFromScratch(opts *CreateOptions) error { return err } - if opts.Interactive { - doCreate, err := confirmSubmission(opts.Name, repoToCreate.RepoOwner(), opts.Visibility) - if err != nil { - return err - } - if !doCreate { - return cmdutil.CancelError - } - } - cs := opts.IO.ColorScheme() isTTY := opts.IO.IsStdoutTTY() if isTTY { @@ -356,6 +345,8 @@ func createFromScratch(opts *CreateOptions) error { "%s Created repository %s on GitHub\n", cs.SuccessIconWithColor(cs.Green), ghrepo.FullName(repo)) + } else { + fmt.Fprintln(opts.IO.Out, repo.URL) } if opts.Interactive { @@ -503,6 +494,8 @@ func createFromLocal(opts *CreateOptions) error { "%s Created repository %s on GitHub\n", cs.SuccessIconWithColor(cs.Green), ghrepo.FullName(repo)) + } else { + fmt.Fprintln(stdout, repo.URL) } protocol, err := cfg.Get(repo.RepoHost(), "git_protocol") @@ -783,13 +776,13 @@ func interactiveRepoInfo(defaultName string) (string, string, string, error) { { Name: "repoName", Prompt: &survey.Input{ - Message: "Repository Name: ", + Message: "Repository name", Default: defaultName, }, }, { Name: "repoDescription", - Prompt: &survey.Input{Message: "Description: "}, + Prompt: &survey.Input{Message: "Description"}, }, { Name: "repoVisibility", @@ -816,7 +809,7 @@ func interactiveRepoInfo(defaultName string) (string, string, string, error) { func interactiveSource() (string, error) { var sourcePath string sourcePrompt := &survey.Input{ - Message: "Path to local repository: ", + Message: "Path to local repository", Default: "."} err := prompt.SurveyAskOne(sourcePrompt, &sourcePath) @@ -826,33 +819,28 @@ func interactiveSource() (string, error) { return sourcePath, nil } -func confirmSubmission(repoName, repoOwner, visibility string) (bool, error) { - qs := []*survey.Question{} - targetRepo := normalizeRepoName(repoName) - if repoOwner != "" { - targetRepo = fmt.Sprintf("%s/%s", repoOwner, targetRepo) +func confirmSubmission(repoWithOwner, visibility string) error { + targetRepo := normalizeRepoName(repoWithOwner) + if idx := strings.IndexRune(repoWithOwner, '/'); idx > 0 { + targetRepo = repoWithOwner[0:idx+1] + normalizeRepoName(repoWithOwner[idx+1:]) } - promptString := fmt.Sprintf(`This will create "%s" as a %s repository on GitHub. Continue?`, targetRepo, strings.ToLower(visibility)) - - confirmSubmitQuestion := &survey.Question{ + var answer struct { + ConfirmSubmit bool + } + err := prompt.SurveyAsk([]*survey.Question{{ Name: "confirmSubmit", Prompt: &survey.Confirm{ - Message: promptString, + Message: fmt.Sprintf(`This will create "%s" as a %s repository on GitHub. Continue?`, targetRepo, strings.ToLower(visibility)), Default: true, }, - } - qs = append(qs, confirmSubmitQuestion) - - answer := struct { - ConfirmSubmit bool - }{} - - err := prompt.SurveyAsk(qs, &answer) + }}, &answer) if err != nil { - return false, err + return err } - - return answer.ConfirmSubmit, nil + if !answer.ConfirmSubmit { + return cmdutil.CancelError + } + return nil } // normalizeRepoName takes in the repo name the user inputted and normalizes it using the same logic as GitHub (GitHub.com/new) diff --git a/pkg/cmd/repo/create/create_test.go b/pkg/cmd/repo/create/create_test.go index 162bf7741..9048845fd 100644 --- a/pkg/cmd/repo/create/create_test.go +++ b/pkg/cmd/repo/create/create_test.go @@ -205,6 +205,28 @@ func Test_createRun(t *testing.T) { cs.Register(`git clone https://github.com/OWNER/REPO.git`, 0, "") }, }, + { + name: "interactive create from scratch but cancel before submit", + opts: &CreateOptions{Interactive: true}, + tty: true, + askStubs: func(as *prompt.AskStubber) { + as.StubOne("Create a new repository on GitHub from scratch") + as.Stub([]*prompt.QuestionStub{ + {Name: "repoName", Value: "REPO"}, + {Name: "repoDescription", Value: "my new repo"}, + {Name: "repoVisibility", Value: "PRIVATE"}, + }) + as.Stub([]*prompt.QuestionStub{ + {Name: "addGitIgnore", Value: false}}) + as.Stub([]*prompt.QuestionStub{ + {Name: "addLicense", Value: false}}) + as.Stub([]*prompt.QuestionStub{ + {Name: "confirmSubmit", Value: false}}) + }, + wantStdout: "", + wantErr: true, + errMsg: "CancelError", + }, { name: "interactive with existing repository public", opts: &CreateOptions{Interactive: true}, @@ -323,6 +345,66 @@ func Test_createRun(t *testing.T) { }, wantStdout: "āœ“ Created repository OWNER/REPO on GitHub\nāœ“ Added remote https://github.com/OWNER/REPO.git\nāœ“ Pushed commits to https://github.com/OWNER/REPO.git\n", }, + { + name: "noninteractive create from scratch", + opts: &CreateOptions{ + Interactive: false, + Name: "REPO", + Visibility: "PRIVATE", + }, + tty: false, + httpStubs: func(reg *httpmock.Registry) { + reg.Register( + httpmock.GraphQL(`mutation RepositoryCreate\b`), + httpmock.StringResponse(` + { + "data": { + "createRepository": { + "repository": { + "id": "REPOID", + "name": "REPO", + "owner": {"login":"OWNER"}, + "url": "https://github.com/OWNER/REPO" + } + } + } + }`)) + }, + wantStdout: "https://github.com/OWNER/REPO\n", + }, + { + name: "noninteractive create from source", + opts: &CreateOptions{ + Interactive: false, + Source: ".", + Name: "REPO", + Visibility: "PRIVATE", + }, + tty: false, + httpStubs: func(reg *httpmock.Registry) { + reg.Register( + httpmock.GraphQL(`mutation RepositoryCreate\b`), + httpmock.StringResponse(` + { + "data": { + "createRepository": { + "repository": { + "id": "REPOID", + "name": "REPO", + "owner": {"login":"OWNER"}, + "url": "https://github.com/OWNER/REPO" + } + } + } + }`)) + }, + execStubs: func(cs *run.CommandStubber) { + cs.Register(`git -C . rev-parse --git-dir`, 0, ".git") + cs.Register(`git -C . rev-parse HEAD`, 0, "commithash") + cs.Register(`git -C . remote add origin https://github.com/OWNER/REPO`, 0, "") + }, + wantStdout: "https://github.com/OWNER/REPO\n", + }, } for _, tt := range tests { q, teardown := prompt.InitAskStubber() @@ -348,7 +430,7 @@ func Test_createRun(t *testing.T) { tt.execStubs(cs) } - io, _, stdout, _ := iostreams.Test() + io, _, stdout, stderr := iostreams.Test() io.SetStdinTTY(tt.tty) io.SetStdoutTTY(tt.tty) tt.opts.IO = io @@ -363,6 +445,7 @@ func Test_createRun(t *testing.T) { } assert.NoError(t, err) assert.Equal(t, tt.wantStdout, stdout.String()) + assert.Equal(t, "", stderr.String()) }) } }