Skip to content

Commit

Permalink
Merge pull request #59 from gruntwork-io/yori-remote-templates
Browse files Browse the repository at this point in the history
Remote templates support
  • Loading branch information
yorinasub17 committed Aug 31, 2020
2 parents 593e898 + e77854c commit c6198a5
Show file tree
Hide file tree
Showing 42 changed files with 1,367 additions and 152 deletions.
44 changes: 27 additions & 17 deletions _docs/README.md
Expand Up @@ -34,11 +34,11 @@ Template](https://golang.org/pkg/text/template) syntax:

Copy an image into the `website-boilerplate` folder and call it `logo.png`.

Finally, run `boilerplate`, setting the `--template-folder` to `website-boilerplate` and `--output-folder` to the path
Finally, run `boilerplate`, setting the `--template-url` to `website-boilerplate` and `--output-folder` to the path
where you want the generated code to go:

```
boilerplate --template-folder /home/ubuntu/website-boilerplate --output-folder /home/ubuntu/website-output
boilerplate --template-url /home/ubuntu/website-boilerplate --output-folder /home/ubuntu/website-output
Title
Expand All @@ -58,7 +58,7 @@ Generating /home/ubuntu/website-output/index.html
Copying /home/ubuntu/website-output/logo.png
```

Boilerplate copies any files from the `--template-folder` into the `--output-folder`, passing them through the
Boilerplate copies any files from the `--template-url` into the `--output-folder`, passing them through the
[Go Template](https://golang.org/pkg/text/template) engine along the way. After running the command above, the
`website-output` folder will contain a `logo.png` (unchanged from the original) and an `index.html` with the
following contents:
Expand All @@ -71,7 +71,7 @@ You can also run Boilerplate non-interactively, which is great for automation:

```
boilerplate \
--template-folder /home/ubuntu/website-boilerplate \
--template-url /home/ubuntu/website-boilerplate \
--output-folder /home/ubuntu/website-output \
--non-interactive \
--var Title="Boilerplate Example" \
Expand Down Expand Up @@ -112,11 +112,11 @@ You can find older versions on the [Releases Page](https://github.com/gruntwork-

When you run Boilerplate, it performs the following steps:

1. Read the `boilerplate.yml` file in the folder specified by the `--template-folder` option to find all defined
1. Read the `boilerplate.yml` file in the folder specified by the `--template-url` option to find all defined
varaibles.
1. Gather values for the variables from any `--var` and `--var-file` options that were passed in and prompting the user
for the rest (unless the `--non-interactive` flag is specified).
1. Copy each file from `--template-folder` to `--output-folder`, running each non-binary file through the Go
1. Copy each file from `--template-url` to `--output-folder`, running each non-binary file through the Go
Template engine with the map of variables as the data structure.

Learn more about boilerplate in the following sections:
Expand All @@ -133,7 +133,9 @@ Learn more about boilerplate in the following sections:

The `boilerplate` binary supports the following options:

* `--template-folder FOLDER` (required): Generate the project from the templates in `FOLDER`.
* `--template-url URL` (required): Generate the project from the templates in `URL`. This can be a local path, or a
[go-getter](https://github.com/hashicorp/go-getter) compatible URL for remote templates (e.g.,
`git@github.com:gruntwork-io/boilerplate.git//examples/include?ref=master`).
* `--output-folder` (required): Create the output files and folders in `FOLDER`.
* `--non-interactive` (optional): Do not prompt for input variables. All variables must be set via `--var` and
`--var-file` options instead.
Expand All @@ -155,21 +157,28 @@ Some examples:
Generate a project in ~/output from the templates in ~/templates:

```
boilerplate --template-folder ~/templates --output-folder ~/output
boilerplate --template-url ~/templates --output-folder ~/output
```

Generate a project in ~/output from the templates in ~/templates, using variables passed in via the command line:

```
boilerplate --template-folder ~/templates --output-folder ~/output --var "Title=Boilerplate" --var "ShowLogo=false"
boilerplate --template-url ~/templates --output-folder ~/output --var "Title=Boilerplate" --var "ShowLogo=false"
```

Generate a project in ~/output from the templates in ~/templates, using variables read from a file:

```
boilerplate --template-folder ~/templates --output-folder ~/output --var-file vars.yml
boilerplate --template-url ~/templates --output-folder ~/output --var-file vars.yml
```

Generate a project in ~/output from the templates in this repo's `include` example dir, using variables read from a file:

```
boilerplate --template-url "git@github.com:gruntwork-io/boilerplate.git//examples/include?ref=master" --output-folder ~/output --var-file vars.yml
```


#### The boilerplate.yml file

The `boilerplate.yml` file is used to configure `boilerplate`. The file is optional. If you don't specify it, you can
Expand All @@ -191,7 +200,7 @@ variables:

dependencies:
- name: <DEPENDENCY_NAME>
template-folder: <FOLDER>
template-url: <FOLDER>
output-folder: <FOLDER>
skip: <CONDITION>
dont-inherit-variables: <BOOLEAN>
Expand Down Expand Up @@ -244,7 +253,7 @@ See the [Variables](#variables) section for more info.
executing the current one. Each dependency may contain the following keys:

* `name` (Required): A unique name for the dependency.
* `template-folder` (Required): Run `boilerplate` on the templates in this folder. This path is relative to the
* `template-url` (Required): Run `boilerplate` on the templates in this folder. This path is relative to the
current template.
* `output-folder` (Required): Create the output files and folders in this folder. This path is relative to the output
folder of the current template.
Expand Down Expand Up @@ -350,7 +359,7 @@ Note the following:
prompt you for each of those variables separately from the root ones. You can also use the
`<DEPENDENCY_NAME>.<VARIABLE_NAME>` syntax as the name of the variable with the `-var` flag and inside of a var file
to provide a value for a variable in a dependency.
* Interpolation: You may use interpolation in the `template-folder` and `output-folder` parameters of your
* Interpolation: You may use interpolation in the `template-url` and `output-folder` parameters of your
dependencies. This allows you to use specify the paths to your template and output folders dynamically.
* Conditional dependencies: You can enable or disable a dependency using the `skip` parameter, which supports Go
templating syntax and boilerplate variables. If the `skip` parameter evaluates to the string `true`, the
Expand All @@ -366,7 +375,7 @@ Note the following:

dependencies:
- name: conditional-dependency-example
template-folder: ../foo
template-url: ../foo
output-folder: foo
# Skip this dependency if both .Foo and .Bar are set to true
skip: "{{"{{"}} and .Foo .Bar {{"}}"}}"
Expand Down Expand Up @@ -402,7 +411,7 @@ Note the following:
- {{"{{"}} .SomeVariable {{"}}"}}
- {{"{{"}} .AnotherVariable {{"}}"}}
```
* Boilerplate runs your `command` with the working directory set to the `--template-folder` option.
* Boilerplate runs your `command` with the working directory set to the `--template-url` option.
* `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
dependencies.
Expand All @@ -411,7 +420,7 @@ Note the following:
#### Templates

Boilerplate puts all the variables into a Go map where the key is the name of the variable and the value is what
the user provided. It then starts copying files from the `--template-folder` into the `--output-folder`, passing each
the user provided. It then starts copying files from the `--template-url` into the `--output-folder`, passing each
non-binary file through the [Go Template](https://golang.org/pkg/text/template) engine, with the variable map as a
data structure.

Expand Down Expand Up @@ -481,7 +490,8 @@ Boilerplate also includes several custom helpers that you can access that enhanc
so you can use paths relative to the file from which you are calling the `shell` helper. Any argument you pass of the
form `ENV:KEY=VALUE` will be set as an environment variable for the command rather than an argument. For another way
to execute commands, see [hooks](#hooks).
* `templateFolder`: Return the value of the `--template-folder` command-line option. Useful for building relative paths.
* `templateFolder`: Return the value of the template working dir. This is the value of the `--template-url` command-line
option if local template, or the download dir if remote template. Useful for building relative paths.
* `outputFolder`: Return the value of the `--output-folder` command-line option. Useful for building relative paths.
* `envWithDefault NAME DEFAULT`: Render the value of environment variable `NAME`. If that environment variable is empty or not
defined, render `DEFAULT` instead.
Expand Down
2 changes: 1 addition & 1 deletion _docs/boilerplate.yml
@@ -1,2 +1,2 @@
variables:
- name: Version
- name: Version
17 changes: 10 additions & 7 deletions cli/boilerplate_cli.go
Expand Up @@ -17,15 +17,19 @@ A tool for generating files and folders (\"boilerplate\") from a set of template
Generate a project in ~/output from the templates in ~/templates:
boilerplate --template-folder ~/templates --output-folder ~/output
boilerplate --template-url ~/templates --output-folder ~/output
Generate a project in ~/output from the templates in ~/templates, using variables passed in via the command line:
boilerplate --template-folder ~/templates --output-folder ~/output --var "Title=Boilerplate" --var "ShowLogo=false"
boilerplate --template-url ~/templates --output-folder ~/output --var "Title=Boilerplate" --var "ShowLogo=false"
Generate a project in ~/output from the templates in ~/templates, using variables read from a file:
boilerplate --template-folder ~/templates --output-folder ~/output --var-file vars.yml
boilerplate --template-url ~/templates --output-folder ~/output --var-file vars.yml
Generate a project in ~/output from the templates in this repo's include example dir, using variables read from a file:
boilerplate --template-url "git@github.com:gruntwork-io/boilerplate.git//examples/include?ref=master" --output-folder ~/output --var-file vars.yml
Options:
Expand All @@ -47,8 +51,8 @@ func CreateBoilerplateCli(version string) *cli.App {

app.Flags = []cli.Flag{
cli.StringFlag{
Name: options.OptTemplateFolder,
Usage: "Generate the project from the templates in `FOLDER`.",
Name: options.OptTemplateUrl,
Usage: "Generate the project from the templates in `URL`. This can be a local path, or a go-getter compatible URL for remote templates (e.g., `git@github.com:gruntwork-io/boilerplate.git//examples/include?ref=master`).",
},
cli.StringFlag{
Name: options.OptOutputFolder,
Expand Down Expand Up @@ -91,8 +95,7 @@ func CreateBoilerplateCli(version string) *cli.App {
// When you run the CLI, this is the action function that gets called
func runApp(cliContext *cli.Context) error {
if !cliContext.Args().Present() && cliContext.NumFlags() == 0 {
cli.ShowAppHelp(cliContext)
return nil
return cli.ShowAppHelp(cliContext)
}

opts, err := options.ParseOptions(cliContext)
Expand Down
38 changes: 19 additions & 19 deletions config/config_test.go
Expand Up @@ -300,7 +300,7 @@ func TestParseBoilerplateConfigAllTypes(t *testing.T) {
// YAML is whitespace sensitive, so we need to be careful that we don't introduce unnecessary indentation
const CONFIG_ONE_DEPENDENCY = `dependencies:
- name: dep1
template-folder: /template/folder1
template-url: /template/folder1
output-folder: /output/folder1
`

Expand All @@ -311,7 +311,7 @@ func TestParseBoilerplateConfigOneDependency(t *testing.T) {
expected := &BoilerplateConfig{
Variables: []variables.Variable{},
Dependencies: []variables.Dependency{
{Name: "dep1", TemplateFolder: "/template/folder1", OutputFolder: "/output/folder1", DontInheritVariables: false, Variables: []variables.Variable{}},
{Name: "dep1", TemplateUrl: "/template/folder1", OutputFolder: "/output/folder1", DontInheritVariables: false, Variables: []variables.Variable{}},
},
Hooks: variables.Hooks{},
}
Expand All @@ -323,11 +323,11 @@ func TestParseBoilerplateConfigOneDependency(t *testing.T) {
// YAML is whitespace sensitive, so we need to be careful that we don't introduce unnecessary indentation
const CONFIG_MULTIPLE_DEPENDENCIES = `dependencies:
- name: dep1
template-folder: /template/folder1
template-url: /template/folder1
output-folder: /output/folder1
- name: dep2
template-folder: /template/folder2
template-url: /template/folder2
output-folder: /output/folder2
dont-inherit-variables: true
variables:
Expand All @@ -336,7 +336,7 @@ const CONFIG_MULTIPLE_DEPENDENCIES = `dependencies:
default: foo
- name: dep3
template-folder: /template/folder3
template-url: /template/folder3
output-folder: /output/folder3
skip: "{{ and .Foo .Bar }}"
`
Expand All @@ -350,14 +350,14 @@ func TestParseBoilerplateConfigMultipleDependencies(t *testing.T) {
Dependencies: []variables.Dependency{
{
Name: "dep1",
TemplateFolder: "/template/folder1",
TemplateUrl: "/template/folder1",
OutputFolder: "/output/folder1",
DontInheritVariables: false,
Variables: []variables.Variable{},
},
{
Name: "dep2",
TemplateFolder: "/template/folder2",
TemplateUrl: "/template/folder2",
OutputFolder: "/output/folder2",
DontInheritVariables: true,
Variables: []variables.Variable{
Expand All @@ -366,7 +366,7 @@ func TestParseBoilerplateConfigMultipleDependencies(t *testing.T) {
},
{
Name: "dep3",
TemplateFolder: "/template/folder3",
TemplateUrl: "/template/folder3",
OutputFolder: "/output/folder3",
DontInheritVariables: false,
Variables: []variables.Variable{},
Expand All @@ -382,7 +382,7 @@ func TestParseBoilerplateConfigMultipleDependencies(t *testing.T) {

// YAML is whitespace sensitive, so we need to be careful that we don't introduce unnecessary indentation
const CONFIG_DEPENDENCY_MISSING_NAME = `dependencies:
- template-folder: /template/folder1
- template-url: /template/folder1
output-folder: /output/folder1
`

Expand All @@ -401,19 +401,19 @@ const CONFIG_DEPENDENCY_MISSING_TEMPLATE_FOLDER = `dependencies:
output-folder: /output/folder1
`

func TestParseBoilerplateConfigDependencyMissingTemplateFolder(t *testing.T) {
func TestParseBoilerplateConfigDependencyMissingTemplateUrl(t *testing.T) {
t.Parallel()

_, err := ParseBoilerplateConfig([]byte(CONFIG_DEPENDENCY_MISSING_TEMPLATE_FOLDER))

assert.NotNil(t, err)
assert.True(t, errors.IsError(err, variables.RequiredFieldMissing("template-folder")), "Expected a RequiredFieldMissing error but got %s", reflect.TypeOf(errors.Unwrap(err)))
assert.True(t, errors.IsError(err, variables.RequiredFieldMissing("template-url")), "Expected a RequiredFieldMissing error but got %s", reflect.TypeOf(errors.Unwrap(err)))
}

// YAML is whitespace sensitive, so we need to be careful that we don't introduce unnecessary indentation
const CONFIG_DEPENDENCY_MISSING_VARIABLE_NAME = `dependencies:
- name: dep1
template-folder: /template/folder1
template-url: /template/folder1
output-folder: /output/folder1
variables:
- description: Enter foo
Expand All @@ -432,11 +432,11 @@ func TestParseBoilerplateConfigDependencyMissingVariableName(t *testing.T) {
// YAML is whitespace sensitive, so we need to be careful that we don't introduce unnecessary indentation
const CONFIG_DEPENDENCY_MISSING_OUTPUT_FOLDER = `dependencies:
- name: dep1
template-folder: /template/folder1
template-url: /template/folder1
output-folder: /output/folder1
- name: dep2
template-folder: /template/folder2
template-url: /template/folder2
`

func TestParseBoilerplateConfigDependencyMissingOutputFolder(t *testing.T) {
Expand All @@ -451,15 +451,15 @@ func TestParseBoilerplateConfigDependencyMissingOutputFolder(t *testing.T) {
// YAML is whitespace sensitive, so we need to be careful that we don't introduce unnecessary indentation
const CONFIG_DEPENDENCY_DUPLICATE_NAMES = `dependencies:
- name: dep1
template-folder: /template/folder1
template-url: /template/folder1
output-folder: /output/folder1
- name: dep2
template-folder: /template/folder2
template-url: /template/folder2
output-folder: /output/folder2
- name: dep1
template-folder: /template/folder3
template-url: /template/folder3
output-folder: /output/folder3
`

Expand Down Expand Up @@ -619,8 +619,8 @@ func TestLoadBoilerplateConfigFullConfig(t *testing.T) {
variables.NewStringVariable("baz").WithDescription("example description").WithDefault("default"),
},
Dependencies: []variables.Dependency{
{Name: "dep1", TemplateFolder: "/template/folder1", OutputFolder: "/output/folder1", DontInheritVariables: false, Variables: []variables.Variable{}},
{Name: "dep2", TemplateFolder: "/template/folder2", OutputFolder: "/output/folder2", DontInheritVariables: true, Variables: []variables.Variable{
{Name: "dep1", TemplateUrl: "/template/folder1", OutputFolder: "/output/folder1", DontInheritVariables: false, Variables: []variables.Variable{}},
{Name: "dep2", TemplateUrl: "/template/folder2", OutputFolder: "/output/folder2", DontInheritVariables: true, Variables: []variables.Variable{
variables.NewStringVariable("baz").WithDescription("example description").WithDefault("other-default"),
variables.NewStringVariable("abc").WithDescription("example description").WithDefault("default"),
}},
Expand Down
6 changes: 3 additions & 3 deletions examples/dependencies-dynamic/README.md
Expand Up @@ -2,6 +2,6 @@

{{ .Description }}. It specifies both the
[docs](/examples/docs) and [website](/examples/website) examples as dependencies to show how one boilerplate template
can pull in another, and that you can use interpolation in the `template-folder` and `output-folder` parameters of
dependencies to dynamically specify where to read the template and where to write the output. It also defines all the
variables needed for both of those dependencies at the top level to show how variable inheritance works.
can pull in another, and that you can use interpolation in the `template-url` and `output-folder` parameters of
dependencies to dynamically specify where to read the template and where to write the output. It also defines all the
variables needed for both of those dependencies at the top level to show how variable inheritance works.
10 changes: 5 additions & 5 deletions examples/dependencies-dynamic/boilerplate.yml
Expand Up @@ -17,7 +17,7 @@ variables:
default: true

- name: WebsiteTemplateFolder
description: The path to the website templates folder. This is used to check that interpolations work in the template-folder parameter of dependencies.
description: The path to the website templates folder. This is used to check that interpolations work in the template-url parameter of dependencies.

- name: WebsiteOutputFolder
description: The path to the website output folder. This is used to check that interpolations work in the output-folder parameter of dependencies.
Expand All @@ -39,26 +39,26 @@ variables:

dependencies:
- name: docs
template-folder: ../docs
template-url: ../docs
output-folder: ./docs
skip: "{{ .SkipAllDependencies }}"
variables:
- name: Title
description: Enter the title of the docs page

- name: website
template-folder: "{{ .WebsiteTemplateFolder }}"
template-url: "{{ .WebsiteTemplateFolder }}"
output-folder: "{{ .WebsiteOutputFolder }}"
skip: "{{ or .SkipAllDependencies .SkipFirstWebsiteDependency }}"
variables:
- name: Title
description: Enter the title of the website

- name: skip-website
template-folder: "{{ .WebsiteTemplateFolder }}"
template-url: "{{ .WebsiteTemplateFolder }}"
output-folder: "{{ .WebsiteOutputFolder }}"
skip: "{{ or .SkipAllDependencies .SkipSecondWebsiteDependency }}"
variables:
- name: Title
description: Enter the title of the website
default: This website dependency should be skipped
default: This website dependency should be skipped

0 comments on commit c6198a5

Please sign in to comment.