The problem was that opts.confirmSubmit was mutated before reaching doSetup. This commit creates a copy of the initial confirmSubmit value. So the doSetup receives the initial data passed from the command, not the mutated one.
365 lines
8.9 KiB
Go
365 lines
8.9 KiB
Go
package create
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"path"
|
|
"strings"
|
|
|
|
"github.com/AlecAivazis/survey/v2"
|
|
"github.com/MakeNowJust/heredoc"
|
|
"github.com/cli/cli/git"
|
|
"github.com/cli/cli/internal/config"
|
|
"github.com/cli/cli/internal/ghrepo"
|
|
"github.com/cli/cli/internal/run"
|
|
"github.com/cli/cli/pkg/cmdutil"
|
|
"github.com/cli/cli/pkg/iostreams"
|
|
"github.com/cli/cli/pkg/prompt"
|
|
"github.com/cli/cli/utils"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
type CreateOptions struct {
|
|
HttpClient func() (*http.Client, error)
|
|
Config func() (config.Config, error)
|
|
IO *iostreams.IOStreams
|
|
|
|
Name string
|
|
Description string
|
|
Homepage string
|
|
Team string
|
|
EnableIssues bool
|
|
EnableWiki bool
|
|
Public bool
|
|
Private bool
|
|
Internal bool
|
|
ConfirmSubmit bool
|
|
}
|
|
|
|
func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Command {
|
|
opts := &CreateOptions{
|
|
IO: f.IOStreams,
|
|
HttpClient: f.HttpClient,
|
|
Config: f.Config,
|
|
}
|
|
|
|
cmd := &cobra.Command{
|
|
Use: "create [<name>]",
|
|
Short: "Create a new repository",
|
|
Long: `Create a new GitHub repository.`,
|
|
Args: cobra.MaximumNArgs(1),
|
|
Example: heredoc.Doc(`
|
|
# create a repository under your account using the current directory name
|
|
$ gh repo create
|
|
|
|
# create a repository with a specific name
|
|
$ gh repo create my-project
|
|
|
|
# create a repository in an organization
|
|
$ gh repo create cli/my-project
|
|
`),
|
|
Annotations: map[string]string{
|
|
"help:arguments": heredoc.Doc(
|
|
`A repository can be supplied as an argument in any of the following formats:
|
|
- <OWNER/REPO>
|
|
- by URL, e.g. "https://github.com/OWNER/REPO"`),
|
|
},
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if len(args) > 0 {
|
|
opts.Name = args[0]
|
|
}
|
|
|
|
if runF != nil {
|
|
return runF(opts)
|
|
}
|
|
|
|
return createRun(opts)
|
|
},
|
|
}
|
|
|
|
cmd.Flags().StringVarP(&opts.Description, "description", "d", "", "Description of repository")
|
|
cmd.Flags().StringVarP(&opts.Homepage, "homepage", "h", "", "Repository home page URL")
|
|
cmd.Flags().StringVarP(&opts.Team, "team", "t", "", "The name of the organization team to be granted access")
|
|
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().BoolVar(&opts.Public, "public", false, "Make the new repository public")
|
|
cmd.Flags().BoolVar(&opts.Private, "private", false, "Make the new repository private")
|
|
cmd.Flags().BoolVar(&opts.Internal, "internal", false, "Make the new repository internal")
|
|
cmd.Flags().BoolVarP(&opts.ConfirmSubmit, "confirm", "y", false, "Confirm the submission directly")
|
|
|
|
return cmd
|
|
}
|
|
|
|
func createRun(opts *CreateOptions) error {
|
|
projectDir, projectDirErr := git.ToplevelDir()
|
|
|
|
var repoToCreate ghrepo.Interface
|
|
|
|
isNameAnArg := false
|
|
isDescEmpty := opts.Description == ""
|
|
isVisibilityPassed := false
|
|
|
|
if opts.Name != "" {
|
|
isNameAnArg = true
|
|
if strings.Contains(opts.Name, "/") {
|
|
var err error
|
|
repoToCreate, err = ghrepo.FromFullName(opts.Name)
|
|
if err != nil {
|
|
return fmt.Errorf("argument error: %w", err)
|
|
}
|
|
} else {
|
|
repoToCreate = ghrepo.New("", opts.Name)
|
|
}
|
|
} else {
|
|
if projectDirErr != nil {
|
|
return projectDirErr
|
|
}
|
|
repoToCreate = ghrepo.New("", path.Base(projectDir))
|
|
}
|
|
|
|
enabledFlagCount := 0
|
|
visibility := ""
|
|
if opts.Public {
|
|
enabledFlagCount++
|
|
visibility = "PUBLIC"
|
|
}
|
|
if opts.Private {
|
|
enabledFlagCount++
|
|
visibility = "PRIVATE"
|
|
}
|
|
if opts.Internal {
|
|
enabledFlagCount++
|
|
visibility = "INTERNAL"
|
|
}
|
|
|
|
if enabledFlagCount > 1 {
|
|
return fmt.Errorf("expected exactly one of --public, --private, or --internal to be true")
|
|
} else if enabledFlagCount == 1 {
|
|
isVisibilityPassed = true
|
|
}
|
|
|
|
// Trigger interactive prompt if name is not passed
|
|
if !isNameAnArg {
|
|
newName, newDesc, newVisibility, err := interactiveRepoCreate(isDescEmpty, isVisibilityPassed, repoToCreate.RepoName())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if newName != "" {
|
|
opts.Name = newName
|
|
}
|
|
if newDesc != "" {
|
|
opts.Description = newDesc
|
|
}
|
|
if newVisibility != "" {
|
|
visibility = newVisibility
|
|
}
|
|
} else {
|
|
// Go for a prompt only if visibility isn't passed
|
|
if !isVisibilityPassed {
|
|
newVisibility, err := getVisibility()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
visibility = newVisibility
|
|
}
|
|
}
|
|
|
|
input := repoCreateInput{
|
|
Name: repoToCreate.RepoName(),
|
|
Visibility: visibility,
|
|
OwnerID: repoToCreate.RepoOwner(),
|
|
TeamID: opts.Team,
|
|
Description: opts.Description,
|
|
HomepageURL: opts.Homepage,
|
|
HasIssuesEnabled: opts.EnableIssues,
|
|
HasWikiEnabled: opts.EnableWiki,
|
|
}
|
|
|
|
httpClient, err := opts.HttpClient()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
createLocalDirectory := opts.ConfirmSubmit
|
|
if !opts.ConfirmSubmit {
|
|
opts.ConfirmSubmit, err = confirmSubmission(input.Name, input.OwnerID, &opts.ConfirmSubmit)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if opts.ConfirmSubmit {
|
|
repo, err := repoCreate(httpClient, repoToCreate.RepoHost(), input)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
stderr := opts.IO.ErrOut
|
|
stdout := opts.IO.Out
|
|
greenCheck := utils.Green("✓")
|
|
isTTY := opts.IO.IsStdoutTTY()
|
|
|
|
if isTTY {
|
|
fmt.Fprintf(stderr, "%s Created repository %s on GitHub\n", greenCheck, ghrepo.FullName(repo))
|
|
} else {
|
|
fmt.Fprintln(stdout, repo.URL)
|
|
}
|
|
|
|
// TODO This is overly wordy and I'd like to streamline this.
|
|
cfg, err := opts.Config()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
protocol, err := cfg.Get(repo.RepoHost(), "git_protocol")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
remoteURL := ghrepo.FormatRemoteURL(repo, protocol)
|
|
|
|
if projectDirErr == nil {
|
|
_, err = git.AddRemote("origin", remoteURL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if isTTY {
|
|
fmt.Fprintf(stderr, "%s Added remote %s\n", greenCheck, remoteURL)
|
|
}
|
|
} else if isTTY {
|
|
doSetup := createLocalDirectory
|
|
if !doSetup {
|
|
err := prompt.Confirm(fmt.Sprintf("Create a local project directory for %s?", ghrepo.FullName(repo)), &doSetup)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if doSetup {
|
|
path := repo.Name
|
|
|
|
gitInit := git.GitCommand("init", path)
|
|
gitInit.Stdout = stdout
|
|
gitInit.Stderr = stderr
|
|
err = run.PrepareCmd(gitInit).Run()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
gitRemoteAdd := git.GitCommand("-C", path, "remote", "add", "origin", remoteURL)
|
|
gitRemoteAdd.Stdout = stdout
|
|
gitRemoteAdd.Stderr = stderr
|
|
err = run.PrepareCmd(gitRemoteAdd).Run()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Fprintf(stderr, "%s Initialized repository in './%s/'\n", utils.GreenCheck(), path)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
fmt.Fprintln(opts.IO.Out, "Discarding...")
|
|
return nil
|
|
}
|
|
|
|
func interactiveRepoCreate(isDescEmpty bool, isVisibilityPassed bool, repoName string) (string, string, string, error) {
|
|
qs := []*survey.Question{}
|
|
|
|
repoOwnerQuestion := &survey.Question{
|
|
Name: "repoOwner",
|
|
Prompt: &survey.Input{
|
|
Message: "Repository name",
|
|
Default: repoName,
|
|
},
|
|
}
|
|
qs = append(qs, repoOwnerQuestion)
|
|
|
|
if isDescEmpty {
|
|
repoDescriptionQuestion := &survey.Question{
|
|
Name: "repoDescription",
|
|
Prompt: &survey.Input{
|
|
Message: "Repository description",
|
|
},
|
|
}
|
|
|
|
qs = append(qs, repoDescriptionQuestion)
|
|
}
|
|
|
|
if !isVisibilityPassed {
|
|
repoVisibilityQuestion := &survey.Question{
|
|
Name: "repoVisibility",
|
|
Prompt: &survey.Select{
|
|
Message: "Visibility",
|
|
Options: []string{"Public", "Private", "Internal"},
|
|
},
|
|
}
|
|
qs = append(qs, repoVisibilityQuestion)
|
|
}
|
|
|
|
answers := struct {
|
|
RepoOwner string
|
|
RepoDescription string
|
|
RepoVisibility string
|
|
}{}
|
|
|
|
err := prompt.SurveyAsk(qs, &answers)
|
|
|
|
if err != nil {
|
|
return "", "", "", err
|
|
}
|
|
|
|
return answers.RepoOwner, answers.RepoDescription, strings.ToUpper(answers.RepoVisibility), nil
|
|
|
|
}
|
|
|
|
func confirmSubmission(repoName string, repoOwner string, isConfirmFlagPassed *bool) (bool, error) {
|
|
qs := []*survey.Question{}
|
|
|
|
promptString := ""
|
|
if repoOwner != "" {
|
|
promptString = fmt.Sprintf("This will create '%s/%s' in your current directory. Continue? ", repoOwner, repoName)
|
|
} else {
|
|
promptString = fmt.Sprintf("This will create '%s' in your current directory. Continue? ", repoName)
|
|
}
|
|
|
|
confirmSubmitQuestion := &survey.Question{
|
|
Name: "confirmSubmit",
|
|
Prompt: &survey.Confirm{
|
|
Message: promptString,
|
|
Default: true,
|
|
},
|
|
}
|
|
qs = append(qs, confirmSubmitQuestion)
|
|
|
|
answer := struct {
|
|
ConfirmSubmit bool
|
|
}{}
|
|
|
|
err := prompt.SurveyAsk(qs, &answer)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return answer.ConfirmSubmit, nil
|
|
}
|
|
|
|
func getVisibility() (string, error) {
|
|
qs := []*survey.Question{}
|
|
|
|
getVisibilityQuestion := &survey.Question{
|
|
Name: "repoVisibility",
|
|
Prompt: &survey.Select{
|
|
Message: "Visibility",
|
|
Options: []string{"Public", "Private", "Internal"},
|
|
},
|
|
}
|
|
qs = append(qs, getVisibilityQuestion)
|
|
|
|
answer := struct {
|
|
RepoVisibility string
|
|
}{}
|
|
|
|
err := prompt.SurveyAsk(qs, &answer)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return strings.ToUpper(answer.RepoVisibility), nil
|
|
}
|