Skip to content

Commit

Permalink
Add --disable-dependency-prompt flag (#181)
Browse files Browse the repository at this point in the history
Co-authored-by: Denis O <[email protected]>
  • Loading branch information
jasonyoung-pearl and denis256 authored May 29, 2024
1 parent 0586d7c commit 9fd73bc
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 54 deletions.
41 changes: 23 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ The `boilerplate` binary supports the following options:
folder without any variables). Default: `exit`.
* `--disable-hooks`: If this flag is set, no hooks will execute.
* `--disable-shell`: If this flag is set, no `shell` helpers will execute. They will instead return the text "replace-me".
* `--disable-dependency-prompt` (optional): Do not prompt for confirmation to include dependencies. Has the same effect as
--non-interactive, without disabling variable prompts. Default: `false`.
* `--help`: Show the help text and exit.
* `--version`: Show the version and exit.

Expand Down Expand Up @@ -367,11 +369,11 @@ executing the current one. Each dependency may contain the following keys:
- Defaults set on root variables.
- Defaults set within the dependency boilerplate config.
* `for_each`: If you set this to a list of values, `boilerplate` will loop over each value, and render this dependency
once for each value. This allows you to dynamically render a dependency multiple times based on user input. The
once for each value. This allows you to dynamically render a dependency multiple times based on user input. The
current value in the loop will be available as the variable `__each__`, available to both your Go templating and
in other `dependencies` params: e.g., you could reference `{{ .__each__ }}` in `output-folder` to render to each
in other `dependencies` params: e.g., you could reference `{{ .__each__ }}` in `output-folder` to render to each
iteration to a different folder.
* `for_each_reference`: The name of another variable whose value should be used as the `for_each` value.
* `for_each_reference`: The name of another variable whose value should be used as the `for_each` value.

See the [Dependencies](#dependencies) section for more info.

Expand Down Expand Up @@ -506,7 +508,7 @@ Note the following:
# Skip this dependency if both .Foo and .Bar are set to true
skip: "{{ and .Foo .Bar }}"
```
* Looping over dependencies: You can render a dependency multiple times, dynamically, based on user input, via the
* Looping over dependencies: You can render a dependency multiple times, dynamically, based on user input, via the
`for_each` or `for_each_reference` parameter. Example:

```yaml
Expand All @@ -518,7 +520,7 @@ Note the following:
- dev
- stage
- prod
dependencies:
- name: loop-dependency-example
template-url: ../terraform
Expand All @@ -533,6 +535,9 @@ Note the following:
# Use the environment name in the server name
default: "example-{{ .__each__ }}"
```
* Confirming dependency includes: By default in interactive mode, the user must confirm to include each dependency.
If `--non-interactive` is set, dependencies will be included automatically, so long as `skip` is false. If you'd
like to keep interactive mode enabled, but disable dependency confirmation, set `--disable-dependency-prompt`.

#### Hooks

Expand Down Expand Up @@ -572,7 +577,7 @@ Note the following:
```yaml
after:
- command: some-command
dir: "{{ outputFolder }}/foo/bar"
dir: "{{ outputFolder }}/foo/bar"
```
* `skip` (Optional): Skip this hook if this condition, which can use Go templating syntax and
boilerplate variables, evaluates to the string `true`. This is useful to conditionally enable or disable
Expand Down Expand Up @@ -658,36 +663,36 @@ you were using `boilerplate` to generate a Java project, your template folder co

#### Validations

Boilerplate allows you to specify a set of validations when defining a variable. When a user is prompted for a variable that has
validations defined, their input must pass all defined validations. If the user's input does not pass all validations, they'll be
Boilerplate allows you to specify a set of validations when defining a variable. When a user is prompted for a variable that has
validations defined, their input must pass all defined validations. If the user's input does not pass all validations, they'll be
presented with real-time feedback on exactly which rules their submission is failing. Once a user's submission passes all defined
validations, Boilerplate will accept their submitted value.

Here's an example prompt for a variable with validations that shows how invalid submissions are handled:
Here's an example prompt for a variable with validations that shows how invalid submissions are handled:

![Example Boilerplate real-time validation](./docs/bp-validation.png)

Here's an example demonstating how to specify validations when defining your variables:
Here's an example demonstating how to specify validations when defining your variables:

```yaml
variables:
- name: CompanyName
description: Enter the name of your organization.
description: Enter the name of your organization.
default: ""
type: string
validations:
- required
- length-5-22
- alphanumeric
```
This `boilerplate.yml` snippet defines a variable, `CompanyName` which:
This `boilerplate.yml` snippet defines a variable, `CompanyName` which:
* Must be supplied by the user. No empty or nil values will be accepted.
* Must have a length between 5 and 22 characters
* Must contain only alphanumeric characters (no special characters)

**Currently supported validations**

Boilerplate uses the [`go-ozzo/ozzo-validation` library](https://github.com/go-ozzo/ozzo-validation). The following validations are currently supported:
Boilerplate uses the [`go-ozzo/ozzo-validation` library](https://github.com/go-ozzo/ozzo-validation). The following validations are currently supported:

- "required" - field cannot be empty
- "length-{min-int}-{max-int}" - field must be between ${min-int} and ${max-int} characters in length
Expand All @@ -701,18 +706,18 @@ Boilerplate uses the [`go-ozzo/ozzo-validation` library](https://github.com/go-o
#### Variable Ordering

Boilerplate allows you to define the relative order in which a set of variables should be presented to the user when prompting
for human input.
for human input.

Here's an example demonstrating how to define the relative order of a set of variables:
Here's an example demonstrating how to define the relative order of a set of variables:

```yaml
variables:
variables:
- name: WebsiteURL
order: 0
description: Enter the URL to your homepage
- name: ImagePath:
- name: ImagePath:
order: 1
description: Enter the full filepath to your logo image
description: Enter the full filepath to your logo image
- name: ProfileName
order: 2
description: Enter the display name for your user
Expand Down
4 changes: 4 additions & 0 deletions cli/boilerplate_cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ func CreateBoilerplateCli() *cli.App {
Name: options.OptDisableShell,
Usage: "If this flag is set, no shell helpers will execute. They will instead return the text 'replace-me'.",
},
&cli.BoolFlag{
Name: options.OptDisableDependencyPrompt,
Usage: fmt.Sprintf("Do not prompt for confirmation to include dependencies. Has the same effect as --%s, without disabling variable prompts.", options.OptNonInteractive),
},
}

