Use custom prompter for bundle template inputs (#663)

## Changes
Prompt UI glitches often. We are switching to a custom implementation of
a simple prompter which is much more stable.
This also allows new lines in prompts which has been an ask by the
mlflow team.

## Tests
Tested manually
This commit is contained in:
shreyas-goenka 2023-08-15 16:50:20 +02:00 committed by GitHub
parent 878bb6deae
commit 61b103318f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 50 additions and 22 deletions

View File

@ -27,7 +27,7 @@ func (m *delete) Apply(ctx context.Context, b *bundle.Bundle) error {
red := color.New(color.FgRed).SprintFunc()
if !b.AutoApprove {
proceed, err := cmdio.Ask(ctx, fmt.Sprintf("\n%s and all files in it will be %s Proceed?", b.Config.Workspace.RootPath, red("deleted permanently!")))
proceed, err := cmdio.AskYesOrNo(ctx, fmt.Sprintf("\n%s and all files in it will be %s Proceed?", b.Config.Workspace.RootPath, red("deleted permanently!")))
if err != nil {
return err
}

View File

@ -89,7 +89,7 @@ func (w *destroy) Apply(ctx context.Context, b *bundle.Bundle) error {
// Ask for confirmation, if needed
if !b.Plan.ConfirmApply {
red := color.New(color.FgRed).SprintFunc()
b.Plan.ConfirmApply, err = cmdio.Ask(ctx, fmt.Sprintf("\nThis will permanently %s resources! Proceed?", red("destroy")))
b.Plan.ConfirmApply, err = cmdio.AskYesOrNo(ctx, fmt.Sprintf("\nThis will permanently %s resources! Proceed?", red("destroy")))
if err != nil {
return err
}

View File

@ -7,6 +7,7 @@ import (
"fmt"
"io"
"os"
"strings"
"github.com/databricks/cli/libs/flags"
)
@ -74,33 +75,64 @@ func LogError(ctx context.Context, err error) {
})
}
func Ask(ctx context.Context, question string) (bool, error) {
func Ask(ctx context.Context, question, defaultVal string) (string, error) {
logger, ok := FromContext(ctx)
if !ok {
logger = Default()
}
return logger.Ask(question)
return logger.Ask(question, defaultVal)
}
func (l *Logger) Ask(question string) (bool, error) {
if l.Mode == flags.ModeJson {
return false, fmt.Errorf("question prompts are not supported in json mode")
func AskYesOrNo(ctx context.Context, question string) (bool, error) {
logger, ok := FromContext(ctx)
if !ok {
logger = Default()
}
// Add acceptable answers to the question prompt.
question += ` [y/n]:`
l.Writer.Write([]byte(question))
ans, err := l.Reader.ReadString('\n')
question += ` [y/n]`
// Ask the question
ans, err := logger.Ask(question, "")
if err != nil {
return false, err
}
if ans == "y\n" {
if ans == "y" {
return true, nil
} else {
return false, nil
}
return false, nil
}
func (l *Logger) Ask(question string, defaultVal string) (string, error) {
if l.Mode == flags.ModeJson {
return "", fmt.Errorf("question prompts are not supported in json mode")
}
// Add default value to question prompt.
if defaultVal != "" {
question += fmt.Sprintf(` [%s]`, defaultVal)
}
question += `: `
// print prompt
_, err := l.Writer.Write([]byte(question))
if err != nil {
return "", err
}
// read user input. Trim new line characters
ans, err := l.Reader.ReadString('\n')
if err != nil {
return "", err
}
ans = strings.Trim(ans, "\n\r")
// Return default value if user just presses enter
if ans == "" {
return defaultVal, nil
}
return ans, nil
}
func (l *Logger) writeJson(event Event) {

View File

@ -9,6 +9,6 @@ import (
func TestAskFailedInJsonMode(t *testing.T) {
l := NewLogger(flags.ModeJson)
_, err := l.Ask("What is your spirit animal?")
_, err := l.Ask("What is your spirit animal?", "")
assert.ErrorContains(t, err, "question prompts are not supported in json mode")
}

View File

@ -123,22 +123,18 @@ func (c *config) promptForValues() error {
continue
}
// Initialize Prompt dialog
var err error
prompt := cmdio.Prompt(c.ctx)
prompt.Label = property.Description
prompt.AllowEdit = true
// Compute default value to display by converting it to a string
var defaultVal string
var err error
if property.Default != nil {
prompt.Default, err = toString(property.Default, property.Type)
defaultVal, err = toString(property.Default, property.Type)
if err != nil {
return err
}
}
// Get user input by running the prompt
userInput, err := prompt.Run()
userInput, err := cmdio.Ask(c.ctx, property.Description, defaultVal)
if err != nil {
return err
}