Add the option to generate a README for the created repository (#6264)

This commit is contained in:
Mislav Marohnić 2022-09-21 16:08:26 +02:00 committed by GitHub
parent 354cfbd1b1
commit 113acf9245
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 262 additions and 327 deletions

View file

@ -8,7 +8,6 @@ import (
"path/filepath"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/git"
@ -18,14 +17,20 @@ import (
"github.com/cli/cli/v2/pkg/cmd/repo/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/pkg/prompt"
"github.com/spf13/cobra"
)
type iprompter interface {
Input(string, string) (string, error)
Select(string, string, []string) (int, error)
Confirm(string, bool) (bool, error)
}
type CreateOptions struct {
HttpClient func() (*http.Client, error)
Config func() (config.Config, error)
IO *iostreams.IOStreams
Prompter iprompter
Name string
Description string
@ -46,6 +51,7 @@ type CreateOptions struct {
DisableWiki bool
Interactive bool
IncludeAllBranches bool
AddReadme bool
}
func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Command {
@ -53,6 +59,7 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
IO: f.IOStreams,
HttpClient: f.HttpClient,
Config: f.Config,
Prompter: f.Prompter,
}
var enableIssues bool
@ -135,6 +142,10 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
return cmdutil.FlagErrorf(".gitignore and license templates are not added when template is provided")
}
if opts.Template != "" && opts.AddReadme {
return cmdutil.FlagErrorf("the `--add-readme` option is not supported with `--template`")
}
if cmd.Flags().Changed("enable-issues") {
opts.DisableIssues = !enableIssues
}
@ -172,6 +183,7 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
cmd.Flags().BoolVar(&opts.DisableIssues, "disable-issues", false, "Disable issues in the new repository")
cmd.Flags().BoolVar(&opts.DisableWiki, "disable-wiki", false, "Disable wiki in the new repository")
cmd.Flags().BoolVar(&opts.IncludeAllBranches, "include-all-branches", false, "Include all branches from template repository")
cmd.Flags().BoolVar(&opts.AddReadme, "add-readme", false, "Add a README file to the new repository")
// deprecated flags
cmd.Flags().BoolP("confirm", "y", false, "Skip the confirmation prompt")
@ -227,19 +239,14 @@ func createRun(opts *CreateOptions) error {
fromScratch := opts.Source == ""
if opts.Interactive {
var selectedMode string
modeOptions := []string{
selected, err := opts.Prompter.Select("What would you like to do?", "", []string{
"Create a new repository on GitHub from scratch",
"Push an existing local repository to GitHub",
}
//nolint:staticcheck // SA1019: prompt.SurveyAskOne is deprecated: use Prompter
if err := prompt.SurveyAskOne(&survey.Select{
Message: "What would you like to do?",
Options: modeOptions,
}, &selectedMode); err != nil {
})
if err != nil {
return err
}
fromScratch = selectedMode == modeOptions[0]
fromScratch = selected == 0
}
if fromScratch {
@ -264,21 +271,32 @@ func createFromScratch(opts *CreateOptions) error {
host, _ := cfg.DefaultHost()
if opts.Interactive {
opts.Name, opts.Description, opts.Visibility, err = interactiveRepoInfo("")
opts.Name, opts.Description, opts.Visibility, err = interactiveRepoInfo(opts.Prompter, "")
if err != nil {
return err
}
opts.GitIgnoreTemplate, err = interactiveGitIgnore(httpClient, host)
opts.AddReadme, err = opts.Prompter.Confirm("Would you like to add a README file?", false)
if err != nil {
return err
}
opts.LicenseTemplate, err = interactiveLicense(httpClient, host)
opts.GitIgnoreTemplate, err = interactiveGitIgnore(httpClient, host, opts.Prompter)
if err != nil {
return err
}
opts.LicenseTemplate, err = interactiveLicense(httpClient, host, opts.Prompter)
if err != nil {
return err
}
if err := confirmSubmission(opts.Name, opts.Visibility); err != nil {
targetRepo := shared.NormalizeRepoName(opts.Name)
if idx := strings.IndexRune(targetRepo, '/'); idx > 0 {
targetRepo = targetRepo[0:idx+1] + shared.NormalizeRepoName(targetRepo[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)
if err != nil {
return err
} else if !confirmed {
return cmdutil.CancelError
}
}
@ -304,6 +322,7 @@ func createFromScratch(opts *CreateOptions) error {
GitIgnoreTemplate: opts.GitIgnoreTemplate,
LicenseTemplate: opts.LicenseTemplate,
IncludeAllBranches: opts.IncludeAllBranches,
InitReadme: opts.AddReadme,
}
var templateRepoMainBranch string
@ -350,12 +369,8 @@ func createFromScratch(opts *CreateOptions) error {
}
if opts.Interactive {
cloneQuestion := &survey.Confirm{
Message: "Clone the new repository locally?",
Default: true,
}
//nolint:staticcheck // SA1019: prompt.SurveyAskOne is deprecated: use Prompter
err = prompt.SurveyAskOne(cloneQuestion, &opts.Clone)
var err error
opts.Clone, err = opts.Prompter.Confirm("Clone the new repository locally?", true)
if err != nil {
return err
}
@ -405,7 +420,8 @@ func createFromLocal(opts *CreateOptions) error {
host, _ := cfg.DefaultHost()
if opts.Interactive {
opts.Source, err = interactiveSource()
var err error
opts.Source, err = opts.Prompter.Input("Path to local repository", ".")
if err != nil {
return err
}
@ -448,7 +464,7 @@ func createFromLocal(opts *CreateOptions) error {
}
if opts.Interactive {
opts.Name, opts.Description, opts.Visibility, err = interactiveRepoInfo(filepath.Base(absPath))
opts.Name, opts.Description, opts.Visibility, err = interactiveRepoInfo(opts.Prompter, filepath.Base(absPath))
if err != nil {
return err
}
@ -504,27 +520,15 @@ func createFromLocal(opts *CreateOptions) error {
remoteURL := ghrepo.FormatRemoteURL(repo, protocol)
if opts.Interactive {
var addRemote bool
remoteQuesiton := &survey.Confirm{
Message: `Add a remote?`,
Default: true,
}
//nolint:staticcheck // SA1019: prompt.SurveyAskOne is deprecated: use Prompter
err = prompt.SurveyAskOne(remoteQuesiton, &addRemote)
addRemote, err := opts.Prompter.Confirm("Add a remote?", true)
if err != nil {
return err
}
if !addRemote {
return nil
}
pushQuestion := &survey.Input{
Message: "What should the new remote be called?",
Default: "origin",
}
//nolint:staticcheck // SA1019: prompt.SurveyAskOne is deprecated: use Prompter
err = prompt.SurveyAskOne(pushQuestion, &baseRemote)
baseRemote, err = opts.Prompter.Input("What should the new remote be called?", "origin")
if err != nil {
return err
}
@ -536,12 +540,8 @@ func createFromLocal(opts *CreateOptions) error {
// don't prompt for push if there are no commits
if opts.Interactive && committed {
pushQuestion := &survey.Confirm{
Message: fmt.Sprintf(`Would you like to push commits from the current branch to %q?`, baseRemote),
Default: true,
}
//nolint:staticcheck // SA1019: prompt.SurveyAskOne is deprecated: use Prompter
err = prompt.SurveyAskOne(pushQuestion, &opts.Push)
var err error
opts.Push, err = opts.Prompter.Confirm(fmt.Sprintf("Would you like to push commits from the current branch to %q?", baseRemote), true)
if err != nil {
return err
}
@ -676,177 +676,65 @@ func localInit(io *iostreams.IOStreams, remoteURL, path, checkoutBranch string)
return run.PrepareCmd(gitCheckout).Run()
}
func interactiveGitIgnore(client *http.Client, hostname string) (string, error) {
var addGitIgnore bool
var addGitIgnoreSurvey []*survey.Question
addGitIgnoreQuestion := &survey.Question{
Name: "addGitIgnore",
Prompt: &survey.Confirm{
Message: "Would you like to add a .gitignore?",
Default: false,
},
func interactiveGitIgnore(client *http.Client, hostname string, prompter iprompter) (string, error) {
confirmed, err := prompter.Confirm("Would you like to add a .gitignore?", false)
if err != nil {
return "", err
} else if !confirmed {
return "", nil
}
addGitIgnoreSurvey = append(addGitIgnoreSurvey, addGitIgnoreQuestion)
//nolint:staticcheck // SA1019: prompt.SurveyAsk is deprecated: use Prompter
err := prompt.SurveyAsk(addGitIgnoreSurvey, &addGitIgnore)
templates, err := listGitIgnoreTemplates(client, hostname)
if err != nil {
return "", err
}
var wantedIgnoreTemplate string
if addGitIgnore {
var gitIg []*survey.Question
gitIgnoretemplates, err := listGitIgnoreTemplates(client, hostname)
if err != nil {
return "", err
}
gitIgnoreQuestion := &survey.Question{
Name: "chooseGitIgnore",
Prompt: &survey.Select{
Message: "Choose a .gitignore template",
Options: gitIgnoretemplates,
},
}
gitIg = append(gitIg, gitIgnoreQuestion)
//nolint:staticcheck // SA1019: prompt.SurveyAsk is deprecated: use Prompter
err = prompt.SurveyAsk(gitIg, &wantedIgnoreTemplate)
if err != nil {
return "", err
}
selected, err := prompter.Select("Choose a .gitignore template", "", templates)
if err != nil {
return "", err
}
return wantedIgnoreTemplate, nil
return templates[selected], nil
}
func interactiveLicense(client *http.Client, hostname string) (string, error) {
var addLicense bool
var addLicenseSurvey []*survey.Question
var wantedLicense string
addLicenseQuestion := &survey.Question{
Name: "addLicense",
Prompt: &survey.Confirm{
Message: "Would you like to add a license?",
Default: false,
},
func interactiveLicense(client *http.Client, hostname string, prompter iprompter) (string, error) {
confirmed, err := prompter.Confirm("Would you like to add a license?", false)
if err != nil {
return "", err
} else if !confirmed {
return "", nil
}
addLicenseSurvey = append(addLicenseSurvey, addLicenseQuestion)
//nolint:staticcheck // SA1019: prompt.SurveyAsk is deprecated: use Prompter
err := prompt.SurveyAsk(addLicenseSurvey, &addLicense)
licenses, err := listLicenseTemplates(client, hostname)
if err != nil {
return "", err
}
licenseKey := map[string]string{}
if addLicense {
licenseTemplates, err := listLicenseTemplates(client, hostname)
if err != nil {
return "", err
}
var licenseNames []string
for _, l := range licenseTemplates {
licenseNames = append(licenseNames, l.Name)
licenseKey[l.Name] = l.Key
}
var licenseQs []*survey.Question
licenseQuestion := &survey.Question{
Name: "chooseLicense",
Prompt: &survey.Select{
Message: "Choose a license",
Options: licenseNames,
},
}
licenseQs = append(licenseQs, licenseQuestion)
//nolint:staticcheck // SA1019: prompt.SurveyAsk is deprecated: use Prompter
err = prompt.SurveyAsk(licenseQs, &wantedLicense)
if err != nil {
return "", err
}
return licenseKey[wantedLicense], nil
licenseNames := make([]string, 0, len(licenses))
for _, license := range licenses {
licenseNames = append(licenseNames, license.Name)
}
return "", nil
selected, err := prompter.Select("Choose a license", "", licenseNames)
if err != nil {
return "", err
}
return licenses[selected].Key, nil
}
// name, description, and visibility
func interactiveRepoInfo(defaultName string) (string, string, string, error) {
qs := []*survey.Question{
{
Name: "repoName",
Prompt: &survey.Input{
Message: "Repository name",
Default: defaultName,
},
},
{
Name: "repoDescription",
Prompt: &survey.Input{Message: "Description"},
},
{
Name: "repoVisibility",
Prompt: &survey.Select{
Message: "Visibility",
Options: []string{"Public", "Private", "Internal"},
},
}}
answer := struct {
RepoName string
RepoDescription string
RepoVisibility string
}{}
//nolint:staticcheck // SA1019: prompt.SurveyAsk is deprecated: use Prompter
err := prompt.SurveyAsk(qs, &answer)
func interactiveRepoInfo(prompter iprompter, defaultName string) (string, string, string, error) {
name, err := prompter.Input("Repository name", defaultName)
if err != nil {
return "", "", "", err
}
return answer.RepoName, answer.RepoDescription, strings.ToUpper(answer.RepoVisibility), nil
}
func interactiveSource() (string, error) {
var sourcePath string
sourcePrompt := &survey.Input{
Message: "Path to local repository",
Default: "."}
//nolint:staticcheck // SA1019: prompt.SurveyAskOne is deprecated: use Prompter
err := prompt.SurveyAskOne(sourcePrompt, &sourcePath)
description, err := prompter.Input("Description", defaultName)
if err != nil {
return "", err
return "", "", "", err
}
return sourcePath, nil
}
func confirmSubmission(repoWithOwner, visibility string) error {
targetRepo := shared.NormalizeRepoName(repoWithOwner)
if idx := strings.IndexRune(repoWithOwner, '/'); idx > 0 {
targetRepo = repoWithOwner[0:idx+1] + shared.NormalizeRepoName(repoWithOwner[idx+1:])
}
var answer struct {
ConfirmSubmit bool
}
//nolint:staticcheck // SA1019: prompt.SurveyAsk is deprecated: use Prompter
err := prompt.SurveyAsk([]*survey.Question{{
Name: "confirmSubmit",
Prompt: &survey.Confirm{
Message: fmt.Sprintf(`This will create "%s" as a %s repository on GitHub. Continue?`, targetRepo, strings.ToLower(visibility)),
Default: true,
},
}}, &answer)
visibilityOptions := []string{"Public", "Private", "Internal"}
selected, err := prompter.Select("Visibility", "Public", visibilityOptions)
if err != nil {
return err
return "", "", "", err
}
if !answer.ConfirmSubmit {
return cmdutil.CancelError
}
return nil
return name, description, strings.ToUpper(visibilityOptions[selected]), nil
}

View file

@ -2,15 +2,16 @@ package create
import (
"bytes"
"fmt"
"net/http"
"testing"
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/internal/prompter"
"github.com/cli/cli/v2/internal/run"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/httpmock"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/pkg/prompt"
"github.com/google/shlex"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -171,47 +172,62 @@ func TestNewCmdCreate(t *testing.T) {
func Test_createRun(t *testing.T) {
tests := []struct {
name string
tty bool
opts *CreateOptions
httpStubs func(*httpmock.Registry)
askStubs func(*prompt.AskStubber)
execStubs func(*run.CommandStubber)
wantStdout string
wantErr bool
errMsg string
name string
tty bool
opts *CreateOptions
httpStubs func(*httpmock.Registry)
promptStubs func(*prompter.PrompterMock)
execStubs func(*run.CommandStubber)
wantStdout string
wantErr bool
errMsg string
}{
{
name: "interactive create from scratch with gitignore and license",
opts: &CreateOptions{Interactive: true},
tty: true,
wantStdout: "✓ Created repository OWNER/REPO on GitHub\n",
askStubs: func(as *prompt.AskStubber) {
//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
as.StubOne("Create a new repository on GitHub from scratch")
//nolint:staticcheck // SA1019: as.Stub is deprecated: use StubPrompt
as.Stub([]*prompt.QuestionStub{
{Name: "repoName", Value: "REPO"},
{Name: "repoDescription", Value: "my new repo"},
{Name: "repoVisibility", Value: "Private"},
})
//nolint:staticcheck // SA1019: as.Stub is deprecated: use StubPrompt
as.Stub([]*prompt.QuestionStub{
{Name: "addGitIgnore", Value: true}})
//nolint:staticcheck // SA1019: as.Stub is deprecated: use StubPrompt
as.Stub([]*prompt.QuestionStub{
{Name: "chooseGitIgnore", Value: "Go"}})
//nolint:staticcheck // SA1019: as.Stub is deprecated: use StubPrompt
as.Stub([]*prompt.QuestionStub{
{Name: "addLicense", Value: true}})
//nolint:staticcheck // SA1019: as.Stub is deprecated: use StubPrompt
as.Stub([]*prompt.QuestionStub{
{Name: "chooseLicense", Value: "GNU Lesser General Public License v3.0"}})
//nolint:staticcheck // SA1019: as.Stub is deprecated: use StubPrompt
as.Stub([]*prompt.QuestionStub{
{Name: "confirmSubmit", Value: true}})
//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
as.StubOne(true) //clone locally?
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 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?`:
return defaultValue, nil
case "Clone the new repository locally?":
return defaultValue, 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 GitHub 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(
@ -233,24 +249,41 @@ func Test_createRun(t *testing.T) {
name: "interactive create from scratch but cancel before submit",
opts: &CreateOptions{Interactive: true},
tty: true,
askStubs: func(as *prompt.AskStubber) {
//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
as.StubOne("Create a new repository on GitHub from scratch")
//nolint:staticcheck // SA1019: as.Stub is deprecated: use StubPrompt
as.Stub([]*prompt.QuestionStub{
{Name: "repoName", Value: "REPO"},
{Name: "repoDescription", Value: "my new repo"},
{Name: "repoVisibility", Value: "Private"},
})
//nolint:staticcheck // SA1019: as.Stub is deprecated: use StubPrompt
as.Stub([]*prompt.QuestionStub{
{Name: "addGitIgnore", Value: false}})
//nolint:staticcheck // SA1019: as.Stub is deprecated: use StubPrompt
as.Stub([]*prompt.QuestionStub{
{Name: "addLicense", Value: false}})
//nolint:staticcheck // SA1019: as.Stub is deprecated: use StubPrompt
as.Stub([]*prompt.QuestionStub{
{Name: "confirmSubmit", Value: false}})
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 GitHub. Continue?`:
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 GitHub from scratch")
case "Visibility":
return prompter.IndexFor(options, "Private")
default:
return 0, fmt.Errorf("unexpected select prompt: %s", message)
}
}
},
wantStdout: "",
wantErr: true,
@ -260,19 +293,37 @@ func Test_createRun(t *testing.T) {
name: "interactive with existing repository public",
opts: &CreateOptions{Interactive: true},
tty: true,
askStubs: func(as *prompt.AskStubber) {
//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
as.StubOne("Push an existing local repository to GitHub")
//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
as.StubOne(".")
//nolint:staticcheck // SA1019: as.Stub is deprecated: use StubPrompt
as.Stub([]*prompt.QuestionStub{
{Name: "repoName", Value: "REPO"},
{Name: "repoDescription", Value: "my new repo"},
{Name: "repoVisibility", Value: "Private"},
})
//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
as.StubOne(false)
promptStubs: func(p *prompter.PrompterMock) {
p.ConfirmFunc = func(message string, defaultValue bool) (bool, error) {
switch message {
case "Add a remote?":
return false, nil
default:
return false, fmt.Errorf("unexpected confirm prompt: %s", message)
}
}
p.InputFunc = func(message, defaultValue string) (string, error) {
switch message {
case "Path to local repository":
return defaultValue, nil
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, "Push an existing local repository to GitHub")
case "Visibility":
return prompter.IndexFor(options, "Private")
default:
return 0, fmt.Errorf("unexpected select prompt: %s", message)
}
}
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
@ -298,72 +349,44 @@ func Test_createRun(t *testing.T) {
wantStdout: "✓ Created repository OWNER/REPO on GitHub\n",
},
{
name: "interactive with existing repository public add remote",
name: "interactive with existing repository public add remote and push",
opts: &CreateOptions{Interactive: true},
tty: true,
askStubs: func(as *prompt.AskStubber) {
//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
as.StubOne("Push an existing local repository to GitHub")
//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
as.StubOne(".")
//nolint:staticcheck // SA1019: as.Stub is deprecated: use StubPrompt
as.Stub([]*prompt.QuestionStub{
{Name: "repoName", Value: "REPO"},
{Name: "repoDescription", Value: "my new repo"},
{Name: "repoVisibility", Value: "Private"},
})
//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
as.StubOne(true) //ask for adding a remote
//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
as.StubOne("origin") //ask for remote name
//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
as.StubOne(false) //ask to push to remote
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`mutation RepositoryCreate\b`),
httpmock.StringResponse(`
{
"data": {
"createRepository": {
"repository": {
"id": "REPOID",
"name": "REPO",
"owner": {"login":"OWNER"},
"url": "https://github.com/OWNER/REPO"
}
}
}
}`))
},
execStubs: func(cs *run.CommandStubber) {
cs.Register(`git -C . rev-parse --git-dir`, 0, ".git")
cs.Register(`git -C . rev-parse HEAD`, 0, "commithash")
cs.Register(`git -C . remote add origin https://github.com/OWNER/REPO`, 0, "")
},
wantStdout: "✓ Created repository OWNER/REPO on GitHub\n✓ Added remote https://github.com/OWNER/REPO.git\n",
},
{
name: "interactive with existing repository public, add remote, and push",
opts: &CreateOptions{Interactive: true},
tty: true,
askStubs: func(as *prompt.AskStubber) {
//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
as.StubOne("Push an existing local repository to GitHub")
//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
as.StubOne(".")
//nolint:staticcheck // SA1019: as.Stub is deprecated: use StubPrompt
as.Stub([]*prompt.QuestionStub{
{Name: "repoName", Value: "REPO"},
{Name: "repoDescription", Value: "my new repo"},
{Name: "repoVisibility", Value: "Private"},
})
//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
as.StubOne(true) //ask for adding a remote
//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
as.StubOne("origin") //ask for remote name
//nolint:staticcheck // SA1019: as.StubOne is deprecated: use StubPrompt
as.StubOne(true) //ask to push to remote
promptStubs: func(p *prompter.PrompterMock) {
p.ConfirmFunc = func(message string, defaultValue bool) (bool, error) {
switch message {
case "Add a remote?":
return true, nil
case `Would you like to push commits from the current branch to "origin"?`:
return true, nil
default:
return false, fmt.Errorf("unexpected confirm prompt: %s", message)
}
}
p.InputFunc = func(message, defaultValue string) (string, error) {
switch message {
case "Path to local repository":
return defaultValue, nil
case "Repository name":
return "REPO", nil
case "Description":
return "my new repo", nil
case "What should the new remote be called?":
return defaultValue, 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, "Push an existing local repository to GitHub")
case "Visibility":
return prompter.IndexFor(options, "Private")
default:
return 0, fmt.Errorf("unexpected select prompt: %s", message)
}
}
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
@ -452,11 +475,10 @@ func Test_createRun(t *testing.T) {
},
}
for _, tt := range tests {
//nolint:staticcheck // SA1019: prompt.InitAskStubber is deprecated: use NewAskStubber
q, teardown := prompt.InitAskStubber()
defer teardown()
if tt.askStubs != nil {
tt.askStubs(q)
prompterMock := &prompter.PrompterMock{}
tt.opts.Prompter = prompterMock
if tt.promptStubs != nil {
tt.promptStubs(prompterMock)
}
reg := &httpmock.Registry{}

View file

@ -24,6 +24,7 @@ type repoCreateInput struct {
GitIgnoreTemplate string
LicenseTemplate string
IncludeAllBranches bool
InitReadme bool
}
// createRepositoryInputV3 is the payload for the repo create REST API
@ -38,6 +39,7 @@ type createRepositoryInputV3 struct {
HasWikiEnabled bool `json:"has_wiki"`
GitIgnoreTemplate string `json:"gitignore_template,omitempty"`
LicenseTemplate string `json:"license_template,omitempty"`
InitReadme bool `json:"auto_init,omitempty"`
}
// createRepositoryInput is the payload for the repo create GraphQL mutation
@ -134,7 +136,7 @@ func repoCreate(client *http.Client, hostname string, input repoCreateInput) (*a
return api.InitRepoHostname(&response.CloneTemplateRepository.Repository, hostname), nil
}
if input.GitIgnoreTemplate != "" || input.LicenseTemplate != "" {
if input.GitIgnoreTemplate != "" || input.LicenseTemplate != "" || input.InitReadme {
inputv3 := createRepositoryInputV3{
Name: input.Name,
HomepageURL: input.HomepageURL,
@ -145,6 +147,7 @@ func repoCreate(client *http.Client, hostname string, input repoCreateInput) (*a
HasWikiEnabled: input.HasWikiEnabled,
GitIgnoreTemplate: input.GitIgnoreTemplate,
LicenseTemplate: input.LicenseTemplate,
InitReadme: input.InitReadme,
}
path := "user/repos"

View file

@ -304,6 +304,28 @@ func Test_repoCreate(t *testing.T) {
},
wantRepo: "https://github.com/snacks-inc/crisps",
},
{
name: "create with README",
hostname: "github.com",
input: repoCreateInput{
Name: "crisps",
InitReadme: true,
},
stubs: func(t *testing.T, r *httpmock.Registry) {
r.Register(
httpmock.REST("POST", "user/repos"),
httpmock.RESTPayload(201, `{"name":"crisps", "owner":{"login": "snacks-inc"}, "html_url":"the://URL"}`, func(payload map[string]interface{}) {
assert.Equal(t, map[string]interface{}{
"name": "crisps",
"private": false,
"has_issues": false,
"has_wiki": false,
"auto_init": true,
}, payload)
}))
},
wantRepo: "https://github.com/snacks-inc/crisps",
},
{
name: "create with license and gitignore on Enterprise",
hostname: "example.com",