Merge pull request #6711 from cli/release-prompter

port release command to prompter
This commit is contained in:
Nate Smith 2022-12-12 12:09:29 -06:00 committed by GitHub
commit 67e5baea09
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 399 additions and 166 deletions

View file

@ -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)
}

View file

@ -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

View file

@ -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)

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}