diff --git a/internal/prompt/overrides.go b/internal/prompt/overrides.go index d1d94c4a32..230f7b86c0 100644 --- a/internal/prompt/overrides.go +++ b/internal/prompt/overrides.go @@ -6,6 +6,34 @@ import ( type Select struct { *survey.Select + nonInteractiveChoice *string +} + +func (s *Select) Prompt() (interface{}, error) { + if s.nonInteractiveChoice == nil { + return s.Select.Prompt() + } + + idx := 0 + for i, choice := range s.Select.Options { + if choice == *s.nonInteractiveChoice { + idx = i + break + } + } + + err := s.Select.Render( + survey.SelectQuestionTemplate, + survey.SelectTemplateData{ + Select: *s.Select, + PageEntries: s.Select.Options, + SelectedIndex: idx, + }) + if err != nil { + return nil, err + } + + return *s.nonInteractiveChoice, nil } func (s *Select) Cleanup(interface{}) error { @@ -15,6 +43,22 @@ func (s *Select) Cleanup(interface{}) error { type Input struct { *survey.Input + nonInteractiveResponse *string +} + +func (i *Input) Prompt() (interface{}, error) { + if i.nonInteractiveResponse == nil { + return i.Input.Prompt() + } + + err := i.Input.Render( + survey.InputQuestionTemplate, + survey.InputTemplateData{Input: *i.Input}) + if err != nil { + return nil, err + } + + return *i.nonInteractiveResponse, nil } func (i *Input) Cleanup(val interface{}) error { @@ -33,6 +77,22 @@ func (i *Password) Cleanup(val interface{}) error { type Confirm struct { *survey.Confirm + nonInteractiveChoice *bool +} + +func (s *Confirm) Prompt() (interface{}, error) { + if s.nonInteractiveChoice == nil { + return s.Confirm.Prompt() + } + + err := s.Confirm.Render( + survey.ConfirmQuestionTemplate, + survey.ConfirmTemplateData{Confirm: *s.Confirm}) + if err != nil { + return nil, err + } + + return *s.nonInteractiveChoice, nil } func (s *Confirm) Cleanup(interface{}) error { diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index ae18a55cfe..d1db14cb01 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -106,26 +106,6 @@ func interactiveInputError(message string) error { // If the prompt is non-interactive, it returns defaultResponse. // If the prompt is forced, it returns forcedResponse if not nil, or defaultResponse. func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string, forcedResponse *string, validator ValidatorFunc, flags ...ValidatorFlag) (string, error) { - if p.isForced { - response := forcedResponse - if response == nil { - response = defaultResponse - } - if response != nil { - p.out.Notice(locale.Tr("prompt_using_force", *response)) - return *response, nil - } - return "", ErrNoForceOption - } - - if !p.isInteractive { - if defaultResponse != nil { - p.out.Notice(locale.Tr("prompt_using_non_interactive", *defaultResponse)) - return *defaultResponse, nil - } - return "", interactiveInputError(message) - } - var response string flagValidators, err := processValidators(flags) if err != nil { @@ -139,6 +119,29 @@ func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string p.out.Notice(output.Emphasize(title)) } + var nonInteractiveResponse *string + + if p.isForced { + nonInteractiveResponse = forcedResponse + if nonInteractiveResponse == nil { + nonInteractiveResponse = defaultResponse + } + if nonInteractiveResponse == nil { + return "", ErrNoForceOption + } + } + + if !p.isInteractive { + nonInteractiveResponse = defaultResponse + if nonInteractiveResponse == nil { + return "", interactiveInputError(message) + } + } + + if p.out.Type().IsStructured() { + return *nonInteractiveResponse, nil + } + // We handle defaults more clearly than the survey package can if defaultResponse != nil && *defaultResponse != "" { v, err := p.Select("", formatMessage(message, !p.out.Config().Colored), []string{*defaultResponse, locale.Tl("prompt_custom", "Other ..")}, defaultResponse, forcedResponse) @@ -153,11 +156,18 @@ func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string err = survey.AskOne(&Input{&survey.Input{ Message: formatMessage(message, !p.out.Config().Colored), - }}, &response, validator) + }, nonInteractiveResponse}, &response, validator) if err != nil { return "", locale.NewInputError(err.Error()) } + switch { + case p.isForced: + p.out.Notice(locale.Tr("prompt_using_force", response)) + case !p.isInteractive: + p.out.Notice(locale.Tr("prompt_using_non_interactive", response)) + } + return response, nil } @@ -165,33 +175,36 @@ func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string // If the prompt is non-interactive, it returns defaultChoice. // If the prompt is forced, it returns forcedChoice if not nil, or defaultChoice. func (p *Prompt) Select(title, message string, choices []string, defaultChoice *string, forcedChoice *string) (string, error) { + if title != "" { + p.out.Notice(output.Emphasize(title)) + } + + var defChoice string + if defaultChoice != nil { + defChoice = *defaultChoice + } + + var nonInteractiveChoice *string + if p.isForced { - choice := forcedChoice - if choice == nil { - choice = defaultChoice + nonInteractiveChoice = forcedChoice + if nonInteractiveChoice == nil { + nonInteractiveChoice = defaultChoice } - if choice != nil { - p.out.Notice(locale.Tr("prompt_using_force", *choice)) - return *choice, nil + if nonInteractiveChoice == nil { + return "", ErrNoForceOption } - return "", ErrNoForceOption } if !p.isInteractive { - if defaultChoice != nil { - p.out.Notice(locale.Tr("prompt_using_non_interactive", *defaultChoice)) - return *defaultChoice, nil + nonInteractiveChoice = defaultChoice + if nonInteractiveChoice == nil { + return "", interactiveInputError(message) } - return "", interactiveInputError(message) } - if title != "" { - p.out.Notice(output.Emphasize(title)) - } - - var defChoice string - if defaultChoice != nil { - defChoice = *defaultChoice + if p.out.Type().IsStructured() { + return *nonInteractiveChoice, nil } var response string @@ -200,10 +213,18 @@ func (p *Prompt) Select(title, message string, choices []string, defaultChoice * Options: choices, Default: defChoice, FilterFn: func(input string, choices []string) []string { return choices }, // no filter - }}, &response, nil) + }, nonInteractiveChoice}, &response, nil) if err != nil { return "", locale.NewInputError(err.Error()) } + + switch { + case p.isForced: + p.out.Notice(locale.Tr("prompt_using_force", response)) + case !p.isInteractive: + p.out.Notice(locale.Tr("prompt_using_non_interactive", response)) + } + return response, nil } @@ -211,44 +232,45 @@ func (p *Prompt) Select(title, message string, choices []string, defaultChoice * // If the prompt is non-interactive, it returns defaultChoice. // If the prompt is forced, it returns forcedChoice if not nil, or defaultChoice. func (p *Prompt) Confirm(title, message string, defaultChoice *bool, forcedChoice *bool) (bool, error) { + p.analytics.EventWithLabel(constants.CatPrompt, title, "present") + + if title != "" { + p.out.Notice(output.Emphasize(title)) + } + + var defChoice bool + if defaultChoice != nil { + defChoice = *defaultChoice + } + + var nonInteractiveChoice *bool + if p.isForced { - choice := forcedChoice - if choice == nil { - choice = defaultChoice + nonInteractiveChoice = forcedChoice + if nonInteractiveChoice == nil { + nonInteractiveChoice = defaultChoice } - if choice != nil { - p.out.Notice(locale.T("prompt_continue_force")) - return *choice, nil + if nonInteractiveChoice == nil { + return false, ErrNoForceOption } - return false, ErrNoForceOption } if !p.isInteractive { - if defaultChoice != nil { - if *defaultChoice { - p.out.Notice(locale.T("prompt_continue_non_interactive")) - return true, nil - } - return false, locale.NewInputError("prompt_abort_non_interactive") + nonInteractiveChoice = defaultChoice + if nonInteractiveChoice == nil { + return false, interactiveInputError(message) } - return false, interactiveInputError(message) } - if title != "" { - p.out.Notice(output.Emphasize(title)) - } - - p.analytics.EventWithLabel(constants.CatPrompt, title, "present") - var defChoice bool - if defaultChoice != nil { - defChoice = *defaultChoice + if p.out.Type().IsStructured() { + return *nonInteractiveChoice, nil } var resp bool err := survey.AskOne(&Confirm{&survey.Confirm{ Message: formatMessage(strings.TrimSuffix(message, "\n"), !p.out.Config().Colored), Default: defChoice, - }}, &resp, nil) + }, nonInteractiveChoice}, &resp, nil) if err != nil { if err == terminal.InterruptErr { p.analytics.EventWithLabel(constants.CatPrompt, title, "interrupt") @@ -257,6 +279,15 @@ func (p *Prompt) Confirm(title, message string, defaultChoice *bool, forcedChoic } p.analytics.EventWithLabel(constants.CatPrompt, title, translateConfirm(resp)) + switch { + case p.isForced: + p.out.Notice(locale.T("prompt_continue_force")) + case !p.isInteractive && resp: + p.out.Notice(locale.T("prompt_continue_non_interactive")) + case !p.isInteractive && !resp: + return false, locale.NewInputError("prompt_abort_non_interactive") + } + return resp, nil }