Skip to content

Commit

Permalink
feat: support confirm (boolean) values
Browse files Browse the repository at this point in the history
Use `optBool` to convert any value to a boolean to be used in the
template.

A new `type` field can be set for an option.
Possible values are:

- input: text field
- select: single select field
- confirm: ask for a yes/no confirmation

If no type is set, if some values are set type will be select, else it
will be an input. This is just for backward compatibility, type should
be set.

Signed-off-by: Yves Brissaud <[email protected]>
  • Loading branch information
eunomie committed Oct 15, 2024
1 parent 500d495 commit 57d1f31
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 13 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ actions:
# A list of options that can be provided by the user.
opts:
- name: OPTION_NAME # Name of the option. Also used in the local override or with `--opt` flag.
type: input|select|confirm # Type of the option.
desc: DESCRIPTION # Description, rendered in the documentation of the action.
prompt: PROMPT # A specific prompt to ask the user for the value.
no-prompt: true|false # If set to true, the option will not be prompted to the user.
Expand All @@ -155,6 +156,8 @@ actions:
# The environment variable needs to be defined in the `env` section.
# - `{{opt "OPTION"}}` will be replaced by the value of the option `OPTION`.
# The value needs to be provided by the local configuration, on the command line or interactively.
# - `{{optBool "OPTION"}}` is equivalent to `{{opt "OPTION"}}` but will return the value as a boolean.
# True values are `1`, `t`, `T`, `TRUE`, `true` and `True`. Everything else is considered as false.
# - `{{sh "COMMAND"}}` will be replaced by the output of the shell command `COMMAND`.
# The command will be run using https://github.com/mvdan/sh without a standard input.
cmd: COMMAND
Expand Down
3 changes: 3 additions & 0 deletions docs/index.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ actions:
# A list of options that can be provided by the user.
opts:
- name: OPTION_NAME # Name of the option. Also used in the local override or with `--opt` flag.
type: input|select|confirm # Type of the option.
desc: DESCRIPTION # Description, rendered in the documentation of the action.
prompt: PROMPT # A specific prompt to ask the user for the value.
no-prompt: true|false # If set to true, the option will not be prompted to the user.
Expand All @@ -148,6 +149,8 @@ actions:
# The environment variable needs to be defined in the `env` section.
# - `{{opt "OPTION"}}` will be replaced by the value of the option `OPTION`.
# The value needs to be provided by the local configuration, on the command line or interactively.
# - `{{optBool "OPTION"}}` is equivalent to `{{opt "OPTION"}}` but will return the value as a boolean.
# True values are `1`, `t`, `T`, `TRUE`, `true` and `True`. Everything else is considered as false.
# - `{{sh "COMMAND"}}` will be replaced by the output of the shell command `COMMAND`.
# The command will be run using https://github.com/mvdan/sh without a standard input.
cmd: COMMAND
Expand Down
41 changes: 30 additions & 11 deletions internal/prompt/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package prompt
import (
"cmp"
"errors"
"strconv"
"strings"

"github.com/charmbracelet/huh"
Expand Down Expand Up @@ -53,10 +54,11 @@ func Ask(action *runkit.Action, opts map[string]string) (map[string]string, erro
}

var (
err error
form *huh.Form
fields []huh.Field
asked []string
err error
form *huh.Form
fields []huh.Field
asked []string
boolAsked []string
)

for _, opt := range action.Options {
Expand All @@ -67,27 +69,41 @@ func Ask(action *runkit.Action, opts map[string]string) (map[string]string, erro
continue
}
opt := opt
if len(opt.Values) == 0 {

var (
title = cmp.Or(opt.Prompt, cmp.Or(opt.Description, opt.Name))
description = sugar.If(title != opt.Description, opt.Description, "")
)
switch opt.Type {
case runkit.OptTypeInput:
fields = append(fields,
huh.NewInput().
Title(cmp.Or(opt.Prompt, cmp.Or(opt.Description, opt.Name))).
Title(title).
Key(opt.Name).
Description(opt.Description).
Description(description).
Placeholder(opt.Default).
Suggestions(sugar.If(opt.Default != "", []string{opt.Default}, nil)).
Validate(checkRequired(opt.Required)))
} else {
asked = append(asked, opt.Name)
case runkit.OptTypeSelect:
fields = append(fields,
huh.NewSelect[string]().
Title(cmp.Or(opt.Prompt, cmp.Or(opt.Description, opt.Name))).
Title(title).
Key(opt.Name).
Description(opt.Description).
Description(description).
Validate(checkRequired(opt.Required)).
Options(pizza.Map(opt.Values, func(str string) huh.Option[string] {
return huh.NewOption(str, str).Selected(str == opt.Default)
})...))
asked = append(asked, opt.Name)
case runkit.OptTypeConfirm:
fields = append(fields,
huh.NewConfirm().
Title(title).
Key(opt.Name).
Description(description))
boolAsked = append(boolAsked, opt.Name)
}
asked = append(asked, opt.Name)
}

if len(fields) == 0 {
Expand All @@ -102,6 +118,9 @@ func Ask(action *runkit.Action, opts map[string]string) (map[string]string, erro
for _, optName := range asked {
opts[optName] = form.GetString(optName)
}
for _, optName := range boolAsked {
opts[optName] = strconv.FormatBool(form.GetBool(optName))
}

return opts, nil
}
Expand Down
11 changes: 11 additions & 0 deletions runkit/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,17 @@ func decodeConfig(rk *RunKit, src string, runxConfig []byte) error {
a.isDefault = true
}

for i, o := range a.Options {
if o.Type == OptTypeNotSet {
if len(o.Values) > 0 {
o.Type = OptTypeSelect
} else {
o.Type = OptTypeInput
}
a.Options[i] = o
}
}

actions = append(actions, a)
}
config.Actions = actions
Expand Down
9 changes: 9 additions & 0 deletions runkit/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"os"
"strconv"
"strings"
"text/template"

Expand Down Expand Up @@ -118,6 +119,14 @@ func (r *Runnable) compute() error {
"opt": func(optName string) string {
return r.data.Opts[optName]
},
"optBool": func(optName string) bool {
o, ok := r.data.Opts[optName]
if !ok {
return false
}
v, _ := strconv.ParseBool(o)
return v
},
"sh": func(cmdName string) (string, error) {
v, ok := shells[cmdName]
if !ok {
Expand Down
12 changes: 10 additions & 2 deletions runkit/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type (

Opt struct {
Name string `yaml:"name" json:"name"`
Type OptType `yaml:"type,omitempty" json:"type,omitempty"`
Description string `yaml:"desc" json:"desc,omitempty"`
NoPrompt bool `yaml:"no-prompt,omitempty" json:"no-prompt,omitempty"`
Prompt string `yaml:"prompt,omitempty" json:"prompt,omitempty"`
Expand All @@ -39,6 +40,8 @@ type (

ActionType string

OptType string

LocalConfig struct {
Ref string `yaml:"ref,omitempty" json:"ref,omitempty"`
Images map[string]ConfigImage `yaml:"images,omitempty" json:"images,omitempty"`
Expand All @@ -58,8 +61,13 @@ type (
const (
ActionTypeRun ActionType = "run"
ActionTypeBuild ActionType = "build"

OptTypeNotSet OptType = ""
OptTypeInput OptType = "input"
OptTypeSelect OptType = "select"
OptTypeConfirm OptType = "confirm"
)

func (a *Action) IsDefault() bool {
return a.isDefault
func (action *Action) IsDefault() bool {
return action.isDefault
}

0 comments on commit 57d1f31

Please sign in to comment.