Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use a simpler prompt implementation when we lack a terminal #10144

Merged
merged 1 commit into from
Jan 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pkg/compose/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (s *composeService) Remove(ctx context.Context, projectName string, options
if options.Force {
fmt.Fprintln(s.stdout(), msg)
} else {
confirm, err := prompt.User{}.Confirm(msg, false)
confirm, err := prompt.NewPrompt(s.stdin(), s.stdout()).Confirm(msg, false)
if err != nil {
return err
}
Expand Down
87 changes: 55 additions & 32 deletions pkg/prompt/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,42 +17,58 @@
package prompt

import (
"fmt"
"io"

"github.com/AlecAivazis/survey/v2"
"github.com/docker/cli/cli/streams"
"github.com/docker/compose/v2/pkg/utils"
)

//go:generate mockgen -destination=./prompt_mock.go -self_package "github.com/docker/compose/v2/pkg/prompt" -package=prompt . UI

// UI - prompt user input
type UI interface {
Select(message string, options []string) (int, error)
Input(message string, defaultValue string) (string, error)
Confirm(message string, defaultValue bool) (bool, error)
Password(message string) (string, error)
}

// User - aggregates prompt methods
type User struct{}

// Select - displays a list
func (u User) Select(message string, options []string) (int, error) {
qs := &survey.Select{
Message: message,
Options: options,
func NewPrompt(stdin *streams.In, stdout *streams.Out) UI {
if stdin.IsTerminal() {
return User{stdin: streamsFileReader{stdin}, stdout: streamsFileWriter{stdout}}
}
var selected int
err := survey.AskOne(qs, &selected, nil)
return selected, err
return Pipe{stdin: stdin, stdout: stdout}
}

// Input text with default value
func (u User) Input(message string, defaultValue string) (string, error) {
qs := &survey.Input{
Message: message,
Default: defaultValue,
}
var s string
err := survey.AskOne(qs, &s, nil)
return s, err
// User - in a terminal
type User struct {
stdout streamsFileWriter
stdin streamsFileReader
}

// adapt streams.Out to terminal.FileWriter
type streamsFileWriter struct {
stream *streams.Out
}

func (s streamsFileWriter) Write(p []byte) (n int, err error) {
return s.stream.Write(p)
}

func (s streamsFileWriter) Fd() uintptr {
return s.stream.FD()
}

// adapt streams.In to terminal.FileReader
type streamsFileReader struct {
stream *streams.In
}

func (s streamsFileReader) Read(p []byte) (n int, err error) {
return s.stream.Read(p)
}

func (s streamsFileReader) Fd() uintptr {
return s.stream.FD()
}

// Confirm asks for yes or no input
Expand All @@ -62,17 +78,24 @@ func (u User) Confirm(message string, defaultValue bool) (bool, error) {
Default: defaultValue,
}
var b bool
err := survey.AskOne(qs, &b, nil)
err := survey.AskOne(qs, &b, func(options *survey.AskOptions) error {
options.Stdio.In = u.stdin
options.Stdio.Out = u.stdout
return nil
})
return b, err
}

// Password implements a text input with masked characters.
func (u User) Password(message string) (string, error) {
qs := &survey.Password{
Message: message,
}
var p string
err := survey.AskOne(qs, &p, nil)
return p, err
// Pipe - aggregates prompt methods
type Pipe struct {
stdout io.Writer
stdin io.Reader
}

// Confirm asks for yes or no input
func (u Pipe) Confirm(message string, defaultValue bool) (bool, error) {
fmt.Fprint(u.stdout, message)
var answer string
fmt.Scanln(&answer)
return utils.StringToBool(answer), nil
}
6 changes: 5 additions & 1 deletion pkg/utils/stringutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ func StringContains(array []string, needle string) bool {

// StringToBool converts a string to a boolean ignoring errors
func StringToBool(s string) bool {
b, _ := strconv.ParseBool(strings.ToLower(strings.TrimSpace(s)))
s = strings.ToLower(strings.TrimSpace(s))
if s == "y" {
return true
}
b, _ := strconv.ParseBool(s)
return b
}