Skip to content

Commit

Permalink
feat: Add promptBoolOnce, promptIntOnce, and promptStringOnce init te…
Browse files Browse the repository at this point in the history
…mplate functions
  • Loading branch information
twpayne committed Jul 22, 2022
1 parent 8bb70ab commit 06b212b
Show file tree
Hide file tree
Showing 11 changed files with 234 additions and 80 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# `promptBoolOnce` *map* *key* *prompt* [*default*]

`promptBoolOnce` returns *map*.*key* if it exists and is a boolean value,
otherwise it prompts the user for a boolean value with *prompt* and an optional
*default* using `promptBool`.

!!! example

```
{{ $hasGUI := promptBoolOnce . "hasGUI" "Does this machine have a GUI" }}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# `promptIntOnce` *map* *key* *prompt* [*default*]

`promptIntOnce` returns *map*.*key* if it exists and is an integer value,
otherwise it prompts the user for a integer value with *prompt* and an optional
*default* using `promptInt`.

!!! example

```
{{ $monitors := promptIntOnce . "monitors" "How many monitors does this machine have" }}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# `promptStringOnce` *map* *key* *prompt* [*default*]

`promptStringOnce` returns *map*.*key* if it exists and is an string value,
otherwise it prompts the user for a string value with *prompt* and an optional
*default* using `promptString`.

!!! example

```
{{ $email := promptStringOnce . "email" "What is your email address" }}
```
7 changes: 1 addition & 6 deletions assets/chezmoi.io/docs/user-guide/encryption/gpg.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,7 @@ passphrase being stored in plaintext on your machines, then you can use the
following configuration:

