Merge pull request #6711 from cli/release-prompter
port release command to prompter
This commit is contained in:
commit
67e5baea09
7 changed files with 399 additions and 166 deletions
|
|
@ -1,6 +1,11 @@
|
|||
package prompter
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Test helpers
|
||||
|
||||
|
|
@ -13,6 +18,10 @@ func IndexFor(options []string, answer string) (int, error) {
|
|||
return -1, NoSuchAnswerErr(answer)
|
||||
}
|
||||
|
||||
func AssertOptions(t *testing.T, expected, actual []string) {
|
||||
assert.Equal(t, expected, actual)
|
||||
}
|
||||
|
||||
func NoSuchAnswerErr(answer string) error {
|
||||
return fmt.Errorf("no such answer '%s'", answer)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/cli/cli/v2/git"
|
||||
"github.com/cli/cli/v2/internal/config"
|
||||
|
|
@ -18,11 +17,16 @@ import (
|
|||
"github.com/cli/cli/v2/pkg/cmd/release/shared"
|
||||
"github.com/cli/cli/v2/pkg/cmdutil"
|
||||
"github.com/cli/cli/v2/pkg/iostreams"
|
||||
"github.com/cli/cli/v2/pkg/prompt"
|
||||
"github.com/cli/cli/v2/pkg/surveyext"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type iprompter interface {
|
||||
Select(string, string, []string) (int, error)
|
||||
Input(string, string) (string, error)
|
||||
Confirm(string, bool) (bool, error)
|
||||
}
|
||||
|
||||
type CreateOptions struct {
|
||||
IO *iostreams.IOStreams
|
||||
Config func() (config.Config, error)
|
||||
|
|
@ -30,6 +34,7 @@ type CreateOptions struct {
|
|||
GitClient *git.Client
|
||||
BaseRepo func() (ghrepo.Interface, error)
|
||||
Edit func(string, string, string, io.Reader, io.Writer, io.Writer) (string, error)
|
||||
Prompter iprompter
|
||||
|
||||
TagName string
|
||||
Target string
|
||||
|
|
@ -62,6 +67,7 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
|
|||
HttpClient: f.HttpClient,
|
||||
GitClient: f.GitClient,
|
||||
Config: f.Config,
|
||||
Prompter: f.Prompter,
|
||||
Edit: surveyext.Edit,
|
||||
}
|
||||
|
||||
|
|
@ -199,17 +205,11 @@ func createRun(opts *CreateOptions) error {
|
|||
}
|
||||
createNewTagOption := "Create a new tag"
|
||||
options = append(options, createNewTagOption)
|
||||
var tag string
|
||||
q := &survey.Select{
|
||||
Message: "Choose a tag",
|
||||
Options: options,
|
||||
Default: options[0],
|
||||
}
|
||||
//nolint:staticcheck // SA1019: prompt.SurveyAskOne is deprecated: use Prompter
|
||||
err := prompt.SurveyAskOne(q, &tag)
|
||||
selected, err := opts.Prompter.Select("Choose a tag", options[0], options)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not prompt: %w", err)
|
||||
}
|
||||
tag := options[selected]
|
||||
if tag != createNewTagOption {
|
||||
existingTag = true
|
||||
opts.TagName = tag
|
||||
|
|
@ -217,11 +217,7 @@ func createRun(opts *CreateOptions) error {
|
|||
}
|
||||
|
||||
if opts.TagName == "" {
|
||||
q := &survey.Input{
|
||||
Message: "Tag name",
|
||||
}
|
||||
//nolint:staticcheck // SA1019: prompt.SurveyAskOne is deprecated: use Prompter
|
||||
err := prompt.SurveyAskOne(q, &opts.TagName)
|
||||
opts.TagName, err = opts.Prompter.Input("Tag name", "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not prompt: %w", err)
|
||||
}
|
||||
|
|
@ -313,28 +309,18 @@ func createRun(opts *CreateOptions) error {
|
|||
if defaultName == "" && generatedNotes != nil {
|
||||
defaultName = generatedNotes.Name
|
||||
}
|
||||
qs := []*survey.Question{
|
||||
{
|
||||
Name: "name",
|
||||
Prompt: &survey.Input{
|
||||
Message: "Title (optional)",
|
||||
Default: defaultName,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "releaseNotesAction",
|
||||
Prompt: &survey.Select{
|
||||
Message: "Release notes",
|
||||
Options: editorOptions,
|
||||
},
|
||||
},
|
||||
}
|
||||
//nolint:staticcheck // SA1019: prompt.SurveyAsk is deprecated: use Prompter
|
||||
err = prompt.SurveyAsk(qs, opts)
|
||||
|
||||
opts.Name, err = opts.Prompter.Input("Title (optional)", defaultName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not prompt: %w", err)
|
||||
}
|
||||
|
||||
selected, err := opts.Prompter.Select("Release notes", "", editorOptions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not prompt: %w", err)
|
||||
}
|
||||
opts.ReleaseNotesAction = editorOptions[selected]
|
||||
|
||||
var openEditor bool
|
||||
var editorContents string
|
||||
|
||||
|
|
@ -372,34 +358,19 @@ func createRun(opts *CreateOptions) error {
|
|||
defaultSubmit = saveAsDraft
|
||||
}
|
||||
|
||||
qs = []*survey.Question{
|
||||
{
|
||||
Name: "prerelease",
|
||||
Prompt: &survey.Confirm{
|
||||
Message: "Is this a prerelease?",
|
||||
Default: opts.Prerelease,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "submitAction",
|
||||
Prompt: &survey.Select{
|
||||
Message: "Submit?",
|
||||
Options: []string{
|
||||
publishRelease,
|
||||
saveAsDraft,
|
||||
"Cancel",
|
||||
},
|
||||
Default: defaultSubmit,
|
||||
},
|
||||
},
|
||||
opts.Prerelease, err = opts.Prompter.Confirm("Is this a prerelease?", opts.Prerelease)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//nolint:staticcheck // SA1019: prompt.SurveyAsk is deprecated: use Prompter
|
||||
err = prompt.SurveyAsk(qs, opts)
|
||||
options := []string{publishRelease, saveAsDraft, "Cancel"}
|
||||
selected, err = opts.Prompter.Select("Submit?", defaultSubmit, options)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not prompt: %w", err)
|
||||
}
|
||||
|
||||
opts.SubmitAction = options[selected]
|
||||
|
||||
switch opts.SubmitAction {
|
||||
case "Publish release":
|
||||
opts.Draft = false
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@ import (
|
|||
"github.com/cli/cli/v2/git"
|
||||
"github.com/cli/cli/v2/internal/config"
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
"github.com/cli/cli/v2/internal/prompter"
|
||||
"github.com/cli/cli/v2/internal/run"
|
||||
"github.com/cli/cli/v2/pkg/cmd/release/shared"
|
||||
"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"
|
||||
|
|
@ -828,29 +828,49 @@ func Test_createRun(t *testing.T) {
|
|||
|
||||
func Test_createRun_interactive(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
httpStubs func(*httpmock.Registry)
|
||||
askStubs func(*prompt.AskStubber)
|
||||
runStubs func(*run.CommandStubber)
|
||||
opts *CreateOptions
|
||||
wantParams map[string]interface{}
|
||||
wantOut string
|
||||
wantErr string
|
||||
name string
|
||||
httpStubs func(*httpmock.Registry)
|
||||
prompterStubs func(*testing.T, *prompter.PrompterMock)
|
||||
runStubs func(*run.CommandStubber)
|
||||
opts *CreateOptions
|
||||
wantParams map[string]interface{}
|
||||
wantOut string
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "create a release from existing tag",
|
||||
opts: &CreateOptions{},
|
||||
askStubs: func(as *prompt.AskStubber) {
|
||||
as.StubPrompt("Choose a tag").
|
||||
AssertOptions([]string{"v1.2.3", "v1.2.2", "v1.0.0", "v0.1.2", "Create a new tag"}).
|
||||
AnswerWith("v1.2.3")
|
||||
as.StubPrompt("Title (optional)").AnswerWith("")
|
||||
as.StubPrompt("Release notes").
|
||||
AssertOptions([]string{"Write my own", "Write using generated notes as template", "Leave blank"}).
|
||||
AnswerWith("Leave blank")
|
||||
as.StubPrompt("Is this a prerelease?").AnswerWith(false)
|
||||
as.StubPrompt("Submit?").
|
||||
AssertOptions([]string{"Publish release", "Save as draft", "Cancel"}).AnswerWith("Publish release")
|
||||
prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) {
|
||||
pm.SelectFunc = func(p, d string, opts []string) (int, error) {
|
||||
switch p {
|
||||
case "Choose a tag":
|
||||
prompter.AssertOptions(t, []string{"v1.2.3", "v1.2.2", "v1.0.0", "v0.1.2", "Create a new tag"}, opts)
|
||||
return prompter.IndexFor(opts, "v1.2.3")
|
||||
case "Release notes":
|
||||
prompter.AssertOptions(t, []string{"Write my own", "Write using generated notes as template", "Leave blank"}, opts)
|
||||
return prompter.IndexFor(opts, "Leave blank")
|
||||
case "Submit?":
|
||||
prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts)
|
||||
return prompter.IndexFor(opts, "Publish release")
|
||||
default:
|
||||
return -1, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
pm.InputFunc = func(p, d string) (string, error) {
|
||||
if p == "Title (optional)" {
|
||||
return d, nil
|
||||
}
|
||||
|
||||
return "", prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
pm.ConfirmFunc = func(p string, d bool) (bool, error) {
|
||||
switch p {
|
||||
case "Is this a prerelease?":
|
||||
return false, nil
|
||||
default:
|
||||
return false, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
},
|
||||
runStubs: func(rs *run.CommandStubber) {
|
||||
rs.Register(`git tag --list`, 1, "")
|
||||
|
|
@ -875,15 +895,40 @@ func Test_createRun_interactive(t *testing.T) {
|
|||
{
|
||||
name: "create a release from new tag",
|
||||
opts: &CreateOptions{},
|
||||
askStubs: func(as *prompt.AskStubber) {
|
||||
as.StubPrompt("Choose a tag").AnswerWith("Create a new tag")
|
||||
as.StubPrompt("Tag name").AnswerWith("v1.2.3")
|
||||
as.StubPrompt("Title (optional)").AnswerWith("")
|
||||
as.StubPrompt("Release notes").
|
||||
AssertOptions([]string{"Write my own", "Write using generated notes as template", "Leave blank"}).
|
||||
AnswerWith("Leave blank")
|
||||
as.StubPrompt("Is this a prerelease?").AnswerWith(false)
|
||||
as.StubPrompt("Submit?").AnswerWith("Publish release")
|
||||
prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) {
|
||||
pm.SelectFunc = func(p, d string, opts []string) (int, error) {
|
||||
switch p {
|
||||
case "Choose a tag":
|
||||
prompter.AssertOptions(t, []string{"v1.2.2", "v1.0.0", "v0.1.2", "Create a new tag"}, opts)
|
||||
return prompter.IndexFor(opts, "Create a new tag")
|
||||
case "Release notes":
|
||||
prompter.AssertOptions(t, []string{"Write my own", "Write using generated notes as template", "Leave blank"}, opts)
|
||||
return prompter.IndexFor(opts, "Leave blank")
|
||||
case "Submit?":
|
||||
prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts)
|
||||
return prompter.IndexFor(opts, "Publish release")
|
||||
default:
|
||||
return -1, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
pm.InputFunc = func(p, d string) (string, error) {
|
||||
switch p {
|
||||
case "Tag name":
|
||||
return "v1.2.3", nil
|
||||
case "Title (optional)":
|
||||
return d, nil
|
||||
default:
|
||||
return "", prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
pm.ConfirmFunc = func(p string, d bool) (bool, error) {
|
||||
switch p {
|
||||
case "Is this a prerelease?":
|
||||
return false, nil
|
||||
default:
|
||||
return false, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
},
|
||||
runStubs: func(rs *run.CommandStubber) {
|
||||
rs.Register(`git tag --list`, 1, "")
|
||||
|
|
@ -910,13 +955,35 @@ func Test_createRun_interactive(t *testing.T) {
|
|||
opts: &CreateOptions{
|
||||
TagName: "v1.2.3",
|
||||
},
|
||||
askStubs: func(as *prompt.AskStubber) {
|
||||
as.StubPrompt("Title (optional)").AnswerDefault()
|
||||
as.StubPrompt("Release notes").
|
||||
AssertOptions([]string{"Write my own", "Write using generated notes as template", "Leave blank"}).
|
||||
AnswerWith("Write using generated notes as template")
|
||||
as.StubPrompt("Is this a prerelease?").AnswerWith(false)
|
||||
as.StubPrompt("Submit?").AnswerWith("Publish release")
|
||||
prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) {
|
||||
pm.SelectFunc = func(p, d string, opts []string) (int, error) {
|
||||
switch p {
|
||||
case "Release notes":
|
||||
prompter.AssertOptions(t, []string{"Write my own", "Write using generated notes as template", "Leave blank"}, opts)
|
||||
return prompter.IndexFor(opts, "Write using generated notes as template")
|
||||
case "Submit?":
|
||||
prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts)
|
||||
return prompter.IndexFor(opts, "Publish release")
|
||||
default:
|
||||
return -1, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
pm.InputFunc = func(p, d string) (string, error) {
|
||||
switch p {
|
||||
case "Title (optional)":
|
||||
return d, nil
|
||||
default:
|
||||
return "", prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
pm.ConfirmFunc = func(p string, d bool) (bool, error) {
|
||||
switch p {
|
||||
case "Is this a prerelease?":
|
||||
return false, nil
|
||||
default:
|
||||
return false, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
},
|
||||
runStubs: func(rs *run.CommandStubber) {
|
||||
rs.Register(`git tag --list`, 1, "")
|
||||
|
|
@ -948,13 +1015,35 @@ func Test_createRun_interactive(t *testing.T) {
|
|||
opts: &CreateOptions{
|
||||
TagName: "v1.2.3",
|
||||
},
|
||||
askStubs: func(as *prompt.AskStubber) {
|
||||
as.StubPrompt("Title (optional)").AnswerDefault()
|
||||
as.StubPrompt("Release notes").
|
||||
AssertOptions([]string{"Write my own", "Write using commit log as template", "Leave blank"}).
|
||||
AnswerWith("Write using commit log as template")
|
||||
as.StubPrompt("Is this a prerelease?").AnswerWith(false)
|
||||
as.StubPrompt("Submit?").AnswerWith("Publish release")
|
||||
prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) {
|
||||
pm.SelectFunc = func(p, d string, opts []string) (int, error) {
|
||||
switch p {
|
||||
case "Release notes":
|
||||
prompter.AssertOptions(t, []string{"Write my own", "Write using commit log as template", "Leave blank"}, opts)
|
||||
return prompter.IndexFor(opts, "Write using commit log as template")
|
||||
case "Submit?":
|
||||
prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts)
|
||||
return prompter.IndexFor(opts, "Publish release")
|
||||
default:
|
||||
return -1, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
pm.InputFunc = func(p, d string) (string, error) {
|
||||
switch p {
|
||||
case "Title (optional)":
|
||||
return d, nil
|
||||
default:
|
||||
return "", prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
pm.ConfirmFunc = func(p string, d bool) (bool, error) {
|
||||
switch p {
|
||||
case "Is this a prerelease?":
|
||||
return false, nil
|
||||
default:
|
||||
return false, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
},
|
||||
runStubs: func(rs *run.CommandStubber) {
|
||||
rs.Register(`git tag --list`, 1, "")
|
||||
|
|
@ -984,13 +1073,35 @@ func Test_createRun_interactive(t *testing.T) {
|
|||
opts: &CreateOptions{
|
||||
TagName: "v1.2.3",
|
||||
},
|
||||
askStubs: func(as *prompt.AskStubber) {
|
||||
as.StubPrompt("Title (optional)").AnswerDefault()
|
||||
as.StubPrompt("Release notes").
|
||||
AssertOptions([]string{"Write my own", "Write using git tag message as template", "Leave blank"}).
|
||||
AnswerWith("Write using git tag message as template")
|
||||
as.StubPrompt("Is this a prerelease?").AnswerWith(false)
|
||||
as.StubPrompt("Submit?").AnswerWith("Publish release")
|
||||
prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) {
|
||||
pm.SelectFunc = func(p, d string, opts []string) (int, error) {
|
||||
switch p {
|
||||
case "Release notes":
|
||||
prompter.AssertOptions(t, []string{"Write my own", "Write using git tag message as template", "Leave blank"}, opts)
|
||||
return prompter.IndexFor(opts, "Write using git tag message as template")
|
||||
case "Submit?":
|
||||
prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts)
|
||||
return prompter.IndexFor(opts, "Publish release")
|
||||
default:
|
||||
return -1, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
pm.InputFunc = func(p, d string) (string, error) {
|
||||
switch p {
|
||||
case "Title (optional)":
|
||||
return d, nil
|
||||
default:
|
||||
return "", prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
pm.ConfirmFunc = func(p string, d bool) (bool, error) {
|
||||
switch p {
|
||||
case "Is this a prerelease?":
|
||||
return false, nil
|
||||
default:
|
||||
return false, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
},
|
||||
runStubs: func(rs *run.CommandStubber) {
|
||||
rs.Register(`git tag --list`, 0, "hello from annotated tag")
|
||||
|
|
@ -1036,13 +1147,35 @@ func Test_createRun_interactive(t *testing.T) {
|
|||
TagName: "v1.2.3",
|
||||
Target: "main",
|
||||
},
|
||||
askStubs: func(as *prompt.AskStubber) {
|
||||
as.StubPrompt("Title (optional)").AnswerWith("")
|
||||
as.StubPrompt("Release notes").
|
||||
AssertOptions([]string{"Write my own", "Write using generated notes as template", "Write using git tag message as template", "Leave blank"}).
|
||||
AnswerWith("Leave blank")
|
||||
as.StubPrompt("Is this a prerelease?").AnswerWith(false)
|
||||
as.StubPrompt("Submit?").AnswerWith("Publish release")
|
||||
prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) {
|
||||
pm.SelectFunc = func(p, d string, opts []string) (int, error) {
|
||||
switch p {
|
||||
case "Release notes":
|
||||
prompter.AssertOptions(t, []string{"Write my own", "Write using generated notes as template", "Write using git tag message as template", "Leave blank"}, opts)
|
||||
return prompter.IndexFor(opts, "Leave blank")
|
||||
case "Submit?":
|
||||
prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts)
|
||||
return prompter.IndexFor(opts, "Publish release")
|
||||
default:
|
||||
return -1, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
pm.InputFunc = func(p, d string) (string, error) {
|
||||
switch p {
|
||||
case "Title (optional)":
|
||||
return d, nil
|
||||
default:
|
||||
return "", prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
pm.ConfirmFunc = func(p string, d bool) (bool, error) {
|
||||
switch p {
|
||||
case "Is this a prerelease?":
|
||||
return false, nil
|
||||
default:
|
||||
return false, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
},
|
||||
runStubs: func(rs *run.CommandStubber) {
|
||||
rs.Register(`git tag --list`, 0, "tag exists")
|
||||
|
|
@ -1061,6 +1194,7 @@ func Test_createRun_interactive(t *testing.T) {
|
|||
},
|
||||
wantParams: map[string]interface{}{
|
||||
"draft": false,
|
||||
"name": "generated name",
|
||||
"prerelease": false,
|
||||
"tag_name": "v1.2.3",
|
||||
"target_commitish": "main",
|
||||
|
|
@ -1073,13 +1207,35 @@ func Test_createRun_interactive(t *testing.T) {
|
|||
TagName: "v1.2.3",
|
||||
NotesStartTag: "v1.1.0",
|
||||
},
|
||||
askStubs: func(as *prompt.AskStubber) {
|
||||
as.StubPrompt("Title (optional)").AnswerDefault()
|
||||
as.StubPrompt("Release notes").
|
||||
AssertOptions([]string{"Write my own", "Write using generated notes as template", "Leave blank"}).
|
||||
AnswerWith("Write using generated notes as template")
|
||||
as.StubPrompt("Is this a prerelease?").AnswerWith(false)
|
||||
as.StubPrompt("Submit?").AnswerWith("Publish release")
|
||||
prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) {
|
||||
pm.SelectFunc = func(p, d string, opts []string) (int, error) {
|
||||
switch p {
|
||||
case "Release notes":
|
||||
prompter.AssertOptions(t, []string{"Write my own", "Write using generated notes as template", "Leave blank"}, opts)
|
||||
return prompter.IndexFor(opts, "Write using generated notes as template")
|
||||
case "Submit?":
|
||||
prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts)
|
||||
return prompter.IndexFor(opts, "Publish release")
|
||||
default:
|
||||
return -1, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
pm.InputFunc = func(p, d string) (string, error) {
|
||||
switch p {
|
||||
case "Title (optional)":
|
||||
return d, nil
|
||||
default:
|
||||
return "", prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
pm.ConfirmFunc = func(p string, d bool) (bool, error) {
|
||||
switch p {
|
||||
case "Is this a prerelease?":
|
||||
return false, nil
|
||||
default:
|
||||
return false, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
},
|
||||
runStubs: func(rs *run.CommandStubber) {
|
||||
rs.Register(`git tag --list`, 1, "")
|
||||
|
|
@ -1117,13 +1273,35 @@ func Test_createRun_interactive(t *testing.T) {
|
|||
TagName: "v1.2.3",
|
||||
NotesStartTag: "v1.1.0",
|
||||
},
|
||||
askStubs: func(as *prompt.AskStubber) {
|
||||
as.StubPrompt("Title (optional)").AnswerDefault()
|
||||
as.StubPrompt("Release notes").
|
||||
AssertOptions([]string{"Write my own", "Write using commit log as template", "Leave blank"}).
|
||||
AnswerWith("Write using commit log as template")
|
||||
as.StubPrompt("Is this a prerelease?").AnswerWith(false)
|
||||
as.StubPrompt("Submit?").AnswerWith("Publish release")
|
||||
prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) {
|
||||
pm.SelectFunc = func(p, d string, opts []string) (int, error) {
|
||||
switch p {
|
||||
case "Release notes":
|
||||
prompter.AssertOptions(t, []string{"Write my own", "Write using commit log as template", "Leave blank"}, opts)
|
||||
return prompter.IndexFor(opts, "Write using commit log as template")
|
||||
case "Submit?":
|
||||
prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts)
|
||||
return prompter.IndexFor(opts, "Publish release")
|
||||
default:
|
||||
return -1, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
pm.InputFunc = func(p, d string) (string, error) {
|
||||
switch p {
|
||||
case "Title (optional)":
|
||||
return d, nil
|
||||
default:
|
||||
return "", prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
pm.ConfirmFunc = func(p string, d bool) (bool, error) {
|
||||
switch p {
|
||||
case "Is this a prerelease?":
|
||||
return false, nil
|
||||
default:
|
||||
return false, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
},
|
||||
runStubs: func(rs *run.CommandStubber) {
|
||||
rs.Register(`git tag --list`, 1, "")
|
||||
|
|
@ -1153,13 +1331,35 @@ func Test_createRun_interactive(t *testing.T) {
|
|||
TagName: "v1.2.3",
|
||||
VerifyTag: true,
|
||||
},
|
||||
askStubs: func(as *prompt.AskStubber) {
|
||||
as.StubPrompt("Title (optional)").AnswerWith("")
|
||||
as.StubPrompt("Release notes").
|
||||
AssertOptions([]string{"Write my own", "Write using generated notes as template", "Write using git tag message as template", "Leave blank"}).
|
||||
AnswerWith("Leave blank")
|
||||
as.StubPrompt("Is this a prerelease?").AnswerWith(false)
|
||||
as.StubPrompt("Submit?").AnswerWith("Publish release")
|
||||
prompterStubs: func(t *testing.T, pm *prompter.PrompterMock) {
|
||||
pm.SelectFunc = func(p, d string, opts []string) (int, error) {
|
||||
switch p {
|
||||
case "Release notes":
|
||||
prompter.AssertOptions(t, []string{"Write my own", "Write using generated notes as template", "Write using git tag message as template", "Leave blank"}, opts)
|
||||
return prompter.IndexFor(opts, "Leave blank")
|
||||
case "Submit?":
|
||||
prompter.AssertOptions(t, []string{"Publish release", "Save as draft", "Cancel"}, opts)
|
||||
return prompter.IndexFor(opts, "Publish release")
|
||||
default:
|
||||
return -1, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
pm.InputFunc = func(p, d string) (string, error) {
|
||||
switch p {
|
||||
case "Title (optional)":
|
||||
return d, nil
|
||||
default:
|
||||
return "", prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
pm.ConfirmFunc = func(p string, d bool) (bool, error) {
|
||||
switch p {
|
||||
case "Is this a prerelease?":
|
||||
return false, nil
|
||||
default:
|
||||
return false, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
}
|
||||
},
|
||||
runStubs: func(rs *run.CommandStubber) {
|
||||
rs.Register(`git tag --list`, 0, "tag exists")
|
||||
|
|
@ -1180,6 +1380,7 @@ func Test_createRun_interactive(t *testing.T) {
|
|||
},
|
||||
wantParams: map[string]interface{}{
|
||||
"draft": false,
|
||||
"name": "generated name",
|
||||
"prerelease": false,
|
||||
"tag_name": "v1.2.3",
|
||||
},
|
||||
|
|
@ -1227,11 +1428,11 @@ func Test_createRun_interactive(t *testing.T) {
|
|||
tt.opts.GitClient = &git.Client{GitPath: "some/path/git"}
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
//nolint:staticcheck // SA1019: prompt.NewAskStubber is deprecated: use PrompterMock
|
||||
as := prompt.NewAskStubber(t)
|
||||
if tt.askStubs != nil {
|
||||
tt.askStubs(as)
|
||||
pm := &prompter.PrompterMock{}
|
||||
if tt.prompterStubs != nil {
|
||||
tt.prompterStubs(t, pm)
|
||||
}
|
||||
tt.opts.Prompter = pm
|
||||
|
||||
rs, teardown := run.Stub()
|
||||
defer teardown(t)
|
||||
|
|
|
|||
|
|
@ -4,20 +4,23 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/cli/cli/v2/api"
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
"github.com/cli/cli/v2/pkg/cmd/release/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 {
|
||||
Confirm(string, bool) (bool, error)
|
||||
}
|
||||
|
||||
type DeleteAssetOptions struct {
|
||||
HttpClient func() (*http.Client, error)
|
||||
IO *iostreams.IOStreams
|
||||
BaseRepo func() (ghrepo.Interface, error)
|
||||
Prompter iprompter
|
||||
|
||||
TagName string
|
||||
SkipConfirm bool
|
||||
|
|
@ -28,6 +31,7 @@ func NewCmdDeleteAsset(f *cmdutil.Factory, runF func(*DeleteAssetOptions) error)
|
|||
opts := &DeleteAssetOptions{
|
||||
IO: f.IOStreams,
|
||||
HttpClient: f.HttpClient,
|
||||
Prompter: f.Prompter,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
|
@ -68,12 +72,9 @@ func deleteAssetRun(opts *DeleteAssetOptions) error {
|
|||
}
|
||||
|
||||
if !opts.SkipConfirm && opts.IO.CanPrompt() {
|
||||
var confirmed bool
|
||||
//nolint:staticcheck // SA1019: prompt.SurveyAskOne is deprecated: use Prompter
|
||||
err := prompt.SurveyAskOne(&survey.Confirm{
|
||||
Message: fmt.Sprintf("Delete asset %s in release %s in %s?", opts.AssetName, release.TagName, ghrepo.FullName(baseRepo)),
|
||||
Default: true,
|
||||
}, &confirmed)
|
||||
confirmed, err := opts.Prompter.Confirm(
|
||||
fmt.Sprintf("Delete asset %s in release %s in %s?", opts.AssetName, release.TagName, ghrepo.FullName(baseRepo)),
|
||||
true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
"github.com/cli/cli/v2/internal/prompter"
|
||||
"github.com/cli/cli/v2/pkg/cmdutil"
|
||||
"github.com/cli/cli/v2/pkg/httpmock"
|
||||
"github.com/cli/cli/v2/pkg/iostreams"
|
||||
|
|
@ -98,13 +99,32 @@ func Test_NewCmdDeleteAsset(t *testing.T) {
|
|||
|
||||
func Test_deleteAssetRun(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
isTTY bool
|
||||
opts DeleteAssetOptions
|
||||
wantErr string
|
||||
wantStdout string
|
||||
wantStderr string
|
||||
name string
|
||||
isTTY bool
|
||||
opts DeleteAssetOptions
|
||||
prompterStubs func(*prompter.PrompterMock)
|
||||
wantErr string
|
||||
wantStdout string
|
||||
wantStderr string
|
||||
}{
|
||||
{
|
||||
name: "interactive confirm",
|
||||
isTTY: true,
|
||||
opts: DeleteAssetOptions{
|
||||
TagName: "v1.2.3",
|
||||
AssetName: "test-asset",
|
||||
},
|
||||
prompterStubs: func(pm *prompter.PrompterMock) {
|
||||
pm.ConfirmFunc = func(p string, d bool) (bool, error) {
|
||||
if p == "Delete asset test-asset in release v1.2.3 in OWNER/REPO?" {
|
||||
return true, nil
|
||||
}
|
||||
return false, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
},
|
||||
wantStdout: ``,
|
||||
wantStderr: "✓ Deleted asset test-asset from release v1.2.3\n",
|
||||
},
|
||||
{
|
||||
name: "skipping confirmation",
|
||||
isTTY: true,
|
||||
|
|
@ -150,7 +170,13 @@ func Test_deleteAssetRun(t *testing.T) {
|
|||
}`))
|
||||
fakeHTTP.Register(httpmock.REST("DELETE", "repos/OWNER/REPO/releases/assets/1"), httpmock.StatusStringResponse(204, ""))
|
||||
|
||||
pm := &prompter.PrompterMock{}
|
||||
if tt.prompterStubs != nil {
|
||||
tt.prompterStubs(pm)
|
||||
}
|
||||
|
||||
tt.opts.IO = ios
|
||||
tt.opts.Prompter = pm
|
||||
tt.opts.HttpClient = func() (*http.Client, error) {
|
||||
return &http.Client{Transport: fakeHTTP}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,21 +4,24 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/cli/cli/v2/api"
|
||||
"github.com/cli/cli/v2/internal/ghinstance"
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
"github.com/cli/cli/v2/pkg/cmd/release/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 {
|
||||
Confirm(string, bool) (bool, error)
|
||||
}
|
||||
|
||||
type DeleteOptions struct {
|
||||
HttpClient func() (*http.Client, error)
|
||||
IO *iostreams.IOStreams
|
||||
BaseRepo func() (ghrepo.Interface, error)
|
||||
Prompter iprompter
|
||||
|
||||
TagName string
|
||||
SkipConfirm bool
|
||||
|
|
@ -29,6 +32,7 @@ func NewCmdDelete(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co
|
|||
opts := &DeleteOptions{
|
||||
IO: f.IOStreams,
|
||||
HttpClient: f.HttpClient,
|
||||
Prompter: f.Prompter,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
|
@ -71,12 +75,8 @@ func deleteRun(opts *DeleteOptions) error {
|
|||
}
|
||||
|
||||
if !opts.SkipConfirm && opts.IO.CanPrompt() {
|
||||
var confirmed bool
|
||||
//nolint:staticcheck // SA1019: prompt.SurveyAskOne is deprecated: use Prompter
|
||||
err := prompt.SurveyAskOne(&survey.Confirm{
|
||||
Message: fmt.Sprintf("Delete release %s in %s?", release.TagName, ghrepo.FullName(baseRepo)),
|
||||
Default: true,
|
||||
}, &confirmed)
|
||||
confirmed, err := opts.Prompter.Confirm(
|
||||
fmt.Sprintf("Delete release %s in %s?", release.TagName, ghrepo.FullName(baseRepo)), true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
"github.com/cli/cli/v2/internal/prompter"
|
||||
"github.com/cli/cli/v2/pkg/cmdutil"
|
||||
"github.com/cli/cli/v2/pkg/httpmock"
|
||||
"github.com/cli/cli/v2/pkg/iostreams"
|
||||
|
|
@ -104,13 +105,31 @@ func Test_NewCmdDelete(t *testing.T) {
|
|||
|
||||
func Test_deleteRun(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
isTTY bool
|
||||
opts DeleteOptions
|
||||
wantErr string
|
||||
wantStdout string
|
||||
wantStderr string
|
||||
name string
|
||||
isTTY bool
|
||||
opts DeleteOptions
|
||||
prompterStubs func(*prompter.PrompterMock)
|
||||
wantErr string
|
||||
wantStdout string
|
||||
wantStderr string
|
||||
}{
|
||||
{
|
||||
name: "interactive confirm",
|
||||
isTTY: true,
|
||||
opts: DeleteOptions{
|
||||
TagName: "v1.2.3",
|
||||
},
|
||||
wantStdout: "",
|
||||
wantStderr: "✓ Deleted release v1.2.3\n! Note that the v1.2.3 git tag still remains in the repository\n",
|
||||
prompterStubs: func(pm *prompter.PrompterMock) {
|
||||
pm.ConfirmFunc = func(p string, d bool) (bool, error) {
|
||||
if p == "Delete release v1.2.3 in OWNER/REPO?" {
|
||||
return true, nil
|
||||
}
|
||||
return false, prompter.NoSuchPromptErr(p)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "skipping confirmation",
|
||||
isTTY: true,
|
||||
|
|
@ -168,6 +187,11 @@ func Test_deleteRun(t *testing.T) {
|
|||
ios.SetStdinTTY(tt.isTTY)
|
||||
ios.SetStderrTTY(tt.isTTY)
|
||||
|
||||
pm := &prompter.PrompterMock{}
|
||||
if tt.prompterStubs != nil {
|
||||
tt.prompterStubs(pm)
|
||||
}
|
||||
|
||||
fakeHTTP := &httpmock.Registry{}
|
||||
fakeHTTP.Register(httpmock.REST("GET", "repos/OWNER/REPO/releases/tags/v1.2.3"), httpmock.StringResponse(`{
|
||||
"tag_name": "v1.2.3",
|
||||
|
|
@ -179,6 +203,7 @@ func Test_deleteRun(t *testing.T) {
|
|||
fakeHTTP.Register(httpmock.REST("DELETE", "repos/OWNER/REPO/git/refs/tags/v1.2.3"), httpmock.StatusStringResponse(204, ""))
|
||||
|
||||
tt.opts.IO = ios
|
||||
tt.opts.Prompter = pm
|
||||
tt.opts.HttpClient = func() (*http.Client, error) {
|
||||
return &http.Client{Transport: fakeHTTP}, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue