Repository: cli/cli (go)

Issue:
Show host name in repo creation prompts
### Describe the feature or problem you’d like to solve

I'm logged into multiple accounts across multiple hosts. When I want to create a repo, I want to make sure that I'm doing it in the right place. Right now, the `gh repo create` prompts all refer to performing the task "on GitHub". This isn't necessarily _wrong_, but it doesn't tell me which one.

### Proposed solution

I'd like to propose that we replace "on GitHub" to "on <host>" to be clear as to where the action is being performed.


Pull request:
[gh repo create] Show host name in repo creation prompts
Fixes #8430.

### Tests

```shell
# With default github.com
$ bin/gh repo create
? What would you like to do?  [Use arrows to move, type to filter]
> Create a new repository on github.com from scratch
  Create a new repository on github.com from a template repository
  Push an existing local repository to github.com


# With GH_HOST=ghe.io
$ GH_HOST=ghe.io bin/gh repo create
? What would you like to do?  [Use arrows to move, type to filter]
> Create a new repository on ghe.io from scratch
  Create a new repository on ghe.io from a template repository
  Push an existing local repository to ghe.io
```

Patch:
diff --git a/pkg/cmd/repo/create/create.go b/pkg/cmd/repo/create/create.go
index cd7c56ea8..be0be2ddd 100644
--- a/pkg/cmd/repo/create/create.go
+++ b/pkg/cmd/repo/create/create.go
@@ -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)


Test patch:
diff --git a/pkg/cmd/repo/create/create_test.go b/pkg/cmd/repo/create/create_test.go
index c33cfdad6..5f1f17e60 100644
--- a/pkg/cmd/repo/create/create_test.go
+++ b/pkg/cmd/repo/create/create_test.go
@@ -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{