``` title="~/.local/share/chezmoi/.chezmoi.toml.tmpl"
{{ $passphrase := "" -}}
{{ if hasKey . "passphrase" -}}
{{ $passphrase = .passphrase -}}
{{ else -}}
{{ $passphrase = promptString "passphrase" -}}
{{ end -}}
{{ $passphrase := promptStringOnce "." "passphrase" "passphrase" -}}
encryption = "gpg"
[data]
Expand Down
16 changes: 6 additions & 10 deletions assets/chezmoi.io/docs/user-guide/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,15 @@ your initial config file.
Specifically, if you have `.chezmoi.toml.tmpl` that looks like this:

``` title="~/.local/share/chezmoi/.chezmoi.toml.tmpl"
{{- $email := promptString "email" -}}
{{- $email := promptStringOnce . "email" "Email address" -}}
[data]
email = {{ $email | quote }}
```

Then `chezmoi init` will create an initial `chezmoi.toml` using this template.
`promptString` is a special function that prompts the user (you) for a value.
`promptStringOnce` is a special function that prompts the user (you) for a value
if it is not already set in your `data`.

To test this template, use `chezmoi execute-template` with the `--init` and
`--promptString` flags, for example:
Expand All @@ -135,16 +136,11 @@ you will be prompted again. However, you can avoid this with the following
example template logic:

```
{{- $email := "" -}}
{{- if hasKey . "email" -}}
{{- $email = .email -}}
{{- else -}}
{{- $email = promptString "email" -}}
{{- end -}}
{{- $email := promptStringOnce . "email" "Email address" -}}
[data]
email = {{ $email | quote }}
```

This will cause chezmoi to first try to re-use the existing `$email` variable
and fallback to `promptString` only if it is not set.
This will cause chezmoi use the `email` variable from your `data` and fallback
to `promptString` only if it is not set.
3 changes: 3 additions & 0 deletions assets/chezmoi.io/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,11 @@ nav:
- reference/templates/init-functions/index.md
- exit: reference/templates/init-functions/exit.md
- promptBool: reference/templates/init-functions/promptBool.md
- promptBoolOnce: reference/templates/init-functions/promptBoolOnce.md
- promptInt: reference/templates/init-functions/promptInt.md
- promptIntOnce: reference/templates/init-functions/promptIntOnce.md
- promptString: reference/templates/init-functions/promptString.md
- promptStringOnce: reference/templates/init-functions/promptStringOnce.md
- stdinIsATTY: reference/templates/init-functions/stdinIsATTY.md
- writeToStdout: reference/templates/init-functions/writeToStdout.md
- 1Password functions:
Expand Down
15 changes: 9 additions & 6 deletions pkg/cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -776,12 +776,15 @@ func (c *Config) createConfigFile(filename chezmoi.RelPath, data []byte) ([]byte
funcMap := make(template.FuncMap)
chezmoi.RecursiveMerge(funcMap, c.templateFuncs)
initTemplateFuncs := map[string]interface{}{
"exit": c.exitInitTemplateFunc,
"promptBool": c.promptBoolInitTemplateFunc,
"promptInt": c.promptIntInitTemplateFunc,
"promptString": c.promptStringInitTemplateFunc,
"stdinIsATTY": c.stdinIsATTYInitTemplateFunc,
"writeToStdout": c.writeToStdout,
"exit": c.exitInitTemplateFunc,
"promptBool": c.promptBoolInitTemplateFunc,
"promptBoolOnce": c.promptBoolOnceInitTemplateFunc,
"promptInt": c.promptIntInitTemplateFunc,
"promptIntOnce": c.promptIntOnceInitTemplateFunc,
"promptString": c.promptStringInitTemplateFunc,
"promptStringOnce": c.promptStringOnceInitTemplateFunc,
"stdinIsATTY": c.stdinIsATTYInitTemplateFunc,
"writeToStdout": c.writeToStdout,
}
chezmoi.RecursiveMerge(funcMap, initTemplateFuncs)

Expand Down
135 changes: 87 additions & 48 deletions pkg/cmd/executetemplatecmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,58 +59,97 @@ func (c *Config) runExecuteTemplateCmd(cmd *cobra.Command, args []string) error
promptBool[key] = value
}
if c.executeTemplate.init {
initTemplateFuncs := map[string]interface{}{
"exit": c.exitInitTemplateFunc,
"promptBool": func(prompt string, args ...bool) bool {
switch len(args) {
case 0:
return promptBool[prompt]
case 1:
if value, ok := promptBool[prompt]; ok {
return value
}
return args[0]
default:
err := fmt.Errorf("want 1 or 2 arguments, got %d", len(args)+1)
panic(err)
promptBoolInitTemplateFunc := func(prompt string, args ...bool) bool {
switch len(args) {
case 0:
return promptBool[prompt]
case 1:
if value, ok := promptBool[prompt]; ok {
return value
}
},
"promptInt": func(prompt string, args ...int) int {
switch len(args) {
case 0:
return c.executeTemplate.promptInt[prompt]
case 1:
if value, ok := c.executeTemplate.promptInt[prompt]; ok {
return value
}
return args[0]
default:
err := fmt.Errorf("want 1 or 2 arguments, got %d", len(args)+1)
panic(err)
return args[0]
default:
err := fmt.Errorf("want 1 or 2 arguments, got %d", len(args)+1)
panic(err)
}
}

promptBoolOnceInitTemplateFunc := func(m map[string]interface{}, key, field string, args ...bool) bool {
if value, ok := m[key]; ok {
if boolValue, ok := value.(bool); ok {
return boolValue
}
},
"promptString": func(prompt string, args ...string) string {
switch len(args) {
case 0:
if value, ok := c.executeTemplate.promptString[prompt]; ok {
return value
}
return prompt
case 1:
if value, ok := c.executeTemplate.promptString[prompt]; ok {
return value
}
return args[0]
default:
err := fmt.Errorf("want 1 or 2 arguments, got %d", len(args)+1)
panic(err)
}
return promptBoolInitTemplateFunc(field, args...)
}

promptIntInitTemplateFunc := func(prompt string, args ...int64) int64 {
switch len(args) {
case 0:
return int64(c.executeTemplate.promptInt[prompt])
case 1:
if value, ok := c.executeTemplate.promptInt[prompt]; ok {
return int64(value)
}
},
"stdinIsATTY": func() bool {
return c.executeTemplate.stdinIsATTY
},
"writeToStdout": c.writeToStdout,
return args[0]
default:
err := fmt.Errorf("want 1 or 2 arguments, got %d", len(args)+1)
panic(err)
}
}

promptIntOnceInitTemplateFunc := func(m map[string]interface{}, key, field string, args ...int64) int64 {
if value, ok := m[key]; ok {
if intValue, ok := value.(int64); ok {
return intValue
}
}
return promptIntInitTemplateFunc(field, args...)
}

promptStringInitTemplateFunc := func(prompt string, args ...string) string {
switch len(args) {
case 0:
if value, ok := c.executeTemplate.promptString[prompt]; ok {
return value
}
return prompt
case 1:
if value, ok := c.executeTemplate.promptString[prompt]; ok {
return value
}
return args[0]
default:
err := fmt.Errorf("want 1 or 2 arguments, got %d", len(args)+1)
panic(err)
}
}

promptStringOnceInitTemplateFunc := func(m map[string]interface{}, key, field string, args ...string) string {
if value, ok := m[key]; ok {
if stringValue, ok := value.(string); ok {
return stringValue
}
}
return promptStringInitTemplateFunc(field, args...)
}

stdinIsATTYInitTemplateFunc := func() bool {
return c.executeTemplate.stdinIsATTY
}

initTemplateFuncs := map[string]interface{}{
"exit": c.exitInitTemplateFunc,
"promptBool": promptBoolInitTemplateFunc,
"promptBoolOnce": promptBoolOnceInitTemplateFunc,
"promptInt": promptIntInitTemplateFunc,
"promptIntOnce": promptIntOnceInitTemplateFunc,
"promptString": promptStringInitTemplateFunc,
"promptStringOnce": promptStringOnceInitTemplateFunc,
"stdinIsATTY": stdinIsATTYInitTemplateFunc,
"writeToStdout": c.writeToStdout,
}

chezmoi.RecursiveMerge(c.templateFuncs, initTemplateFuncs)
}

Expand Down
39 changes: 39 additions & 0 deletions pkg/cmd/inittemplatefuncs.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ func (c *Config) promptBoolInitTemplateFunc(prompt string, args ...bool) bool {
}
}

func (c *Config) promptBoolOnceInitTemplateFunc(m map[string]interface{}, key, prompt string, args ...bool) bool {
if len(args) > 1 {
err := fmt.Errorf("want 2 or 3 arguments, got %d", len(args)+2)
panic(err)
}
if value, ok := m[key]; ok {
if boolValue, ok := value.(bool); ok {
return boolValue
}
}
return c.promptBoolInitTemplateFunc(prompt, args...)
}

func (c *Config) promptIntInitTemplateFunc(prompt string, args ...int64) int64 {
switch len(args) {
case 0:
Expand All @@ -65,6 +78,19 @@ func (c *Config) promptIntInitTemplateFunc(prompt string, args ...int64) int64 {
}
}

func (c *Config) promptIntOnceInitTemplateFunc(m map[string]interface{}, key, prompt string, args ...int64) int64 {
if len(args) > 1 {
err := fmt.Errorf("want 2 or 3 arguments, got %d", len(args)+2)
panic(err)
}
if value, ok := m[key]; ok {
if intValue, ok := value.(int64); ok {
return intValue
}
}
return c.promptIntInitTemplateFunc(prompt, args...)
}

func (c *Config) promptStringInitTemplateFunc(prompt string, args ...string) string {
switch len(args) {
case 0:
Expand All @@ -90,6 +116,19 @@ func (c *Config) promptStringInitTemplateFunc(prompt string, args ...string) str
}
}

func (c *Config) promptStringOnceInitTemplateFunc(m map[string]interface{}, key, prompt string, args ...string) string {
if len(args) > 1 {
err := fmt.Errorf("want 2 or 3 arguments, got %d", len(args)+2)
panic(err)
}
if value, ok := m[key]; ok {
if stringValue, ok := value.(string); ok {
return stringValue
}
}
return c.promptStringInitTemplateFunc(prompt, args...)
}

func (c *Config) stdinIsATTYInitTemplateFunc() bool {
file, ok := c.stdin.(*os.File)
if !ok {
Expand Down
54 changes: 54 additions & 0 deletions pkg/cmd/testdata/scripts/inittemplatefuncs.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# test exit template function
chezmoi execute-template --init '{{ exit 0 }}'
! chezmoi execute-template --init '{{ exit 1 }}'

# test promptBoolOnce template function with execute-template --init
chezmoi execute-template --init --promptBool bool=true '{{ promptBoolOnce . "bool" "bool" }}'
stdout true

# test promptIntOnce template function with execute-template --init
chezmoi execute-template --init --promptInt int=1 '{{ promptIntOnce . "int" "int" }}'
stdout 1

# test promptStringOnce template function with execute-template --init
chezmoi execute-template --init --promptString string=value '{{ promptStringOnce . "string" "string" }}'
stdout value

# test writeToStdout template function
chezmoi execute-template --init '{{ writeToStdout "string" }}'
stdout string

# test prompt*Once functions without existing data
stdin golden/input
chezmoi init
cmp ${CHEZMOICONFIGDIR}/chezmoi.toml golden/chezmoi.toml

chhome home2/user

# test prompt*Once functions with existing data
chezmoi init
cmp ${CHEZMOICONFIGDIR}/chezmoi.toml golden/chezmoi.toml

-- golden/chezmoi.toml --
[data]
bool = true
int = 1
string = "value"
-- golden/input --
true
1
value
-- home/user/.local/share/chezmoi/.chezmoi.toml.tmpl --
{{ $bool := promptBoolOnce . "bool" "bool" -}}
{{ $int := promptIntOnce . "int" "int" -}}
{{ $string := promptStringOnce . "string" "string" -}}

[data]
bool = {{ $bool }}
int = {{ $int }}
string = {{ $string | quote }}
-- home2/user/.config/chezmoi/chezmoi.toml --
[data]
bool = true
int = 1
string = "value"

0 comments on commit 06b212b

Please sign in to comment.