Add the option to generate a README for the created repository (#6264)
This commit is contained in:
parent
354cfbd1b1
commit
113acf9245
4 changed files with 262 additions and 327 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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{}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue