Merge remote-tracking branch 'origin' into fix-gh-config-examples

This commit is contained in:
Mislav Marohnić 2020-06-11 16:30:00 +02:00
commit 8ffbc24c65
23 changed files with 1099 additions and 331 deletions

119
command/help.go Normal file
View file

@ -0,0 +1,119 @@
package command
import (
"fmt"
"strings"
"github.com/cli/cli/utils"
"github.com/spf13/cobra"
)
func rootHelpFunc(command *cobra.Command, args []string) {
// Display helpful error message in case subcommand name was mistyped.
// This matches Cobra's behavior for root command, which Cobra
// confusingly doesn't apply to nested commands.
if command != RootCmd {
if command.Parent() == RootCmd && len(args) >= 2 {
if command.SuggestionsMinimumDistance <= 0 {
command.SuggestionsMinimumDistance = 2
}
candidates := command.SuggestionsFor(args[1])
errOut := command.OutOrStderr()
fmt.Fprintf(errOut, "unknown command %q for %q\n", args[1], "gh "+args[0])
if len(candidates) > 0 {
fmt.Fprint(errOut, "\nDid you mean this?\n")
for _, c := range candidates {
fmt.Fprintf(errOut, "\t%s\n", c)
}
fmt.Fprint(errOut, "\n")
}
oldOut := command.OutOrStdout()
command.SetOut(errOut)
defer command.SetOut(oldOut)
}
}
coreCommands := []string{}
additionalCommands := []string{}
for _, c := range command.Commands() {
if c.Short == "" {
continue
}
if c.Hidden {
continue
}
s := rpad(c.Name()+":", c.NamePadding()) + c.Short
if _, ok := c.Annotations["IsCore"]; ok {
coreCommands = append(coreCommands, s)
} else {
additionalCommands = append(additionalCommands, s)
}
}
// If there are no core commands, assume everything is a core command
if len(coreCommands) == 0 {
coreCommands = additionalCommands
additionalCommands = []string{}
}
type helpEntry struct {
Title string
Body string
}
helpEntries := []helpEntry{}
if command.Long != "" {
helpEntries = append(helpEntries, helpEntry{"", command.Long})
} else if command.Short != "" {
helpEntries = append(helpEntries, helpEntry{"", command.Short})
}
helpEntries = append(helpEntries, helpEntry{"USAGE", command.UseLine()})
if len(coreCommands) > 0 {
helpEntries = append(helpEntries, helpEntry{"CORE COMMANDS", strings.Join(coreCommands, "\n")})
}
if len(additionalCommands) > 0 {
helpEntries = append(helpEntries, helpEntry{"ADDITIONAL COMMANDS", strings.Join(additionalCommands, "\n")})
}
if command.HasLocalFlags() {
helpEntries = append(helpEntries, helpEntry{"FLAGS", strings.TrimRight(command.LocalFlags().FlagUsages(), "\n")})
}
if _, ok := command.Annotations["help:arguments"]; ok {
helpEntries = append(helpEntries, helpEntry{"ARGUMENTS", command.Annotations["help:arguments"]})
}
if command.Example != "" {
helpEntries = append(helpEntries, helpEntry{"EXAMPLES", command.Example})
}
helpEntries = append(helpEntries, helpEntry{"LEARN MORE", `
Use "gh <command> <subcommand> --help" for more information about a command.
Read the manual at http://cli.github.com/manual`})
if _, ok := command.Annotations["help:feedback"]; ok {
helpEntries = append(helpEntries, helpEntry{"FEEDBACK", command.Annotations["help:feedback"]})
}
out := colorableOut(command)
for _, e := range helpEntries {
if e.Title != "" {
// If there is a title, add indentation to each line in the body
fmt.Fprintln(out, utils.Bold(e.Title))
for _, l := range strings.Split(strings.Trim(e.Body, "\n\r"), "\n") {
l = strings.Trim(l, " \n\r")
fmt.Fprintln(out, " "+l)
}
} else {
// If there is no title print the body as is
fmt.Fprintln(out, e.Body)
}
fmt.Fprintln(out)
}
}
// rpad adds padding to the right of a string.
func rpad(s string, padding int) string {
template := fmt.Sprintf("%%-%ds ", padding)
return fmt.Sprintf(template, s)
}

