Skip to content

Commit ee56104

Browse files
committed
Added force options for other prompt methods and made --force a global flag.
1 parent 4113b3e commit ee56104

30 files changed

Lines changed: 161 additions & 180 deletions

File tree

cmd/state-installer/installer.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,17 @@ func (i *Installer) Install() (rerr error) {
5353
if err != nil {
5454
return errs.Wrap(err, "Could not determine if running as Windows administrator")
5555
}
56-
if isAdmin && !i.Params.force && !i.Params.isUpdate && !i.Params.nonInteractive {
57-
prompter := prompt.New(true, i.an)
58-
confirm, err := prompter.Confirm("", locale.T("installer_prompt_is_admin"), ptr.To(false), nil)
56+
if isAdmin && !i.Params.isUpdate {
57+
prompter := prompt.New(i.an)
58+
if i.Params.nonInteractive {
59+
prompter.SetInteractive(false)
60+
}
61+
if i.Params.force {
62+
prompter.SetForce(true)
63+
}
64+
confirm, err := prompter.Confirm("", locale.T("installer_prompt_is_admin"), ptr.To(false), ptr.To(true))
5965
if err != nil {
60-
return errs.Wrap(err, "Unable to confirm")
66+
return errs.Wrap(err, "Not confirmed")
6167
}
6268
if !confirm {
6369
return locale.NewInputError("installer_aborted", "Installation aborted by the user")

cmd/state-remote-installer/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ func main() {
116116
an = sync.New(anaConst.SrcStateRemoteInstaller, cfg, nil, out)
117117

118118
// Set up prompter
119-
prompter := prompt.New(true, an)
119+
prompter := prompt.New(an)
120120

121121
params := newParams()
122122
cmd := captain.NewCommand(
@@ -176,7 +176,7 @@ func execute(out output.Outputer, prompt prompt.Prompter, cfg *config.Instance,
176176
msg += locale.Tr("tos_disclaimer_prompt", constants.TermsOfServiceURLLatest)
177177
cont, err := prompt.Confirm(locale.Tr("install_remote_title"), msg, ptr.To(true), nil)
178178
if err != nil {
179-
return errs.Wrap(err, "Could not prompt for confirmation")
179+
return errs.Wrap(err, "Not confirmed")
180180
}
181181

182182
if !cont {

cmd/state/internal/cmdtree/clean.go

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,6 @@ func newCleanUninstallCommand(prime *primer.Values, globals *globalOptions) *cap
3636
Description: locale.Tl("flag_state_clean_uninstall_all", "Also delete all associated config and cache files"),
3737
Value: &params.All,
3838
},
39-
{
40-
Name: "force",
41-
Shorthand: "f",
42-
Description: locale.T("flag_state_clean_uninstall_force_description"),
43-
Value: &params.Force,
44-
},
4539
{
4640
// This option is only used by the Windows uninstall shortcut to ask the user if they wish
4741
// to delete everything or keep cache and config. The user is also asked to press Enter
@@ -62,8 +56,9 @@ func newCleanUninstallCommand(prime *primer.Values, globals *globalOptions) *cap
6256
if globals.NonInteractive {
6357
prime.Prompt().SetInteractive(false)
6458
}
65-
if params.Force {
66-
prime.Prompt().EnableForce()
59+
if globals.Force {
60+
prime.Prompt().SetForce(true)
61+
params.Force = true
6762
}
6863
return runner.Run(&params)
6964
},
@@ -96,26 +91,20 @@ func newCleanCacheCommand(prime *primer.Values, globals *globalOptions) *captain
9691
)
9792
}
9893

99-
func newCleanConfigCommand(prime *primer.Values) *captain.Command {
94+
func newCleanConfigCommand(prime *primer.Values, globals *globalOptions) *captain.Command {
10095
runner := clean.NewConfig(prime)
10196
params := clean.ConfigParams{}
10297
return captain.NewCommand(
10398
"config",
10499
locale.Tl("clean_config_title", "Cleaning Configuration"),
105100
locale.T("clean_config_description"),
106101
prime,
107-
[]*captain.Flag{
108-
{
109-
Name: "force",
110-
Shorthand: "f",
111-
Description: locale.T("flag_state_clean_config_force_description"),
112-
Value: &params.Force,
113-
},
114-
},
102+
[]*captain.Flag{},
115103
[]*captain.Argument{},
116104
func(ccmd *captain.Command, _ []string) error {
117-
if params.Force {
118-
prime.Prompt().EnableForce()
105+
if globals.Force {
106+
prime.Prompt().SetForce(true)
107+
params.Force = true
119108
}
120109
return runner.Run(&params)
121110
},

cmd/state/internal/cmdtree/cmdtree.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func New(prime *primer.Values, args ...string) *CmdTree {
8080
cleanCmd.AddChildren(
8181
newCleanUninstallCommand(prime, globals),
8282
newCleanCacheCommand(prime, globals),
83-
newCleanConfigCommand(prime),
83+
newCleanConfigCommand(prime, globals),
8484
)
8585

8686
deployCmd := newDeployCommand(prime)
@@ -95,7 +95,7 @@ func New(prime *primer.Values, args ...string) *CmdTree {
9595
eventsCmd := newEventsCommand(prime)
9696
eventsCmd.AddChildren(newEventsLogCommand(prime))
9797

98-
installCmd := newInstallCommand(prime)
98+
installCmd := newInstallCommand(prime, globals)
9999
uninstallCmd := newUninstallCommand(prime)
100100
importCmd := newImportCommand(prime, globals)
101101
searchCmd := newSearchCommand(prime)
@@ -234,6 +234,7 @@ type globalOptions struct {
234234
Output string
235235
Monochrome bool
236236
NonInteractive bool
237+
Force bool
237238
}
238239

239240
// Group instances are used to group command help output.
@@ -303,6 +304,12 @@ func newStateCommand(globals *globalOptions, prime *primer.Values) *captain.Comm
303304
Persist: true,
304305
Value: &globals.NonInteractive,
305306
},
307+
{
308+
Name: "force",
309+
Description: locale.T("flag_state_force_description"),
310+
Persist: true,
311+
Value: &globals.Force,
312+
},
306313
{
307314
Name: "version",
308315
Description: locale.T("flag_state_version_description"),

cmd/state/internal/cmdtree/packages.go

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,10 @@ func newPackagesCommand(prime *primer.Values) *captain.Command {
5050
return cmd
5151
}
5252

53-
func newInstallCommand(prime *primer.Values) *captain.Command {
53+
func newInstallCommand(prime *primer.Values, globals *globalOptions) *captain.Command {
5454
runner := install.New(prime, model.NamespacePackage)
5555

5656
params := install.Params{}
57-
force := false
5857

5958
var packagesRaw string
6059
cmd := captain.NewCommand(
@@ -68,11 +67,6 @@ func newInstallCommand(prime *primer.Values) *captain.Command {
6867
Description: locale.T("package_flag_ts_description"),
6968
Value: &params.Timestamp,
7069
},
71-
{
72-
Name: "force",
73-
Description: locale.Tl("package_flag_force_description", "Ignore security policy preventing packages with CVEs from being installed (not recommended)"),
74-
Value: &force,
75-
},
7670
},
7771
[]*captain.Argument{
7872
{
@@ -88,8 +82,8 @@ func newInstallCommand(prime *primer.Values) *captain.Command {
8882
return locale.WrapInputError(err, "err_install_packages_args", "Invalid install arguments")
8983
}
9084
}
91-
if force {
92-
prime.Prompt().EnableForce()
85+
if globals.Force {
86+
prime.Prompt().SetForce(true)
9387
}
9488
return runner.Run(params)
9589
},

cmd/state/main.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,10 @@ func run(args []string, isInteractive bool, cfg *config.Instance, out output.Out
224224
}()
225225

226226
// Set up prompter
227-
prompter := prompt.New(isInteractive, an)
227+
prompter := prompt.New(an)
228+
if !isInteractive {
229+
prompter.SetInteractive(false)
230+
}
228231

229232
// Set up conditional, which accesses a lot of primer data
230233
sshell := subshell.New(cfg)

internal/locale/locales/en-us.yaml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ flag_state_output_description:
2323
other: "Set the output method. Possible values: plain, simple, json, editor"
2424
flag_state_non_interactive_description:
2525
other: Run the State Tool without any prompts
26+
flag_state_force_description:
27+
other: Run the State Tool without any prompts, overriding any safe defaults
2628
flag_state_version_description:
2729
other: Show the version of our state executable
2830
flag_state_activate_path_description:
@@ -755,12 +757,8 @@ cache_description:
755757
other: Removes cached Runtime Environments
756758
clean_config_description:
757759
other: Removes global State Tool configuration. Project configuration will not be affected.
758-
flag_state_clean_uninstall_force_description:
759-
other: Run uninstall operation without prompts and ignoring any errors stopping running services
760760
arg_state_clean_cache_project_description:
761761
other: The project to be removed from the local cache.
762-
flag_state_clean_config_force_description:
763-
other: Run clean config operation without prompts and ignoring any errors stopping running services
764762
err_uninstall_activated:
765763
other: Cannot uninstall the State Tool while in an activated state. Please deactivate by entering [ACTIONABLE]exit[/RESET] or pressing [ACTIONABLE]Ctrl+D[/RESET] and then try again.
766764
err_remove_cache:
@@ -1171,6 +1169,10 @@ prompt_continue_non_interactive:
11711169
other: "Continuing because State Tool is running in non-interactive mode."
11721170
prompt_abort_non_interactive:
11731171
other: "Aborting because State Tool is running in non-interactive mode. To bypass you can use the '[ACTIONABLE]--force[/RESET]' flag."
1172+
prompt_using_force:
1173+
other: "Using '[ACTIONABLE]{{.V0}}[/RESET]' because the '[ACTIONABLE]--force[/RESET]' flag is set."
1174+
prompt_using_non_interactive:
1175+
other: "Using '[ACTIONABLE]{{.V0}}[/RESET]' because State Tool is running in non-interactive mode."
11741176
unstable_command_warning:
11751177
other: |
11761178
This command is still in beta. If you want to opt-in to unstable features, run the following command:

internal/prompt/prompt.go

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/ActiveState/cli/internal/analytics/constants"
1212
"github.com/ActiveState/cli/internal/errs"
1313
"github.com/ActiveState/cli/internal/locale"
14-
"github.com/ActiveState/cli/internal/logging"
1514
"github.com/ActiveState/cli/internal/output"
1615
)
1716

@@ -21,15 +20,14 @@ type EventDispatcher interface {
2120

2221
// Prompter is the interface used to run our prompt from
2322
type Prompter interface {
24-
Input(title, message string, defaultResponse *string, flags ...ValidatorFlag) (string, error)
25-
InputAndValidate(title, message string, defaultResponse *string, validator ValidatorFunc, flags ...ValidatorFlag) (string, error)
26-
Select(title, message string, choices []string, defaultResponse *string) (string, error)
23+
Input(title, message string, defaultResponse *string, forcedResponse *string, flags ...ValidatorFlag) (string, error)
24+
InputAndValidate(title, message string, defaultResponse *string, forcedResponse *string, validator ValidatorFunc, flags ...ValidatorFlag) (string, error)
25+
Select(title, message string, choices []string, defaultResponse *string, forcedResponse *string) (string, error)
2726
Confirm(title, message string, defaultChoice *bool, forcedChoice *bool) (bool, error)
2827
InputSecret(title, message string, flags ...ValidatorFlag) (string, error)
2928
IsInteractive() bool
3029
SetInteractive(bool)
31-
EnableForce()
32-
IsForceEnabled() bool
30+
SetForce(bool)
3331
}
3432

3533
// ValidatorFunc is a function pass to the Prompter to perform validation
@@ -47,8 +45,8 @@ type Prompt struct {
4745
}
4846

4947
// New creates a new prompter
50-
func New(isInteractive bool, an EventDispatcher) Prompter {
51-
return &Prompt{output.Get(), an, isInteractive, false}
48+
func New(an EventDispatcher) Prompter {
49+
return &Prompt{output.Get(), an, true, false}
5250
}
5351

5452
// IsInteractive checks if the prompts can be interactive or should just return default values
@@ -60,13 +58,13 @@ func (p *Prompt) SetInteractive(interactive bool) {
6058
p.isInteractive = interactive
6159
}
6260

63-
// EnableForce forces confirm prompts to return the force value (which is often different from the
61+
// SetForce enables prompts to return the force value (which is often different from the
6462
// non-interactive value).
65-
func (p *Prompt) EnableForce() {
66-
p.isForced = true
63+
func (p *Prompt) SetForce(force bool) {
64+
p.isForced = force
6765
}
6866

69-
func (p *Prompt) IsForceEnabled() bool {
67+
func (p *Prompt) IsForced() bool {
7068
return p.isForced
7169
}
7270

@@ -82,8 +80,8 @@ const (
8280
)
8381

8482
// Input prompts the user for input. The user can specify available validation flags to trigger validation of responses
85-
func (p *Prompt) Input(title, message string, defaultResponse *string, flags ...ValidatorFlag) (string, error) {
86-
return p.InputAndValidate(title, message, defaultResponse, func(val interface{}) error {
83+
func (p *Prompt) Input(title, message string, defaultResponse *string, forcedResponse *string, flags ...ValidatorFlag) (string, error) {
84+
return p.InputAndValidate(title, message, defaultResponse, forcedResponse, func(val interface{}) error {
8785
return nil
8886
}, flags...)
8987
}
@@ -100,10 +98,22 @@ func interactiveInputError(message string) error {
10098
}
10199

102100
// InputAndValidate prompts an input field and allows you to specfiy a custom validation function as well as the built in flags
103-
func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string, validator ValidatorFunc, flags ...ValidatorFlag) (string, error) {
101+
func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string, forcedResponse *string, validator ValidatorFunc, flags ...ValidatorFlag) (string, error) {
102+
if p.isForced {
103+
response := forcedResponse
104+
if response == nil {
105+
response = defaultResponse
106+
}
107+
if response != nil {
108+
p.out.Notice(locale.Tr("prompt_using_force", *response))
109+
return *response, nil
110+
}
111+
return "", errs.New("No force option given for forced prompt")
112+
}
113+
104114
if !p.isInteractive {
105115
if defaultResponse != nil {
106-
logging.Debug("Selecting default choice %s for Input prompt %s in non-interactive mode", *defaultResponse, title)
116+
p.out.Notice(locale.Tr("prompt_using_non_interactive", *defaultResponse))
107117
return *defaultResponse, nil
108118
}
109119
return "", interactiveInputError(message)
@@ -124,7 +134,7 @@ func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string
124134

125135
// We handle defaults more clearly than the survey package can
126136
if defaultResponse != nil && *defaultResponse != "" {
127-
v, err := p.Select("", formatMessage(message, !p.out.Config().Colored), []string{*defaultResponse, locale.Tl("prompt_custom", "Other ..")}, defaultResponse)
137+
v, err := p.Select("", formatMessage(message, !p.out.Config().Colored), []string{*defaultResponse, locale.Tl("prompt_custom", "Other ..")}, defaultResponse, forcedResponse)
128138
if err != nil {
129139
return "", err
130140
}
@@ -145,10 +155,22 @@ func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string
145155
}
146156

147157
// Select prompts the user to select one entry from multiple choices
148-
func (p *Prompt) Select(title, message string, choices []string, defaultChoice *string) (string, error) {
158+
func (p *Prompt) Select(title, message string, choices []string, defaultChoice *string, forcedChoice *string) (string, error) {
159+
if p.isForced {
160+
choice := forcedChoice
161+
if choice == nil {
162+
choice = defaultChoice
163+
}
164+
if choice != nil {
165+
p.out.Notice(locale.Tr("prompt_using_force", *choice))
166+
return *choice, nil
167+
}
168+
return "", errs.New("No force option given for forced prompt")
169+
}
170+
149171
if !p.isInteractive {
150172
if defaultChoice != nil {
151-
logging.Debug("Selecting default choice %s for Select prompt %s in non-interactive mode", *defaultChoice, title)
173+
p.out.Notice(locale.Tr("prompt_using_non_interactive", *defaultChoice))
152174
return *defaultChoice, nil
153175
}
154176
return "", interactiveInputError(message)
@@ -179,17 +201,24 @@ func (p *Prompt) Select(title, message string, choices []string, defaultChoice *
179201
// Confirm prompts user for yes or no response.
180202
func (p *Prompt) Confirm(title, message string, defaultChoice *bool, forcedChoice *bool) (bool, error) {
181203
if p.isForced {
182-
if forcedChoice == nil {
183-
return false, errs.New("No force option given for force-enabled prompt")
204+
choice := forcedChoice
205+
if choice == nil {
206+
choice = defaultChoice
184207
}
185-
logging.Debug("Prompt %s confirmed with choice %v in force mode", title, forcedChoice)
186-
return *forcedChoice, nil
208+
if choice != nil {
209+
p.out.Notice(locale.T("prompt_continue_force"))
210+
return *choice, nil
211+
}
212+
return false, errs.New("No force option given for forced prompt")
187213
}
188214

189215
if !p.isInteractive {
190216
if defaultChoice != nil {
191-
logging.Debug("Prompt %s confirmed with default choice %v in non-interactive mode", title, defaultChoice)
192-
return *defaultChoice, nil
217+
if *defaultChoice {
218+
p.out.Notice(locale.T("prompt_continue_non_interactive"))
219+
return true, nil
220+
}
221+
return false, locale.NewInputError("prompt_abort_non_interactive")
193222
}
194223
return false, interactiveInputError(message)
195224
}

internal/runbits/auth/keypair.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,12 @@ func promptForPreviousPassphrase(prompt prompt.Prompter) (string, error) {
113113
return passphrase, nil
114114
}
115115

116-
func promptUserToRegenerateKeypair(passphrase string, cfg keypairs.Configurable, out output.Outputer, prmpt prompt.Prompter, auth *authentication.Auth) error {
116+
func promptUserToRegenerateKeypair(passphrase string, cfg keypairs.Configurable, out output.Outputer, prompt prompt.Prompter, auth *authentication.Auth) error {
117117
// previous passphrase is invalid, inform user and ask if they want to generate a new keypair
118118
out.Notice(locale.T("auth_generate_new_keypair_message"))
119-
yes, err := prmpt.Confirm("", locale.T("auth_confirm_generate_new_keypair_prompt"), ptr.To(false), nil)
119+
yes, err := prompt.Confirm("", locale.T("auth_confirm_generate_new_keypair_prompt"), ptr.To(false), nil)
120120
if err != nil {
121-
return errs.Wrap(err, "Unable to confirm")
121+
return errs.Wrap(err, "Not confirmed")
122122
}
123123
if !yes {
124124
return locale.NewInputError("auth_err_unrecoverable_keypair")

0 commit comments

Comments
 (0)