// We pass JSON/YAML content to various CLI flags, such as --var, and this JSON/YAML content may contain commas or
Expand Down
35 changes: 19 additions & 16 deletions options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const OptMissingKeyAction = "missing-key-action"
const OptMissingConfigAction = "missing-config-action"
const OptDisableHooks = "disable-hooks"
const OptDisableShell = "disable-shell"
const OptDisableDependencyPrompt = "disable-dependency-prompt"

// The command-line options for the boilerplate app
type BoilerplateOptions struct {
Expand All @@ -27,13 +28,14 @@ type BoilerplateOptions struct {
// Working directory where the go-getter defined template is downloaded.
TemplateFolder string

OutputFolder string
NonInteractive bool
Vars map[string]interface{}
OnMissingKey MissingKeyAction
OnMissingConfig MissingConfigAction
DisableHooks bool
DisableShell bool
OutputFolder string
NonInteractive bool
Vars map[string]interface{}
OnMissingKey MissingKeyAction
OnMissingConfig MissingConfigAction
DisableHooks bool
DisableShell bool
DisableDependencyPrompt bool
}

// Validate that the options have reasonable values and return an error if they don't
Expand Down Expand Up @@ -84,15 +86,16 @@ func ParseOptions(cliContext *cli.Context) (*BoilerplateOptions, error) {
}

options := &BoilerplateOptions{
TemplateUrl: templateUrl,
TemplateFolder: templateFolder,
OutputFolder: cliContext.String(OptOutputFolder),
NonInteractive: cliContext.Bool(OptNonInteractive),
OnMissingKey: missingKeyAction,
OnMissingConfig: missingConfigAction,
Vars: vars,
DisableHooks: cliContext.Bool(OptDisableHooks),
DisableShell: cliContext.Bool(OptDisableShell),
TemplateUrl: templateUrl,
TemplateFolder: templateFolder,
OutputFolder: cliContext.String(OptOutputFolder),
NonInteractive: cliContext.Bool(OptNonInteractive),
OnMissingKey: missingKeyAction,
OnMissingConfig: missingConfigAction,
Vars: vars,
DisableHooks: cliContext.Bool(OptDisableHooks),
DisableShell: cliContext.Bool(OptDisableShell),
DisableDependencyPrompt: cliContext.Bool(OptDisableDependencyPrompt),
}

if err := options.Validate(); err != nil {
Expand Down
38 changes: 20 additions & 18 deletions templates/template_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,15 +305,16 @@ func cloneOptionsForDependency(
}

return &options.BoilerplateOptions{
TemplateUrl: templateUrl,
TemplateFolder: templateFolder,
OutputFolder: outputFolder,
NonInteractive: originalOpts.NonInteractive,
Vars: vars,
OnMissingKey: originalOpts.OnMissingKey,
OnMissingConfig: originalOpts.OnMissingConfig,
DisableHooks: originalOpts.DisableHooks,
DisableShell: originalOpts.DisableShell,
TemplateUrl: templateUrl,
TemplateFolder: templateFolder,
OutputFolder: outputFolder,
NonInteractive: originalOpts.NonInteractive,
Vars: vars,
OnMissingKey: originalOpts.OnMissingKey,
OnMissingConfig: originalOpts.OnMissingConfig,
DisableHooks: originalOpts.DisableHooks,
DisableShell: originalOpts.DisableShell,
DisableDependencyPrompt: originalOpts.DisableDependencyPrompt,
}, nil
}

Expand All @@ -338,13 +339,14 @@ func cloneVariablesForDependency(
NonInteractive: true,
OnMissingKey: options.ExitWithError,

TemplateUrl: opts.TemplateUrl,
TemplateFolder: opts.TemplateFolder,
OutputFolder: opts.OutputFolder,
Vars: opts.Vars,
OnMissingConfig: opts.OnMissingConfig,
DisableHooks: opts.DisableHooks,
DisableShell: opts.DisableShell,
TemplateUrl: opts.TemplateUrl,
TemplateFolder: opts.TemplateFolder,
OutputFolder: opts.OutputFolder,
Vars: opts.Vars,
OnMissingConfig: opts.OnMissingConfig,
DisableHooks: opts.DisableHooks,
DisableShell: opts.DisableShell,
DisableDependencyPrompt: opts.DisableDependencyPrompt,
}

// Start with the original variables. Note that it doesn't matter that originalVariables contains both CLI and
Expand Down Expand Up @@ -414,7 +416,7 @@ func cloneVariablesForDependency(
}

// Prompt the user to verify if the given dependency should be executed and return true if they confirm. If
// options.NonInteractive is set to true, this function always returns true.
// options.NonInteractive or options.DisableDependencyPrompt are set to true, this function always returns true.
func shouldProcessDependency(dependency variables.Dependency, opts *options.BoilerplateOptions, variables map[string]interface{}) (bool, error) {
shouldSkip, err := shouldSkipDependency(dependency, opts, variables)
if err != nil {
Expand All @@ -424,7 +426,7 @@ func shouldProcessDependency(dependency variables.Dependency, opts *options.Boil
return false, nil
}

if opts.NonInteractive {
if opts.NonInteractive || opts.DisableDependencyPrompt {
return true, nil
}

Expand Down
9 changes: 7 additions & 2 deletions templates/template_processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ import (
"path/filepath"
"testing"

"github.com/stretchr/testify/require"

"github.com/gruntwork-io/boilerplate/options"
"github.com/gruntwork-io/boilerplate/variables"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestOutPath(t *testing.T) {
Expand Down Expand Up @@ -62,6 +61,12 @@ func TestCloneOptionsForDependency(t *testing.T) {
map[string]interface{}{},
options.BoilerplateOptions{TemplateUrl: "../dep1", TemplateFolder: filepath.FromSlash("/template/dep1"), OutputFolder: filepath.FromSlash("/output/out1"), NonInteractive: true, Vars: map[string]interface{}{}, OnMissingKey: options.ExitWithError},
},
{
variables.Dependency{Name: "dep1", TemplateUrl: "../dep1", OutputFolder: "../out1"},
options.BoilerplateOptions{TemplateFolder: "/template/path/", OutputFolder: "/output/path/", DisableDependencyPrompt: true, Vars: map[string]interface{}{}, OnMissingKey: options.ExitWithError},
map[string]interface{}{},
options.BoilerplateOptions{TemplateUrl: "../dep1", TemplateFolder: filepath.FromSlash("/template/dep1"), OutputFolder: filepath.FromSlash("/output/out1"), DisableDependencyPrompt: true, Vars: map[string]interface{}{}, OnMissingKey: options.ExitWithError},
},
{
variables.Dependency{Name: "dep1", TemplateUrl: "../dep1", OutputFolder: "../out1"},
options.BoilerplateOptions{TemplateFolder: "/template/path/", OutputFolder: "/output/path/", NonInteractive: false, Vars: map[string]interface{}{"foo": "bar"}, OnMissingKey: options.Invalid},
Expand Down

0 comments on commit 9fd73bc

Please sign in to comment.