From cc3dd515a6dad6a965007bd95f7fcad0ffd27d6d Mon Sep 17 00:00:00 2001 From: Azeem Sajid Date: Fri, 28 Feb 2025 12:25:49 +0500 Subject: [PATCH 1/2] Show host name in repo creation prompts --- pkg/cmd/repo/create/create.go | 28 ++++++++++++++++------- pkg/cmd/repo/create/create_test.go | 36 +++++++++++++++--------------- 2 files changed, 38 insertions(+), 26 deletions(-) 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) diff --git a/pkg/cmd/repo/create/create_test.go b/pkg/cmd/repo/create/create_test.go index c33cfdad6..760789786 100644 --- a/pkg/cmd/repo/create/create_test.go +++ b/pkg/cmd/repo/create/create_test.go @@ -201,7 +201,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 +211,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 +232,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 +267,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 +277,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 +300,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 +345,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 +364,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 +409,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 +441,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 +475,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 +509,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 +543,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 +577,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 +586,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 +611,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 +654,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 +680,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: From a1136bf5cd51d0bd8fd99ca639f1096e2ee9af2e Mon Sep 17 00:00:00 2001 From: Azeem Sajid Date: Sun, 9 Mar 2025 12:19:16 +0500 Subject: [PATCH 2/2] Add initial test --- pkg/cmd/repo/create/create_test.go | 90 +++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/pkg/cmd/repo/create/create_test.go b/pkg/cmd/repo/create/create_test.go index 760789786..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" @@ -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{