Merge pull request #10516 from iamazeem/8430-show-hostname-in-repo-creation-prompts

[gh repo create] Show host name in repo creation prompts
This commit is contained in:
Kynan Ware 2025-04-09 14:30:56 -06:00 committed by GitHub
commit 19633d3855
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 126 additions and 28 deletions

View file

@ -262,10 +262,15 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
func createRun(opts *CreateOptions) error {
if opts.Interactive {
cfg, err := opts.Config()
if err != nil {
return err
}
host, _ := cfg.Authentication().DefaultHost()
answer, err := opts.Prompter.Select("What would you like to do?", "", []string{
"Create a new repository on GitHub from scratch",
"Create a new repository on GitHub from a template repository",
"Push an existing local repository to GitHub",
fmt.Sprintf("Create a new repository on %s from scratch", host),
fmt.Sprintf("Create a new repository on %s from a template repository", host),
fmt.Sprintf("Push an existing local repository to %s", host),
})
if err != nil {
return err
@ -323,7 +328,9 @@ func createFromScratch(opts *CreateOptions) error {
if idx := strings.IndexRune(opts.Name, '/'); idx > 0 {
targetRepo = opts.Name[0:idx+1] + shared.NormalizeRepoName(opts.Name[idx+1:])
}
confirmed, err := opts.Prompter.Confirm(fmt.Sprintf(`This will create "%s" as a %s repository on GitHub. Continue?`, targetRepo, strings.ToLower(opts.Visibility)), true)
confirmed, err := opts.Prompter.Confirm(
fmt.Sprintf(`This will create "%s" as a %s repository on %s. Continue?`, targetRepo, strings.ToLower(opts.Visibility), host),
true)
if err != nil {
return err
} else if !confirmed {
@ -392,9 +399,10 @@ func createFromScratch(opts *CreateOptions) error {
isTTY := opts.IO.IsStdoutTTY()
if isTTY {
fmt.Fprintf(opts.IO.Out,
"%s Created repository %s on GitHub\n %s\n",
"%s Created repository %s on %s\n %s\n",
cs.SuccessIconWithColor(cs.Green),
ghrepo.FullName(repo),
host,
repo.URL)
} else {
fmt.Fprintln(opts.IO.Out, repo.URL)
@ -482,7 +490,9 @@ func createFromTemplate(opts *CreateOptions) error {
if idx := strings.IndexRune(opts.Name, '/'); idx > 0 {
targetRepo = opts.Name[0:idx+1] + shared.NormalizeRepoName(opts.Name[idx+1:])
}
confirmed, err := opts.Prompter.Confirm(fmt.Sprintf(`This will create "%s" as a %s repository on GitHub. Continue?`, targetRepo, strings.ToLower(opts.Visibility)), true)
confirmed, err := opts.Prompter.Confirm(
fmt.Sprintf(`This will create "%s" as a %s repository on %s. Continue?`, targetRepo, strings.ToLower(opts.Visibility), host),
true)
if err != nil {
return err
} else if !confirmed {
@ -496,9 +506,10 @@ func createFromTemplate(opts *CreateOptions) error {
cs := opts.IO.ColorScheme()
fmt.Fprintf(opts.IO.Out,
"%s Created repository %s on GitHub\n %s\n",
"%s Created repository %s on %s\n %s\n",
cs.SuccessIconWithColor(cs.Green),
ghrepo.FullName(repo),
host,
repo.URL)
opts.Clone, err = opts.Prompter.Confirm("Clone the new repository locally?", true)
@ -622,9 +633,10 @@ func createFromLocal(opts *CreateOptions) error {
if isTTY {
fmt.Fprintf(stdout,
"%s Created repository %s on GitHub\n %s\n",
"%s Created repository %s on %s\n %s\n",
cs.SuccessIconWithColor(cs.Green),
ghrepo.FullName(repo),
host,
repo.URL)
} else {
fmt.Fprintln(stdout, repo.URL)

View file

@ -10,6 +10,7 @@ import (
"github.com/cli/cli/v2/git"
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/internal/gh"
ghmock "github.com/cli/cli/v2/internal/gh/mock"
"github.com/cli/cli/v2/internal/prompter"
"github.com/cli/cli/v2/internal/run"
"github.com/cli/cli/v2/pkg/cmdutil"
@ -201,7 +202,7 @@ func Test_createRun(t *testing.T) {
name: "interactive create from scratch with gitignore and license",
opts: &CreateOptions{Interactive: true},
tty: true,
wantStdout: "✓ Created repository OWNER/REPO on GitHub\n https://github.com/OWNER/REPO\n",
wantStdout: "✓ Created repository OWNER/REPO on github.com\n https://github.com/OWNER/REPO\n",
promptStubs: func(p *prompter.PrompterMock) {
p.ConfirmFunc = func(message string, defaultValue bool) (bool, error) {
switch message {
@ -211,7 +212,7 @@ func Test_createRun(t *testing.T) {
return true, nil
case "Would you like to add a license?":
return true, nil
case `This will create "REPO" as a private repository on GitHub. Continue?`:
case `This will create "REPO" as a private repository on github.com. Continue?`:
return defaultValue, nil
case "Clone the new repository locally?":
return defaultValue, nil
@ -232,7 +233,7 @@ func Test_createRun(t *testing.T) {
p.SelectFunc = func(message, defaultValue string, options []string) (int, error) {
switch message {
case "What would you like to do?":
return prompter.IndexFor(options, "Create a new repository on GitHub from scratch")
return prompter.IndexFor(options, "Create a new repository on github.com from scratch")
case "Visibility":
return prompter.IndexFor(options, "Private")
case "Choose a license":
@ -267,7 +268,7 @@ func Test_createRun(t *testing.T) {
name: "interactive create from scratch but with prompted owner",
opts: &CreateOptions{Interactive: true},
tty: true,
wantStdout: "✓ Created repository org1/REPO on GitHub\n https://github.com/org1/REPO\n",
wantStdout: "✓ Created repository org1/REPO on github.com\n https://github.com/org1/REPO\n",
promptStubs: func(p *prompter.PrompterMock) {
p.ConfirmFunc = func(message string, defaultValue bool) (bool, error) {
switch message {
@ -277,7 +278,7 @@ func Test_createRun(t *testing.T) {
return false, nil
case "Would you like to add a license?":
return false, nil
case `This will create "org1/REPO" as a private repository on GitHub. Continue?`:
case `This will create "org1/REPO" as a private repository on github.com. Continue?`:
return true, nil
case "Clone the new repository locally?":
return false, nil
@ -300,7 +301,7 @@ func Test_createRun(t *testing.T) {
case "Repository owner":
return prompter.IndexFor(options, "org1")
case "What would you like to do?":
return prompter.IndexFor(options, "Create a new repository on GitHub from scratch")
return prompter.IndexFor(options, "Create a new repository on github.com from scratch")
case "Visibility":
return prompter.IndexFor(options, "Private")
default:
@ -345,7 +346,7 @@ func Test_createRun(t *testing.T) {
return false, nil
case "Would you like to add a license?":
return false, nil
case `This will create "REPO" as a private repository on GitHub. Continue?`:
case `This will create "REPO" as a private repository on github.com. Continue?`:
return false, nil
default:
return false, fmt.Errorf("unexpected confirm prompt: %s", message)
@ -364,7 +365,7 @@ func Test_createRun(t *testing.T) {
p.SelectFunc = func(message, defaultValue string, options []string) (int, error) {
switch message {
case "What would you like to do?":
return prompter.IndexFor(options, "Create a new repository on GitHub from scratch")
return prompter.IndexFor(options, "Create a new repository on github.com from scratch")
case "Visibility":
return prompter.IndexFor(options, "Private")
default:
@ -409,7 +410,7 @@ func Test_createRun(t *testing.T) {
p.SelectFunc = func(message, defaultValue string, options []string) (int, error) {
switch message {
case "What would you like to do?":
return prompter.IndexFor(options, "Push an existing local repository to GitHub")
return prompter.IndexFor(options, "Push an existing local repository to github.com")
case "Visibility":
return prompter.IndexFor(options, "Private")
default:
@ -441,7 +442,7 @@ func Test_createRun(t *testing.T) {
cs.Register(`git -C . rev-parse --git-dir`, 0, ".git")
cs.Register(`git -C . rev-parse HEAD`, 0, "commithash")
},
wantStdout: "✓ Created repository OWNER/REPO on GitHub\n https://github.com/OWNER/REPO\n",
wantStdout: "✓ Created repository OWNER/REPO on github.com\n https://github.com/OWNER/REPO\n",
},
{
name: "interactive with existing bare repository public and push",
@ -475,7 +476,7 @@ func Test_createRun(t *testing.T) {
p.SelectFunc = func(message, defaultValue string, options []string) (int, error) {
switch message {
case "What would you like to do?":
return prompter.IndexFor(options, "Push an existing local repository to GitHub")
return prompter.IndexFor(options, "Push an existing local repository to github.com")
case "Visibility":
return prompter.IndexFor(options, "Private")
default:
@ -509,7 +510,7 @@ func Test_createRun(t *testing.T) {
cs.Register(`git -C . remote add origin https://github.com/OWNER/REPO`, 0, "")
cs.Register(`git -C . push origin --mirror`, 0, "")
},
wantStdout: "✓ Created repository OWNER/REPO on GitHub\n https://github.com/OWNER/REPO\n✓ Added remote https://github.com/OWNER/REPO.git\n✓ Mirrored all refs to https://github.com/OWNER/REPO.git\n",
wantStdout: "✓ Created repository OWNER/REPO on github.com\n https://github.com/OWNER/REPO\n✓ Added remote https://github.com/OWNER/REPO.git\n✓ Mirrored all refs to https://github.com/OWNER/REPO.git\n",
},
{
name: "interactive with existing repository public add remote and push",
@ -543,7 +544,7 @@ func Test_createRun(t *testing.T) {
p.SelectFunc = func(message, defaultValue string, options []string) (int, error) {
switch message {
case "What would you like to do?":
return prompter.IndexFor(options, "Push an existing local repository to GitHub")
return prompter.IndexFor(options, "Push an existing local repository to github.com")
case "Visibility":
return prompter.IndexFor(options, "Private")
default:
@ -577,7 +578,7 @@ func Test_createRun(t *testing.T) {
cs.Register(`git -C . remote add origin https://github.com/OWNER/REPO`, 0, "")
cs.Register(`git -C . push --set-upstream origin HEAD`, 0, "")
},
wantStdout: "✓ Created repository OWNER/REPO on GitHub\n https://github.com/OWNER/REPO\n✓ Added remote https://github.com/OWNER/REPO.git\n✓ Pushed commits to https://github.com/OWNER/REPO.git\n",
wantStdout: "✓ Created repository OWNER/REPO on github.com\n https://github.com/OWNER/REPO\n✓ Added remote https://github.com/OWNER/REPO.git\n✓ Pushed commits to https://github.com/OWNER/REPO.git\n",
},
{
name: "interactive create from a template repository",
@ -586,7 +587,7 @@ func Test_createRun(t *testing.T) {
promptStubs: func(p *prompter.PrompterMock) {
p.ConfirmFunc = func(message string, defaultValue bool) (bool, error) {
switch message {
case `This will create "OWNER/REPO" as a private repository on GitHub. Continue?`:
case `This will create "OWNER/REPO" as a private repository on github.com. Continue?`:
return defaultValue, nil
case "Clone the new repository locally?":
return defaultValue, nil
@ -611,7 +612,7 @@ func Test_createRun(t *testing.T) {
case "Choose a template repository":
return prompter.IndexFor(options, "REPO")
case "What would you like to do?":
return prompter.IndexFor(options, "Create a new repository on GitHub from a template repository")
return prompter.IndexFor(options, "Create a new repository on github.com from a template repository")
case "Visibility":
return prompter.IndexFor(options, "Private")
default:
@ -654,7 +655,7 @@ func Test_createRun(t *testing.T) {
execStubs: func(cs *run.CommandStubber) {
cs.Register(`git clone --branch main https://github.com/OWNER/REPO`, 0, "")
},
wantStdout: "✓ Created repository OWNER/REPO on GitHub\n https://github.com/OWNER/REPO\n",
wantStdout: "✓ Created repository OWNER/REPO on github.com\n https://github.com/OWNER/REPO\n",
},
{
name: "interactive create from template repo but there are no template repos",
@ -680,7 +681,7 @@ func Test_createRun(t *testing.T) {
p.SelectFunc = func(message, defaultValue string, options []string) (int, error) {
switch message {
case "What would you like to do?":
return prompter.IndexFor(options, "Create a new repository on GitHub from a template repository")
return prompter.IndexFor(options, "Create a new repository on github.com from a template repository")
case "Visibility":
return prompter.IndexFor(options, "Private")
default:
@ -950,6 +951,88 @@ func Test_createRun(t *testing.T) {
},
wantStdout: "https://github.com/OWNER/REPO\n",
},
{
name: "interactive create from scratch with host override",
opts: &CreateOptions{
Interactive: true,
Config: func() (gh.Config, error) {
cfg := &ghmock.ConfigMock{
AuthenticationFunc: func() gh.AuthConfig {
authCfg := &config.AuthConfig{}
authCfg.SetHosts([]string{"example.com"})
authCfg.SetDefaultHost("example.com", "GH_HOST")
return authCfg
},
}
return cfg, nil
},
},
tty: true,
promptStubs: func(p *prompter.PrompterMock) {
p.ConfirmFunc = func(message string, defaultValue bool) (bool, error) {
switch message {
case "Would you like to add a README file?":
return false, nil
case "Would you like to add a .gitignore?":
return false, nil
case "Would you like to add a license?":
return false, nil
case `This will create "REPO" as a private repository on example.com. Continue?`:
return defaultValue, nil
case "Clone the new repository locally?":
return false, nil
default:
return false, fmt.Errorf("unexpected confirm prompt: %s", message)
}
}
p.InputFunc = func(message, defaultValue string) (string, error) {
switch message {
case "Repository name":
return "REPO", nil
case "Description":
return "my new repo", nil
default:
return "", fmt.Errorf("unexpected input prompt: %s", message)
}
}
p.SelectFunc = func(message, defaultValue string, options []string) (int, error) {
switch message {
case "What would you like to do?":
return prompter.IndexFor(options, "Create a new repository on example.com from scratch")
case "Visibility":
return prompter.IndexFor(options, "Private")
case "Choose a license":
return prompter.IndexFor(options, "GNU Lesser General Public License v3.0")
case "Choose a .gitignore template":
return prompter.IndexFor(options, "Go")
default:
return 0, fmt.Errorf("unexpected select prompt: %s", message)
}
}
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query UserCurrent\b`),
httpmock.StringResponse(`{"data":{"viewer":{"login":"someuser","organizations":{"nodes": []}}}}`))
reg.Register(
httpmock.GraphQL(`mutation RepositoryCreate\b`),
httpmock.StringResponse(`
{
"data": {
"createRepository": {
"repository": {
"id": "REPOID",
"name": "REPO",
"owner": {"login":"OWNER"},
"url": "https://example.com/OWNER/REPO"
}
}
}
}`),
)
},
wantStdout: "✓ Created repository OWNER/REPO on example.com\n https://example.com/OWNER/REPO\n",
},
}
for _, tt := range tests {
prompterMock := &prompter.PrompterMock{}
@ -965,8 +1048,11 @@ func Test_createRun(t *testing.T) {
tt.opts.HttpClient = func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
}
tt.opts.Config = func() (gh.Config, error) {
return config.NewBlankConfig(), nil
if tt.opts.Config == nil {
tt.opts.Config = func() (gh.Config, error) {
return config.NewBlankConfig(), nil
}
}
tt.opts.GitClient = &git.Client{