View file

@ -49,13 +49,17 @@ func init() {
}
var issueCmd = &cobra.Command{
Use: "issue",
Use: "issue <command>",
Short: "Create and view issues",
Long: `Work with GitHub issues.
An issue can be supplied as argument in any of the following formats:
Long: `Work with GitHub issues`,
Example: `$ gh issue list
$ gh issue create --fill
$ gh issue view --web`,
Annotations: map[string]string{
"IsCore": "true",
"help:arguments": `An issue can be supplied as argument in any of the following formats:
- by number, e.g. "123"; or
- by URL, e.g. "https://github.com/OWNER/REPO/issues/123".`,
- by URL, e.g. "https://github.com/OWNER/REPO/issues/123".`},
}
var issueCreateCmd = &cobra.Command{
Use: "create",
@ -351,11 +355,11 @@ func issueCreate(cmd *cobra.Command, args []string) error {
return err
}
var templateFiles []string
var nonLegacyTemplateFiles []string
if baseOverride == "" {
if rootDir, err := git.ToplevelDir(); err == nil {
// TODO: figure out how to stub this in tests
templateFiles = githubtemplate.Find(rootDir, "ISSUE_TEMPLATE")
nonLegacyTemplateFiles = githubtemplate.FindNonLegacy(rootDir, "ISSUE_TEMPLATE")
}
}
@ -399,7 +403,7 @@ func issueCreate(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
} else if len(templateFiles) > 1 {
} else if len(nonLegacyTemplateFiles) > 1 {
openURL += "/choose"
}
cmd.Printf("Opening %s in your browser.\n", displayURL(openURL))
@ -418,6 +422,7 @@ func issueCreate(cmd *cobra.Command, args []string) error {
action := SubmitAction
tb := issueMetadataState{
Type: issueMetadata,
Assignees: assignees,
Labels: labelNames,
Projects: projectNames,
@ -427,7 +432,14 @@ func issueCreate(cmd *cobra.Command, args []string) error {
interactive := !(cmd.Flags().Changed("title") && cmd.Flags().Changed("body"))
if interactive {
err := titleBodySurvey(cmd, &tb, apiClient, baseRepo, title, body, defaults{}, templateFiles, false, repo.ViewerCanTriage())
var legacyTemplateFile *string
if baseOverride == "" {
if rootDir, err := git.ToplevelDir(); err == nil {
// TODO: figure out how to stub this in tests
legacyTemplateFile = githubtemplate.FindLegacy(rootDir, "ISSUE_TEMPLATE")
}
}
err := titleBodySurvey(cmd, &tb, apiClient, baseRepo, title, body, defaults{}, nonLegacyTemplateFiles, legacyTemplateFile, false, repo.ViewerCanTriage())
if err != nil {
return fmt.Errorf("could not collect title and/or body: %w", err)
}

View file

@ -46,19 +46,27 @@ func init() {
}
var prCmd = &cobra.Command{
Use: "pr",
Use: "pr <command>",
Short: "Create, view, and checkout pull requests",
Long: `Work with GitHub pull requests.
A pull request can be supplied as argument in any of the following formats:
Long: `Work with GitHub pull requests`,
Example: `$ gh pr checkout 353
$ gh pr checkout bug-fix-branch
$ gh pr create --fill
$ gh pr view --web`,
Annotations: map[string]string{
"IsCore": "true",
"help:arguments": `A pull request can be supplied as argument in any of the following formats:
- by number, e.g. "123";
- by URL, e.g. "https://github.com/OWNER/REPO/pull/123"; or
- by the name of its head branch, e.g. "patch-1" or "OWNER:patch-1".`,
- by the name of its head branch, e.g. "patch-1" or "OWNER:patch-1".`},
}
var prListCmd = &cobra.Command{
Use: "list",
Short: "List and filter pull requests in this repository",
RunE: prList,
Example: `$ gh pr list --limit all
$ gh pr list --state closed
$ gh pr list --label priority 1 bug`,
RunE: prList,
}
var prStatusCmd = &cobra.Command{
Use: "status",

View file

@ -40,8 +40,8 @@ func computeDefaults(baseRef, headRef string) (defaults, error) {
out.Title = utils.Humanize(headRef)
body := ""
for _, c := range commits {
body += fmt.Sprintf("- %s\n", c.Title)
for i := len(commits) - 1; i >= 0; i-- {
body += fmt.Sprintf("- %s\n", commits[i].Title)
}
out.Body = body
}
@ -203,6 +203,7 @@ func prCreate(cmd *cobra.Command, _ []string) error {
}
tb := issueMetadataState{
Type: prMetadata,
Reviewers: reviewers,
Assignees: assignees,
Labels: labelNames,
@ -213,13 +214,14 @@ func prCreate(cmd *cobra.Command, _ []string) error {
interactive := !(cmd.Flags().Changed("title") && cmd.Flags().Changed("body"))
if !isWeb && !autofill && interactive {
var templateFiles []string
var nonLegacyTemplateFiles []string
var legacyTemplateFile *string
if rootDir, err := git.ToplevelDir(); err == nil {
// TODO: figure out how to stub this in tests
templateFiles = githubtemplate.Find(rootDir, "PULL_REQUEST_TEMPLATE")
nonLegacyTemplateFiles = githubtemplate.FindNonLegacy(rootDir, "PULL_REQUEST_TEMPLATE")
legacyTemplateFile = githubtemplate.FindLegacy(rootDir, "PULL_REQUEST_TEMPLATE")
}
err := titleBodySurvey(cmd, &tb, client, baseRepo, title, body, defs, templateFiles, true, baseRepo.ViewerCanTriage())
err := titleBodySurvey(cmd, &tb, client, baseRepo, title, body, defs, nonLegacyTemplateFiles, legacyTemplateFile, true, baseRepo.ViewerCanTriage())
if err != nil {
return fmt.Errorf("could not collect title and/or body: %w", err)
}

View file

@ -519,7 +519,7 @@ func TestPRCreate_survey_defaults_multicommit(t *testing.T) {
}{}
_ = json.Unmarshal(bodyBytes, &reqBody)
expectedBody := "- commit 0\n- commit 1\n"
expectedBody := "- commit 1\n- commit 0\n"
eq(t, reqBody.Variables.Input.RepositoryID, "REPOID")
eq(t, reqBody.Variables.Input.Title, "cool bug fixes")

View file

@ -180,7 +180,7 @@ func reviewSurvey(cmd *cobra.Command) (*api.PullRequestReviewInput, error) {
}
bodyQs := []*survey.Question{
&survey.Question{
{
Name: "body",
Prompt: &surveyext.GhEditor{
BlankAllowed: blankAllowed,

View file

@ -213,10 +213,10 @@ func TestPRReview(t *testing.T) {
ExpectedBody string
}
cases := []c{
c{`pr review --request-changes -b"bad"`, "REQUEST_CHANGES", "bad"},
c{`pr review --approve`, "APPROVE", ""},
c{`pr review --approve -b"hot damn"`, "APPROVE", "hot damn"},
c{`pr review --comment --body "i donno"`, "COMMENT", "i donno"},
{`pr review --request-changes -b"bad"`, "REQUEST_CHANGES", "bad"},
{`pr review --approve`, "APPROVE", ""},
{`pr review --approve -b"hot damn"`, "APPROVE", "hot damn"},
{`pr review --comment --body "i donno"`, "COMMENT", "i donno"},
}
for _, kase := range cases {

View file

@ -44,13 +44,19 @@ func init() {
}
var repoCmd = &cobra.Command{
Use: "repo",
Use: "repo <command>",
Short: "Create, clone, fork, and view repositories",
Long: `Work with GitHub repositories.
Long: `Work with GitHub repositories`,
Example: `$ gh repo create
$ gh repo clone cli/cli
$ gh repo view --web
`,
Annotations: map[string]string{
"IsCore": "true",
"help:arguments": `
A repository can be supplied as an argument in any of the following formats:
- "OWNER/REPO"
- by URL, e.g. "https://github.com/OWNER/REPO"`,
- by URL, e.g. "https://github.com/OWNER/REPO"`},
}
var repoCloneCmd = &cobra.Command{
@ -69,9 +75,18 @@ To pass 'git clone' flags, separate them with '--'.`,
var repoCreateCmd = &cobra.Command{
Use: "create [<name>]",
Short: "Create a new repository",
Long: `Create a new GitHub repository.
Long: `Create a new GitHub repository`,
Example: utils.Bold("$ gh repo create") + `
Will create a repository on your account using the name of your current directory
Use the "ORG/NAME" syntax to create a repository within your organization.`,
` + utils.Bold("$ gh repo create my-project") + `
Will create a repository on your account using the name 'my-project'
` + utils.Bold("$ gh repo create cli/my-project") + `
Will create a repository in the organization 'cli' using the name 'my-project'`,
Annotations: map[string]string{"help:arguments": `A repository can be supplied as an argument in any of the following formats:
- <OWNER/REPO>
- by URL, e.g. "https://github.com/OWNER/REPO"`},
RunE: repoCreate,
}

View file

@ -1,7 +1,6 @@
package command
import (
"bytes"
"encoding/json"
"io/ioutil"
"os/exec"
@ -459,11 +458,13 @@ func TestRepoClone(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": {
"parent": null
} } }
`))
http.Register(
httpmock.GraphQL(`\brepository\(`),
httpmock.StringResponse(`
{ "data": { "repository": {
"parent": null
} } }
`))
cs, restore := test.InitCmdStubber()
defer restore()
@ -485,14 +486,16 @@ func TestRepoClone(t *testing.T) {
func TestRepoClone_hasParent(t *testing.T) {
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": {
"parent": {
"owner": {"login": "hubot"},
"name": "ORIG"
}
} } }
`))
http.Register(
httpmock.GraphQL(`\brepository\(`),
httpmock.StringResponse(`
{ "data": { "repository": {
"parent": {
"owner": {"login": "hubot"},
"name": "ORIG"
}
} } }
`))
cs, restore := test.InitCmdStubber()
defer restore()
@ -548,18 +551,19 @@ func TestRepoCreate(t *testing.T) {
}
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "createRepository": {
"repository": {
"id": "REPOID",
"url": "https://github.com/OWNER/REPO",
"name": "REPO",
"owner": {
"login": "OWNER"
http.Register(
httpmock.GraphQL(`\bcreateRepository\(`),
httpmock.StringResponse(`
{ "data": { "createRepository": {
"repository": {
"id": "REPOID",
"url": "https://github.com/OWNER/REPO",
"name": "REPO",
"owner": {
"login": "OWNER"
}
}
}
} } }
`))
} } }`))
var seenCmd *exec.Cmd
restoreCmd := run.SetPrepareCmd(func(cmd *exec.Cmd) run.Runnable {
@ -613,22 +617,24 @@ func TestRepoCreate_org(t *testing.T) {
}
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
{ "node_id": "ORGID"
}
`))
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "createRepository": {
"repository": {
"id": "REPOID",
"url": "https://github.com/ORG/REPO",
"name": "REPO",
"owner": {
"login": "ORG"
http.Register(
httpmock.MatchAny,
httpmock.StringResponse(`
{ "node_id": "ORGID"
}`))
http.Register(
httpmock.GraphQL(`\bcreateRepository\(`),
httpmock.StringResponse(`
{ "data": { "createRepository": {
"repository": {
"id": "REPOID",
"url": "https://github.com/ORG/REPO",
"name": "REPO",
"owner": {
"login": "ORG"
}
}
}
} } }
`))
} } }`))
var seenCmd *exec.Cmd
restoreCmd := run.SetPrepareCmd(func(cmd *exec.Cmd) run.Runnable {
@ -681,23 +687,25 @@ func TestRepoCreate_orgWithTeam(t *testing.T) {
}
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
{ "node_id": "TEAMID",
"organization": { "node_id": "ORGID" }
}
`))
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "createRepository": {
"repository": {
"id": "REPOID",
"url": "https://github.com/ORG/REPO",
"name": "REPO",
"owner": {
"login": "ORG"
http.Register(
httpmock.MatchAny,
httpmock.StringResponse(`
{ "node_id": "TEAMID",
"organization": { "node_id": "ORGID" }
}`))
http.Register(
httpmock.GraphQL(`\bcreateRepository\(`),
httpmock.StringResponse(`
{ "data": { "createRepository": {
"repository": {
"id": "REPOID",
"url": "https://github.com/ORG/REPO",
"name": "REPO",
"owner": {
"login": "ORG"
}
}
}
} } }
`))
} } }`))
var seenCmd *exec.Cmd
restoreCmd := run.SetPrepareCmd(func(cmd *exec.Cmd) run.Runnable {
@ -746,9 +754,10 @@ func TestRepoView_web(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
{ }
`))
http.Register(
httpmock.MatchAny,
httpmock.StringResponse(`
{ }`))
var seenCmd *exec.Cmd
restoreCmd := run.SetPrepareCmd(func(cmd *exec.Cmd) run.Runnable {
@ -779,9 +788,10 @@ func TestRepoView_web_ownerRepo(t *testing.T) {
return ctx
}
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
{ }
`))
http.Register(
httpmock.MatchAny,
httpmock.StringResponse(`
{ }`))
var seenCmd *exec.Cmd
restoreCmd := run.SetPrepareCmd(func(cmd *exec.Cmd) run.Runnable {
@ -812,9 +822,10 @@ func TestRepoView_web_fullURL(t *testing.T) {
return ctx
}
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
{ }
`))
http.Register(
httpmock.MatchAny,
httpmock.StringResponse(`
{ }`))
var seenCmd *exec.Cmd
restoreCmd := run.SetPrepareCmd(func(cmd *exec.Cmd) run.Runnable {
seenCmd = cmd
@ -841,16 +852,18 @@ func TestRepoView(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
http.Register(
httpmock.GraphQL(`\brepository\(`),
httpmock.StringResponse(`
{ "data": {
"repository": {
"description": "social distancing"
}}}
`))
http.StubResponse(200, bytes.NewBufferString(`
"repository": {
"description": "social distancing"
} } }`))
http.Register(
httpmock.MatchAny,
httpmock.StringResponse(`
{ "name": "readme.md",
"content": "IyB0cnVseSBjb29sIHJlYWRtZSBjaGVjayBpdCBvdXQ="}
`))
"content": "IyB0cnVseSBjb29sIHJlYWRtZSBjaGVjayBpdCBvdXQ="}`))
output, err := RunCommand("repo view")
if err != nil {
@ -869,16 +882,18 @@ func TestRepoView_nonmarkdown_readme(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
http.Register(
httpmock.GraphQL(`\brepository\(`),
httpmock.StringResponse(`
{ "data": {
"repository": {
"description": "social distancing"
}}}
`))
http.StubResponse(200, bytes.NewBufferString(`
"description": "social distancing"
} } }`))
http.Register(
httpmock.MatchAny,
httpmock.StringResponse(`
{ "name": "readme.org",
"content": "IyB0cnVseSBjb29sIHJlYWRtZSBjaGVjayBpdCBvdXQ="}
`))
"content": "IyB0cnVseSBjb29sIHJlYWRtZSBjaGVjayBpdCBvdXQ="}`))
output, err := RunCommand("repo view")
if err != nil {
@ -896,8 +911,8 @@ func TestRepoView_blanks(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString("{}"))
http.StubResponse(200, bytes.NewBufferString("{}"))
http.Register(httpmock.MatchAny, httpmock.StringResponse("{}"))
http.Register(httpmock.MatchAny, httpmock.StringResponse("{}"))
output, err := RunCommand("repo view")
if err != nil {

View file

@ -34,7 +34,6 @@ var Version = "DEV"
var BuildDate = "" // YYYY-MM-DD
var versionOutput = ""
var cobraDefaultHelpFunc func(*cobra.Command, []string)
func init() {
if Version == "DEV" {
@ -58,9 +57,11 @@ func init() {
// TODO:
// RootCmd.PersistentFlags().BoolP("verbose", "V", false, "enable verbose output")
cobraDefaultHelpFunc = RootCmd.HelpFunc()
RootCmd.SetHelpFunc(rootHelpFunc)
// This will silence the usage func on error
RootCmd.SetUsageFunc(func(_ *cobra.Command) error { return nil })
RootCmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
if err == pflag.ErrHelp {
return err
@ -95,6 +96,13 @@ var RootCmd = &cobra.Command{
SilenceErrors: true,
SilenceUsage: true,
Example: `$ gh issue create
$ gh repo clone
$ gh pr checkout 321`,
Annotations: map[string]string{
"help:feedback": `
Fill out our feedback form https://forms.gle/umxd3h31c7aMQFKG7
Open an issue using gh issue create -R cli/cli`},
}
var versionCmd = &cobra.Command{
@ -320,100 +328,6 @@ func determineBaseRepo(apiClient *api.Client, cmd *cobra.Command, ctx context.Co
return baseRepo, nil
}
func rootHelpFunc(command *cobra.Command, args []string) {
if command != RootCmd {
// Display helpful error message in case subcommand name was mistyped.
// This matches Cobra's behavior for root command, which Cobra
// confusingly doesn't apply to nested commands.
if command.Parent() == RootCmd && len(args) >= 2 {
if command.SuggestionsMinimumDistance <= 0 {
command.SuggestionsMinimumDistance = 2
}
candidates := command.SuggestionsFor(args[1])
errOut := command.OutOrStderr()
fmt.Fprintf(errOut, "unknown command %q for %q\n", args[1], "gh "+args[0])
if len(candidates) > 0 {
fmt.Fprint(errOut, "\nDid you mean this?\n")
for _, c := range candidates {
fmt.Fprintf(errOut, "\t%s\n", c)
}
fmt.Fprint(errOut, "\n")
}
oldOut := command.OutOrStdout()
command.SetOut(errOut)
defer command.SetOut(oldOut)
}
cobraDefaultHelpFunc(command, args)
return
}
type helpEntry struct {
Title string
Body string
}
coreCommandNames := []string{"issue", "pr", "repo"}
var coreCommands []string
var additionalCommands []string
for _, c := range command.Commands() {
if c.Short == "" {
continue
}
s := " " + rpad(c.Name()+":", c.NamePadding()) + c.Short
if includes(coreCommandNames, c.Name()) {
coreCommands = append(coreCommands, s)
} else if !c.Hidden {
additionalCommands = append(additionalCommands, s)
}
}
helpEntries := []helpEntry{
{
"",
command.Long},
{"USAGE", command.Use},
{"CORE COMMANDS", strings.Join(coreCommands, "\n")},
{"ADDITIONAL COMMANDS", strings.Join(additionalCommands, "\n")},
{"FLAGS", strings.TrimRight(command.LocalFlags().FlagUsages(), "\n")},
{"EXAMPLES", `
$ gh issue create
$ gh repo clone
$ gh pr checkout 321`},
{"LEARN MORE", `
Use "gh <command> <subcommand> --help" for more information about a command.
Read the manual at http://cli.github.com/manual`},
{"FEEDBACK", `
Fill out our feedback form https://forms.gle/umxd3h31c7aMQFKG7
Open an issue using gh issue create -R cli/cli`},
}
out := colorableOut(command)
for _, e := range helpEntries {
if e.Title != "" {
fmt.Fprintln(out, utils.Bold(e.Title))
}
fmt.Fprintln(out, strings.TrimLeft(e.Body, "\n")+"\n")
}
}
// rpad adds padding to the right of a string.
func rpad(s string, padding int) string {
template := fmt.Sprintf("%%-%ds ", padding)
return fmt.Sprintf(template, s)
}
func includes(a []string, s string) bool {
for _, x := range a {
if x == s {
return true
}
}
return false
}
func formatRemoteURL(cmd *cobra.Command, fullRepoName string) string {
ctx := contextForCommand(cmd)

View file

@ -13,8 +13,16 @@ import (
)
type Action int
type metadataStateType int
const (
issueMetadata metadataStateType = iota
prMetadata
)
type issueMetadataState struct {
Type metadataStateType
Body string
Title string
Action Action
@ -99,35 +107,46 @@ func confirmSubmission(allowPreview bool, allowMetadata bool) (Action, error) {
}
}
func selectTemplate(templatePaths []string) (string, error) {
func selectTemplate(nonLegacyTemplatePaths []string, legacyTemplatePath *string, metadataType metadataStateType) (string, error) {
templateResponse := struct {
Index int
}{}
if len(templatePaths) > 1 {
templateNames := make([]string, 0, len(templatePaths))
for _, p := range templatePaths {
templateNames = append(templateNames, githubtemplate.ExtractName(p))
}
selectQs := []*survey.Question{
{
Name: "index",
Prompt: &survey.Select{
Message: "Choose a template",
Options: templateNames,
},
},
}
if err := SurveyAsk(selectQs, &templateResponse); err != nil {
return "", fmt.Errorf("could not prompt: %w", err)
}
templateNames := make([]string, 0, len(nonLegacyTemplatePaths))
for _, p := range nonLegacyTemplatePaths {
templateNames = append(templateNames, githubtemplate.ExtractName(p))
}
if metadataType == issueMetadata {
templateNames = append(templateNames, "Open a blank issue")
} else if metadataType == prMetadata {
templateNames = append(templateNames, "Open a blank pull request")
}
templateContents := githubtemplate.ExtractContents(templatePaths[templateResponse.Index])
selectQs := []*survey.Question{
{
Name: "index",
Prompt: &survey.Select{
Message: "Choose a template",
Options: templateNames,
},
},
}
if err := SurveyAsk(selectQs, &templateResponse); err != nil {
return "", fmt.Errorf("could not prompt: %w", err)
}
if templateResponse.Index == len(nonLegacyTemplatePaths) { // the user has selected the blank template
if legacyTemplatePath != nil {
templateContents := githubtemplate.ExtractContents(*legacyTemplatePath)
return string(templateContents), nil
} else {
return "", nil
}
}
templateContents := githubtemplate.ExtractContents(nonLegacyTemplatePaths[templateResponse.Index])
return string(templateContents), nil
}
func titleBodySurvey(cmd *cobra.Command, issueState *issueMetadataState, apiClient *api.Client, repo ghrepo.Interface, providedTitle, providedBody string, defs defaults, templatePaths []string, allowReviewers, allowMetadata bool) error {
func titleBodySurvey(cmd *cobra.Command, issueState *issueMetadataState, apiClient *api.Client, repo ghrepo.Interface, providedTitle, providedBody string, defs defaults, nonLegacyTemplatePaths []string, legacyTemplatePath *string, allowReviewers, allowMetadata bool) error {
editorCommand, err := determineEditor(cmd)
if err != nil {
return err
@ -137,13 +156,15 @@ func titleBodySurvey(cmd *cobra.Command, issueState *issueMetadataState, apiClie
templateContents := ""
if providedBody == "" {
if len(templatePaths) > 0 {
if len(nonLegacyTemplatePaths) > 0 {
var err error
templateContents, err = selectTemplate(templatePaths)
templateContents, err = selectTemplate(nonLegacyTemplatePaths, legacyTemplatePath, issueState.Type)
if err != nil {
return err
}
issueState.Body = templateContents
} else if legacyTemplatePath != nil {
issueState.Body = string(githubtemplate.ExtractContents(*legacyTemplatePath))
} else {
issueState.Body = defs.Body
}