github.com/cli/cli@v1.14.1-0.20210902173923-1af6a669e342/pkg/cmd/repo/create/create.go (about)

     1  package create
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net/http"
     7  	"path"
     8  	"strings"
     9  
    10  	"github.com/AlecAivazis/survey/v2"
    11  	"github.com/MakeNowJust/heredoc"
    12  	"github.com/cli/cli/api"
    13  	"github.com/cli/cli/git"
    14  	"github.com/cli/cli/internal/config"
    15  	"github.com/cli/cli/internal/ghinstance"
    16  	"github.com/cli/cli/internal/ghrepo"
    17  	"github.com/cli/cli/internal/run"
    18  	"github.com/cli/cli/pkg/cmdutil"
    19  	"github.com/cli/cli/pkg/iostreams"
    20  	"github.com/cli/cli/pkg/prompt"
    21  	"github.com/spf13/cobra"
    22  )
    23  
    24  type CreateOptions struct {
    25  	HttpClient func() (*http.Client, error)
    26  	Config     func() (config.Config, error)
    27  	IO         *iostreams.IOStreams
    28  
    29  	Name              string
    30  	Description       string
    31  	Homepage          string
    32  	Team              string
    33  	Template          string
    34  	EnableIssues      bool
    35  	EnableWiki        bool
    36  	Public            bool
    37  	Private           bool
    38  	Internal          bool
    39  	ConfirmSubmit     bool
    40  	GitIgnoreTemplate string
    41  	LicenseTemplate   string
    42  }
    43  
    44  func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Command {
    45  	opts := &CreateOptions{
    46  		IO:         f.IOStreams,
    47  		HttpClient: f.HttpClient,
    48  		Config:     f.Config,
    49  	}
    50  
    51  	cmd := &cobra.Command{
    52  		Use:   "create [<name>]",
    53  		Short: "Create a new repository",
    54  		Long: heredoc.Docf(`
    55  			Create a new GitHub repository.
    56  
    57  			When the current directory is a local git repository, the new repository will be added
    58  			as the "origin" git remote. Otherwise, the command will prompt to clone the new
    59  			repository into a sub-directory.
    60  
    61  			To create a repository non-interactively, supply the following:
    62  			- the name argument;
    63  			- the %[1]s--confirm%[1]s flag;
    64  			- one of %[1]s--public%[1]s, %[1]s--private%[1]s, or %[1]s--internal%[1]s.
    65  
    66  			To toggle off %[1]s--enable-issues%[1]s or %[1]s--enable-wiki%[1]s, which are enabled
    67  			by default, use the %[1]s--enable-issues=false%[1]s syntax.
    68  		`, "`"),
    69  		Args: cobra.MaximumNArgs(1),
    70  		Example: heredoc.Doc(`
    71  			# create a repository under your account using the current directory name
    72  			$ git init my-project
    73  			$ cd my-project
    74  			$ gh repo create
    75  
    76  			# create a repository with a specific name
    77  			$ gh repo create my-project
    78  
    79  			# create a repository in an organization
    80  			$ gh repo create cli/my-project
    81  
    82  			# disable issues and wiki
    83  			$ gh repo create --enable-issues=false --enable-wiki=false
    84  	  `),
    85  		Annotations: map[string]string{
    86  			"help:arguments": heredoc.Doc(`
    87  				A repository can be supplied as an argument in any of the following formats:
    88  				- "OWNER/REPO"
    89  				- by URL, e.g. "https://github.com/OWNER/REPO"
    90  			`),
    91  		},
    92  		RunE: func(cmd *cobra.Command, args []string) error {
    93  			if len(args) > 0 {
    94  				opts.Name = args[0]
    95  			}
    96  
    97  			if len(args) == 0 && (opts.GitIgnoreTemplate != "" || opts.LicenseTemplate != "") {
    98  				return &cmdutil.FlagError{Err: errors.New(".gitignore and license templates are added only when a specific repository name is passed")}
    99  			}
   100  
   101  			if opts.Template != "" && (opts.GitIgnoreTemplate != "" || opts.LicenseTemplate != "") {
   102  				return &cmdutil.FlagError{Err: errors.New(".gitignore and license templates are not added when template is provided")}
   103  			}
   104  
   105  			if !opts.IO.CanPrompt() {
   106  				if opts.Name == "" {
   107  					return &cmdutil.FlagError{Err: errors.New("name argument required when not running interactively")}
   108  				}
   109  
   110  				if !opts.Internal && !opts.Private && !opts.Public {
   111  					return &cmdutil.FlagError{Err: errors.New("`--public`, `--private`, or `--internal` required when not running interactively")}
   112  				}
   113  			}
   114  
   115  			if opts.Template != "" && (opts.Homepage != "" || opts.Team != "" || cmd.Flags().Changed("enable-issues") || cmd.Flags().Changed("enable-wiki")) {
   116  				return &cmdutil.FlagError{Err: errors.New("The `--template` option is not supported with `--homepage`, `--team`, `--enable-issues`, or `--enable-wiki`")}
   117  			}
   118  
   119  			if runF != nil {
   120  				return runF(opts)
   121  			}
   122  			return createRun(opts)
   123  		},
   124  	}
   125  
   126  	cmd.Flags().StringVarP(&opts.Description, "description", "d", "", "Description of the repository")
   127  	cmd.Flags().StringVarP(&opts.Homepage, "homepage", "h", "", "Repository home page `URL`")
   128  	cmd.Flags().StringVarP(&opts.Team, "team", "t", "", "The `name` of the organization team to be granted access")
   129  	cmd.Flags().StringVarP(&opts.Template, "template", "p", "", "Make the new repository based on a template `repository`")
   130  	cmd.Flags().BoolVar(&opts.EnableIssues, "enable-issues", true, "Enable issues in the new repository")
   131  	cmd.Flags().BoolVar(&opts.EnableWiki, "enable-wiki", true, "Enable wiki in the new repository")
   132  	cmd.Flags().BoolVar(&opts.Public, "public", false, "Make the new repository public")
   133  	cmd.Flags().BoolVar(&opts.Private, "private", false, "Make the new repository private")
   134  	cmd.Flags().BoolVar(&opts.Internal, "internal", false, "Make the new repository internal")
   135  	cmd.Flags().BoolVarP(&opts.ConfirmSubmit, "confirm", "y", false, "Skip the confirmation prompt")
   136  	cmd.Flags().StringVarP(&opts.GitIgnoreTemplate, "gitignore", "g", "", "Specify a gitignore template for the repository")
   137  	cmd.Flags().StringVarP(&opts.LicenseTemplate, "license", "l", "", "Specify an Open Source License for the repository")
   138  
   139  	_ = cmd.RegisterFlagCompletionFunc("gitignore", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
   140  		httpClient, err := opts.HttpClient()
   141  		if err != nil {
   142  			return nil, cobra.ShellCompDirectiveError
   143  		}
   144  		cfg, err := opts.Config()
   145  		if err != nil {
   146  			return nil, cobra.ShellCompDirectiveError
   147  		}
   148  		hostname, err := cfg.DefaultHost()
   149  		if err != nil {
   150  			return nil, cobra.ShellCompDirectiveError
   151  		}
   152  		results, err := listGitIgnoreTemplates(httpClient, hostname)
   153  		if err != nil {
   154  			return nil, cobra.ShellCompDirectiveError
   155  		}
   156  		return results, cobra.ShellCompDirectiveNoFileComp
   157  	})
   158  
   159  	_ = cmd.RegisterFlagCompletionFunc("license", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
   160  		httpClient, err := opts.HttpClient()
   161  		if err != nil {
   162  			return nil, cobra.ShellCompDirectiveError
   163  		}
   164  		cfg, err := opts.Config()
   165  		if err != nil {
   166  			return nil, cobra.ShellCompDirectiveError
   167  		}
   168  		hostname, err := cfg.DefaultHost()
   169  		if err != nil {
   170  			return nil, cobra.ShellCompDirectiveError
   171  		}
   172  		licenses, err := listLicenseTemplates(httpClient, hostname)
   173  		if err != nil {
   174  			return nil, cobra.ShellCompDirectiveError
   175  		}
   176  		var results []string
   177  		for _, license := range licenses {
   178  			results = append(results, fmt.Sprintf("%s\t%s", license.Key, license.Name))
   179  		}
   180  		return results, cobra.ShellCompDirectiveNoFileComp
   181  	})
   182  
   183  	return cmd
   184  }
   185  
   186  func createRun(opts *CreateOptions) error {
   187  	projectDir, projectDirErr := git.ToplevelDir()
   188  	isNameAnArg := false
   189  	isDescEmpty := opts.Description == ""
   190  	isVisibilityPassed := false
   191  	inLocalRepo := projectDirErr == nil
   192  
   193  	if opts.Name != "" {
   194  		isNameAnArg = true
   195  	} else {
   196  		if projectDirErr != nil {
   197  			return projectDirErr
   198  		}
   199  		opts.Name = path.Base(projectDir)
   200  	}
   201  
   202  	enabledFlagCount := 0
   203  	visibility := ""
   204  	if opts.Public {
   205  		enabledFlagCount++
   206  		visibility = "PUBLIC"
   207  	}
   208  	if opts.Private {
   209  		enabledFlagCount++
   210  		visibility = "PRIVATE"
   211  	}
   212  	if opts.Internal {
   213  		enabledFlagCount++
   214  		visibility = "INTERNAL"
   215  	}
   216  
   217  	if enabledFlagCount > 1 {
   218  		return fmt.Errorf("expected exactly one of `--public`, `--private`, or `--internal` to be true")
   219  	} else if enabledFlagCount == 1 {
   220  		isVisibilityPassed = true
   221  	}
   222  
   223  	cfg, err := opts.Config()
   224  	if err != nil {
   225  		return err
   226  	}
   227  
   228  	var gitIgnoreTemplate, repoLicenseTemplate string
   229  
   230  	gitIgnoreTemplate = opts.GitIgnoreTemplate
   231  	repoLicenseTemplate = opts.LicenseTemplate
   232  
   233  	// Trigger interactive prompt if name is not passed
   234  	if !isNameAnArg {
   235  		newName, newDesc, newVisibility, err := interactiveRepoCreate(isDescEmpty, isVisibilityPassed, opts.Name)
   236  		if err != nil {
   237  			return err
   238  		}
   239  		if newName != "" {
   240  			opts.Name = newName
   241  		}
   242  		if newDesc != "" {
   243  			opts.Description = newDesc
   244  		}
   245  		if newVisibility != "" {
   246  			visibility = newVisibility
   247  		}
   248  
   249  	} else {
   250  		// Go for a prompt only if visibility isn't passed
   251  		if !isVisibilityPassed {
   252  			newVisibility, err := getVisibility()
   253  			if err != nil {
   254  				return nil
   255  			}
   256  			visibility = newVisibility
   257  		}
   258  
   259  		httpClient, err := opts.HttpClient()
   260  		if err != nil {
   261  			return err
   262  		}
   263  
   264  		host, err := cfg.DefaultHost()
   265  		if err != nil {
   266  			return err
   267  		}
   268  
   269  		// GitIgnore and License templates not added when a template repository
   270  		// is passed, or when the confirm flag is set.
   271  		if opts.Template == "" && opts.IO.CanPrompt() && !opts.ConfirmSubmit {
   272  			if gitIgnoreTemplate == "" {
   273  				gt, err := interactiveGitIgnore(httpClient, host)
   274  				if err != nil {
   275  					return err
   276  				}
   277  				gitIgnoreTemplate = gt
   278  			}
   279  			if repoLicenseTemplate == "" {
   280  				lt, err := interactiveLicense(httpClient, host)
   281  				if err != nil {
   282  					return err
   283  				}
   284  				repoLicenseTemplate = lt
   285  			}
   286  		}
   287  	}
   288  
   289  	var repoToCreate ghrepo.Interface
   290  
   291  	if strings.Contains(opts.Name, "/") {
   292  		var err error
   293  		repoToCreate, err = ghrepo.FromFullName(opts.Name)
   294  		if err != nil {
   295  			return fmt.Errorf("argument error: %w", err)
   296  		}
   297  	} else {
   298  		host, err := cfg.DefaultHost()
   299  		if err != nil {
   300  			return err
   301  		}
   302  		repoToCreate = ghrepo.NewWithHost("", opts.Name, host)
   303  	}
   304  
   305  	input := repoCreateInput{
   306  		Name:              repoToCreate.RepoName(),
   307  		Visibility:        visibility,
   308  		OwnerLogin:        repoToCreate.RepoOwner(),
   309  		TeamSlug:          opts.Team,
   310  		Description:       opts.Description,
   311  		HomepageURL:       opts.Homepage,
   312  		HasIssuesEnabled:  opts.EnableIssues,
   313  		HasWikiEnabled:    opts.EnableWiki,
   314  		GitIgnoreTemplate: gitIgnoreTemplate,
   315  		LicenseTemplate:   repoLicenseTemplate,
   316  	}
   317  
   318  	httpClient, err := opts.HttpClient()
   319  	if err != nil {
   320  		return err
   321  	}
   322  
   323  	var templateRepoMainBranch string
   324  	if opts.Template != "" {
   325  		var templateRepo ghrepo.Interface
   326  		apiClient := api.NewClientFromHTTP(httpClient)
   327  
   328  		templateRepoName := opts.Template
   329  		if !strings.Contains(templateRepoName, "/") {
   330  			currentUser, err := api.CurrentLoginName(apiClient, ghinstance.Default())
   331  			if err != nil {
   332  				return err
   333  			}
   334  			templateRepoName = currentUser + "/" + templateRepoName
   335  		}
   336  		templateRepo, err = ghrepo.FromFullName(templateRepoName)
   337  		if err != nil {
   338  			return fmt.Errorf("argument error: %w", err)
   339  		}
   340  
   341  		repo, err := api.GitHubRepo(apiClient, templateRepo)
   342  		if err != nil {
   343  			return err
   344  		}
   345  
   346  		input.TemplateRepositoryID = repo.ID
   347  		templateRepoMainBranch = repo.DefaultBranchRef.Name
   348  	}
   349  
   350  	createLocalDirectory := opts.ConfirmSubmit
   351  	if !opts.ConfirmSubmit {
   352  		opts.ConfirmSubmit, err = confirmSubmission(input.Name, input.OwnerLogin, inLocalRepo)
   353  		if err != nil {
   354  			return err
   355  		}
   356  	}
   357  
   358  	if opts.ConfirmSubmit {
   359  		repo, err := repoCreate(httpClient, repoToCreate.RepoHost(), input)
   360  		if err != nil {
   361  			return err
   362  		}
   363  		stderr := opts.IO.ErrOut
   364  		stdout := opts.IO.Out
   365  		cs := opts.IO.ColorScheme()
   366  		isTTY := opts.IO.IsStdoutTTY()
   367  
   368  		if isTTY {
   369  			fmt.Fprintf(stderr, "%s Created repository %s on GitHub\n", cs.SuccessIconWithColor(cs.Green), ghrepo.FullName(repo))
   370  		} else {
   371  			fmt.Fprintln(stdout, ghrepo.GenerateRepoURL(repo, ""))
   372  		}
   373  
   374  		protocol, err := cfg.Get(repo.RepoHost(), "git_protocol")
   375  		if err != nil {
   376  			return err
   377  		}
   378  
   379  		remoteURL := ghrepo.FormatRemoteURL(repo, protocol)
   380  
   381  		if inLocalRepo {
   382  			_, err = git.AddRemote("origin", remoteURL)
   383  			if err != nil {
   384  				return err
   385  			}
   386  			if isTTY {
   387  				fmt.Fprintf(stderr, "%s Added remote %s\n", cs.SuccessIcon(), remoteURL)
   388  			}
   389  		} else {
   390  			if opts.IO.CanPrompt() {
   391  				if !createLocalDirectory && (gitIgnoreTemplate == "" && repoLicenseTemplate == "") {
   392  					err := prompt.Confirm(fmt.Sprintf(`Create a local project directory for "%s"?`, ghrepo.FullName(repo)), &createLocalDirectory)
   393  					if err != nil {
   394  						return err
   395  					}
   396  				} else if !createLocalDirectory && (gitIgnoreTemplate != "" || repoLicenseTemplate != "") {
   397  					err := prompt.Confirm(fmt.Sprintf(`Clone the remote project directory "%s"?`, ghrepo.FullName(repo)), &createLocalDirectory)
   398  					if err != nil {
   399  						return err
   400  					}
   401  				}
   402  			}
   403  			if createLocalDirectory && (gitIgnoreTemplate == "" && repoLicenseTemplate == "") {
   404  				path := repo.RepoName()
   405  				checkoutBranch := ""
   406  				if opts.Template != "" {
   407  					// NOTE: we cannot read `defaultBranchRef` from the newly created repository as it will
   408  					// be null at this time. Instead, we assume that the main branch name of the new
   409  					// repository will be the same as that of the template repository.
   410  					checkoutBranch = templateRepoMainBranch
   411  				}
   412  				if err := localInit(opts.IO, remoteURL, path, checkoutBranch); err != nil {
   413  					return err
   414  				}
   415  				if isTTY {
   416  					fmt.Fprintf(stderr, "%s Initialized repository in \"%s\"\n", cs.SuccessIcon(), path)
   417  				}
   418  			} else if createLocalDirectory && (gitIgnoreTemplate != "" || repoLicenseTemplate != "") {
   419  				_, err := git.RunClone(remoteURL, []string{})
   420  				if err != nil {
   421  					return err
   422  				}
   423  			}
   424  		}
   425  
   426  		return nil
   427  	}
   428  	fmt.Fprintln(opts.IO.Out, "Discarding...")
   429  	return nil
   430  }
   431  
   432  func interactiveGitIgnore(client *http.Client, hostname string) (string, error) {
   433  
   434  	var addGitIgnore bool
   435  	var addGitIgnoreSurvey []*survey.Question
   436  
   437  	addGitIgnoreQuestion := &survey.Question{
   438  		Name: "addGitIgnore",
   439  		Prompt: &survey.Confirm{
   440  			Message: "Would you like to add a .gitignore?",
   441  			Default: false,
   442  		},
   443  	}
   444  
   445  	addGitIgnoreSurvey = append(addGitIgnoreSurvey, addGitIgnoreQuestion)
   446  	err := prompt.SurveyAsk(addGitIgnoreSurvey, &addGitIgnore)
   447  	if err != nil {
   448  		return "", err
   449  	}
   450  
   451  	var wantedIgnoreTemplate string
   452  
   453  	if addGitIgnore {
   454  		var gitIg []*survey.Question
   455  
   456  		gitIgnoretemplates, err := listGitIgnoreTemplates(client, hostname)
   457  		if err != nil {
   458  			return "", err
   459  		}
   460  		gitIgnoreQuestion := &survey.Question{
   461  			Name: "chooseGitIgnore",
   462  			Prompt: &survey.Select{
   463  				Message: "Choose a .gitignore template",
   464  				Options: gitIgnoretemplates,
   465  			},
   466  		}
   467  		gitIg = append(gitIg, gitIgnoreQuestion)
   468  		err = prompt.SurveyAsk(gitIg, &wantedIgnoreTemplate)
   469  		if err != nil {
   470  			return "", err
   471  		}
   472  	}
   473  
   474  	return wantedIgnoreTemplate, nil
   475  }
   476  
   477  func interactiveLicense(client *http.Client, hostname string) (string, error) {
   478  	var addLicense bool
   479  	var addLicenseSurvey []*survey.Question
   480  	var wantedLicense string
   481  
   482  	addLicenseQuestion := &survey.Question{
   483  		Name: "addLicense",
   484  		Prompt: &survey.Confirm{
   485  			Message: "Would you like to add a license?",
   486  			Default: false,
   487  		},
   488  	}
   489  
   490  	addLicenseSurvey = append(addLicenseSurvey, addLicenseQuestion)
   491  	err := prompt.SurveyAsk(addLicenseSurvey, &addLicense)
   492  	if err != nil {
   493  		return "", err
   494  	}
   495  
   496  	licenseKey := map[string]string{}
   497  
   498  	if addLicense {
   499  		licenseTemplates, err := listLicenseTemplates(client, hostname)
   500  		if err != nil {
   501  			return "", err
   502  		}
   503  		var licenseNames []string
   504  		for _, l := range licenseTemplates {
   505  			licenseNames = append(licenseNames, l.Name)
   506  			licenseKey[l.Name] = l.Key
   507  		}
   508  		var licenseQs []*survey.Question
   509  
   510  		licenseQuestion := &survey.Question{
   511  			Name: "chooseLicense",
   512  			Prompt: &survey.Select{
   513  				Message: "Choose a license",
   514  				Options: licenseNames,
   515  			},
   516  		}
   517  		licenseQs = append(licenseQs, licenseQuestion)
   518  		err = prompt.SurveyAsk(licenseQs, &wantedLicense)
   519  		if err != nil {
   520  			return "", err
   521  		}
   522  		return licenseKey[wantedLicense], nil
   523  	}
   524  	return "", nil
   525  }
   526  
   527  func localInit(io *iostreams.IOStreams, remoteURL, path, checkoutBranch string) error {
   528  	gitInit, err := git.GitCommand("init", path)
   529  	if err != nil {
   530  		return err
   531  	}
   532  	isTTY := io.IsStdoutTTY()
   533  	if isTTY {
   534  		gitInit.Stdout = io.Out
   535  	}
   536  	gitInit.Stderr = io.ErrOut
   537  	err = run.PrepareCmd(gitInit).Run()
   538  	if err != nil {
   539  		return err
   540  	}
   541  
   542  	gitRemoteAdd, err := git.GitCommand("-C", path, "remote", "add", "origin", remoteURL)
   543  	if err != nil {
   544  		return err
   545  	}
   546  	gitRemoteAdd.Stdout = io.Out
   547  	gitRemoteAdd.Stderr = io.ErrOut
   548  	err = run.PrepareCmd(gitRemoteAdd).Run()
   549  	if err != nil {
   550  		return err
   551  	}
   552  
   553  	if checkoutBranch == "" {
   554  		return nil
   555  	}
   556  
   557  	gitFetch, err := git.GitCommand("-C", path, "fetch", "origin", fmt.Sprintf("+refs/heads/%[1]s:refs/remotes/origin/%[1]s", checkoutBranch))
   558  	if err != nil {
   559  		return err
   560  	}
   561  	gitFetch.Stdout = io.Out
   562  	gitFetch.Stderr = io.ErrOut
   563  	err = run.PrepareCmd(gitFetch).Run()
   564  	if err != nil {
   565  		return err
   566  	}
   567  
   568  	gitCheckout, err := git.GitCommand("-C", path, "checkout", checkoutBranch)
   569  	if err != nil {
   570  		return err
   571  	}
   572  	gitCheckout.Stdout = io.Out
   573  	gitCheckout.Stderr = io.ErrOut
   574  	return run.PrepareCmd(gitCheckout).Run()
   575  }
   576  
   577  func interactiveRepoCreate(isDescEmpty bool, isVisibilityPassed bool, repoName string) (string, string, string, error) {
   578  	qs := []*survey.Question{}
   579  
   580  	repoNameQuestion := &survey.Question{
   581  		Name: "repoName",
   582  		Prompt: &survey.Input{
   583  			Message: "Repository name",
   584  			Default: repoName,
   585  		},
   586  	}
   587  	qs = append(qs, repoNameQuestion)
   588  
   589  	if isDescEmpty {
   590  		repoDescriptionQuestion := &survey.Question{
   591  			Name: "repoDescription",
   592  			Prompt: &survey.Input{
   593  				Message: "Repository description",
   594  			},
   595  		}
   596  
   597  		qs = append(qs, repoDescriptionQuestion)
   598  	}
   599  
   600  	if !isVisibilityPassed {
   601  		repoVisibilityQuestion := &survey.Question{
   602  			Name: "repoVisibility",
   603  			Prompt: &survey.Select{
   604  				Message: "Visibility",
   605  				Options: []string{"Public", "Private", "Internal"},
   606  			},
   607  		}
   608  		qs = append(qs, repoVisibilityQuestion)
   609  	}
   610  
   611  	answers := struct {
   612  		RepoName        string
   613  		RepoDescription string
   614  		RepoVisibility  string
   615  	}{}
   616  
   617  	err := prompt.SurveyAsk(qs, &answers)
   618  
   619  	if err != nil {
   620  		return "", "", "", err
   621  	}
   622  
   623  	return answers.RepoName, answers.RepoDescription, strings.ToUpper(answers.RepoVisibility), nil
   624  }
   625  
   626  func confirmSubmission(repoName string, repoOwner string, inLocalRepo bool) (bool, error) {
   627  	qs := []*survey.Question{}
   628  
   629  	promptString := ""
   630  	if inLocalRepo {
   631  		promptString = `This will add an "origin" git remote to your local repository. Continue?`
   632  	} else {
   633  		targetRepo := repoName
   634  		if repoOwner != "" {
   635  			targetRepo = fmt.Sprintf("%s/%s", repoOwner, repoName)
   636  		}
   637  		promptString = fmt.Sprintf(`This will create the "%s" repository on GitHub. Continue?`, targetRepo)
   638  	}
   639  
   640  	confirmSubmitQuestion := &survey.Question{
   641  		Name: "confirmSubmit",
   642  		Prompt: &survey.Confirm{
   643  			Message: promptString,
   644  			Default: true,
   645  		},
   646  	}
   647  	qs = append(qs, confirmSubmitQuestion)
   648  
   649  	answer := struct {
   650  		ConfirmSubmit bool
   651  	}{}
   652  
   653  	err := prompt.SurveyAsk(qs, &answer)
   654  	if err != nil {
   655  		return false, err
   656  	}
   657  
   658  	return answer.ConfirmSubmit, nil
   659  }
   660  
   661  func getVisibility() (string, error) {
   662  	qs := []*survey.Question{}
   663  
   664  	getVisibilityQuestion := &survey.Question{
   665  		Name: "repoVisibility",
   666  		Prompt: &survey.Select{
   667  			Message: "Visibility",
   668  			Options: []string{"Public", "Private", "Internal"},
   669  		},
   670  	}
   671  	qs = append(qs, getVisibilityQuestion)
   672  
   673  	answer := struct {
   674  		RepoVisibility string
   675  	}{}
   676  
   677  	err := prompt.SurveyAsk(qs, &answer)
   678  	if err != nil {
   679  		return "", err
   680  	}
   681  
   682  	return strings.ToUpper(answer.RepoVisibility), nil
   683  }