Skip to content

Commit

Permalink
Merge pull request #3606 from ActiveState/mitchell/dx-3166-3
Browse files Browse the repository at this point in the history
Show prompts, even if running in non-interactive or force mode.
  • Loading branch information
mitchell-as authored Nov 22, 2024
2 parents c10ecc4 + 878565b commit bcc739d
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 63 deletions.
60 changes: 60 additions & 0 deletions internal/prompt/overrides.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down
157 changes: 94 additions & 63 deletions internal/prompt/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)
Expand All @@ -153,45 +156,55 @@ 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
}

// Select prompts the user to select one entry from multiple choices.
// 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
Expand All @@ -200,55 +213,64 @@ 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
}

// Confirm prompts user for yes or no response.
// 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")
Expand All @@ -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
}

Expand Down

0 comments on commit bcc739d

Please sign in to comment.