Skip to content

Commit 679fa0c

Browse files
author
Tim Eijgelshoven
committed
feat: Adding custom default template (#3658)
1 parent 948076b commit 679fa0c

File tree

7 files changed

+111
-55
lines changed

7 files changed

+111
-55
lines changed

cli/commands/catalog/module/module.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212

1313
const (
1414
defaultDescription = "(no description found)"
15-
maxDescriptionLenght = 200
15+
maxDescriptionLength = 200
1616
)
1717

1818
var (
@@ -86,7 +86,7 @@ func (module *Module) Title() string {
8686

8787
// Description implements /github.com/charmbracelet/bubbles.list.DefaultItem.Description
8888
func (module *Module) Description() string {
89-
if desc := module.Doc.Description(maxDescriptionLenght); desc != "" {
89+
if desc := module.Doc.Description(maxDescriptionLength); desc != "" {
9090
return desc
9191
}
9292

cli/commands/scaffold/action.go

Lines changed: 70 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -221,62 +221,93 @@ func Run(ctx context.Context, opts *options.TerragruntOptions, moduleURL, templa
221221
return nil
222222
}
223223

224-
// prepareBoilerplateFiles prepares boilerplate files.
225-
func prepareBoilerplateFiles(ctx context.Context, opts *options.TerragruntOptions, templateURL string, tempDir string) (string, error) {
226-
// identify template url
227-
templateDir := ""
224+
// generateDefaultTemplate - write default template to provided dir
225+
func generateDefaultTemplate(boilerplateDir string) (string, error) {
226+
const ownerWriteGlobalReadPerms = 0644
227+
if err := os.WriteFile(util.JoinPath(boilerplateDir, "terragrunt.hcl"), []byte(DefaultTerragruntTemplate), ownerWriteGlobalReadPerms); err != nil {
228+
return "", errors.New(err)
229+
}
228230

229-
if templateURL != "" {
230-
// process template url if was passed
231-
parsedTemplateURL, err := tf.ToSourceURL(templateURL, tempDir)
232-
if err != nil {
233-
return "", errors.New(err)
234-
}
231+
if err := os.WriteFile(util.JoinPath(boilerplateDir, "boilerplate.yml"), []byte(DefaultBoilerplateConfig), ownerWriteGlobalReadPerms); err != nil {
232+
return "", errors.New(err)
233+
}
235234

236-
parsedTemplateURL, err = rewriteTemplateURL(ctx, opts, parsedTemplateURL)
237-
if err != nil {
238-
return "", errors.New(err)
239-
}
240-
// regenerate template url with all changes
241-
templateURL = parsedTemplateURL.String()
235+
return boilerplateDir, nil
236+
}
242237

243-
// prepare temporary directory for template
244-
templateDir, err = os.MkdirTemp("", "template")
245-
if err != nil {
246-
return "", errors.New(err)
247-
}
238+
// parseTemplateURL - parse URL and download files
239+
func parseTemplateURL(ctx context.Context, opts *options.TerragruntOptions, templateURL string, tempDir string) (string, error) {
240+
parsedTemplateURL, err := tf.ToSourceURL(templateURL, tempDir)
241+
if err != nil {
242+
return "", errors.New(err)
243+
}
248244

249-
// downloading template
250-
opts.Logger.Infof("Using template from %s", templateURL)
245+
parsedTemplateURL, err = rewriteTemplateURL(ctx, opts, parsedTemplateURL)
246+
if err != nil {
247+
return "", errors.New(err)
248+
}
249+
// regenerate template url with all changes
250+
templateURL = parsedTemplateURL.String()
251251

252-
if _, err := getter.GetAny(ctx, templateDir, templateURL); err != nil {
253-
return "", errors.New(err)
254-
}
252+
// prepare temporary directory for template
253+
templateDir, err := os.MkdirTemp("", "template")
254+
if err != nil {
255+
return "", errors.New(err)
256+
}
257+
258+
// downloading templateURL
259+
opts.Logger.Infof("Using template from %s", templateURL)
260+
261+
if _, err := getter.GetAny(ctx, templateDir, templateURL); err != nil {
262+
return "", errors.New(err)
255263
}
256-
// prepare boilerplate dir
264+
265+
return templateDir, nil
266+
}
267+
268+
// prepareBoilerplateFiles - prepare boilerplate files from provided template, tf module, or (custom) default template
269+
func prepareBoilerplateFiles(ctx context.Context, opts *options.TerragruntOptions, templateURL string, tempDir string) (string, error) {
257270
boilerplateDir := util.JoinPath(tempDir, util.DefaultBoilerplateDir)
258-
// use template dir as boilerplate dir
259-
if templateDir != "" {
260-
boilerplateDir = templateDir
271+
272+
// process template url if it was passed. This overrides the .boilerplate folder in the OpenTofu/Terraform module
273+
if templateURL != "" {
274+
// process template url if it was passed
275+
tempTemplateDir, err := parseTemplateURL(ctx, opts, templateURL, tempDir)
276+
if err != nil {
277+
return "", errors.New(err)
278+
}
279+
280+
boilerplateDir = tempTemplateDir
261281
}
262282

263283
// if boilerplate dir is not found, create one with default template
264284
if !files.IsExistingDir(boilerplateDir) {
265-
// no default boilerplate dir, create one
266-
defaultTempDir, err := os.MkdirTemp("", "boilerplate")
285+
config, err := config.ReadCatalogConfig(ctx, opts)
267286
if err != nil {
268287
return "", errors.New(err)
269288
}
270289

271-
boilerplateDir = defaultTempDir
290+
// use defaultTemplateURL if defined in config, otherwise use basic default template
291+
if config != nil && config.DefaultTemplate != "" {
292+
// process template url if available
293+
tempTemplateDir, err := parseTemplateURL(ctx, opts, config.DefaultTemplate, tempDir)
294+
if err != nil {
295+
return "", errors.New(err)
296+
}
272297

273-
const ownerWriteGlobalReadPerms = 0644
274-
if err := os.WriteFile(util.JoinPath(boilerplateDir, "terragrunt.hcl"), []byte(DefaultTerragruntTemplate), ownerWriteGlobalReadPerms); err != nil {
275-
return "", errors.New(err)
276-
}
298+
boilerplateDir = tempTemplateDir
299+
} else {
300+
defaultTempDir, err := os.MkdirTemp("", "boilerplate")
301+
if err != nil {
302+
return "", errors.New(err)
303+
}
277304

278-
if err := os.WriteFile(util.JoinPath(boilerplateDir, "boilerplate.yml"), []byte(DefaultBoilerplateConfig), ownerWriteGlobalReadPerms); err != nil {
279-
return "", errors.New(err)
305+
boilerplateDir = defaultTempDir
306+
307+
boilerplateDir, err = generateDefaultTemplate(boilerplateDir)
308+
if err != nil {
309+
return "", errors.New(err)
310+
}
280311
}
281312
}
282313

config/catalog.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ var (
3737
)
3838

3939
type CatalogConfig struct {
40-
URLs []string `hcl:"urls,attr" cty:"urls"`
40+
URLs []string `hcl:"urls,attr" cty:"urls"`
41+
DefaultTemplate string `hcl:"default_template,attr" cty:"default_template"`
4142
}
4243

4344
func (cfg *CatalogConfig) String() string {
44-
return fmt.Sprintf("Catalog{URLs = %v}", cfg.URLs)
45+
return fmt.Sprintf("Catalog{URLs = %v, DefaultTemplate = %v}", cfg.URLs, cfg.DefaultTemplate)
4546
}
4647

4748
func (cfg *CatalogConfig) normalize(configPath string) {

config/catalog_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ func TestCatalogParseConfigFile(t *testing.T) {
125125
},
126126
},
127127
nil,
128+
}, {
129+
filepath.Join(basePath, "config4.hcl"),
130+
&config.CatalogConfig{
131+
DefaultTemplate: "/test/fixtures/scaffold/external-template",
132+
},
133+
nil,
128134
},
129135
}
130136

docs/_docs/02_features/05-catalog.md

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,30 @@ terragrunt catalog <repo-url> [--no-include-root] [--root-file-name] [--output-f
2323

2424
If `<repo-url>` is provided, the repository will be cloned into a temporary directory, otherwise:
2525

26-
1. The repository list are searched in the config file `terragrunt.hcl`. if `terragrunt.hcl` does not exist in the current directory, the config are searched in the parent directories.
26+
1. The repository list are searched in the [config file](#configuration) `terragrunt.hcl`. if `terragrunt.hcl` does not exist in the current directory, the config are searched in the parent directories.
2727
1. If the repository list is not found in the configuration file, the modules are looked for in the current directory.
2828

29-
An example of how to define the list of repositories for the `catalog` command in the `terragrunt.hcl` configuration file:
29+
The provided repositories will recursively be searched for OpenTofu/Terraform modules in the root of the repo and the `modules` directory. A table with all the discovered Terraform modules will subsequently be displayed. You can then:
30+
31+
1. Search and filter the table: `/` and start typing.
32+
1. Select a module in the table: use the arrow keys to go up and down and next/previous page.
33+
1. See the docs for a selected module: `ENTER`.
34+
1. Use [`terragrunt scaffold`]({{site.baseurl}}/docs/features/scaffold/) to render a `terragrunt.hcl` for using the module: `S`.
35+
36+
### Custom templates for scaffolding
37+
38+
Terragrunt has a basic template built-in for rendering `terragrunt.hcl` files, but you can provide your own templates to customize what code is generated! Scaffolding is done via [boilerplate](https://github.com/gruntwork-io/boilerplate), and Terragrunt allows you to specify custom boilerplate templates via two mechanisms while using catalog:
39+
40+
1. You can define a custom boilerplate template in a `.boilerplate` subfolder of your Terraform module.
41+
1. You can specify a custom boilerplate template in the [catalog config](#configuration).
42+
43+
### Configuration
44+
45+
An example of how to define the optional default template, and the list of repositories for the `catalog` command in the `terragrunt.hcl` configuration file:
3046

3147
``` hcl
3248
catalog {
49+
default_template = "[email protected]/gruntwork-io/terragrunt.git//test/fixture-scaffold/external-template"
3350
urls = [
3451
"relative/path/to/repo", # will be converted to the absolute path, relative to the path of the configuration file.
3552
"/absolute/path/to/repo",
@@ -39,17 +56,10 @@ catalog {
3956
}
4057
```
4158

42-
This will recursively search for OpenTofu/Terraform modules in the root of the repo and the `modules` directory and show a table with all the modules. You can then:
43-
44-
1. Search and filter the table: `/` and start typing.
45-
1. Select a module in the table: use the arrow keys to go up and down and next/previous page.
46-
1. See the docs for a selected module: `ENTER`.
47-
1. Use [`terragrunt scaffold`](https://terragrunt.gruntwork.io/docs/features/scaffold/) to render a `terragrunt.hcl` for using the module: `S`.
48-
4959
## Scaffolding Flags
5060

5161
The following `catalog` flags control behavior of the underlying `scaffold` command when the `S` key is pressed in a catalog entry:
5262

5363
- `--no-include-root` - Do not include the root configuration file in any generated `terragrunt.hcl` during scaffolding.
5464
- `--root-file-name` - The name of the root configuration file to include in any generated `terragrunt.hcl` during scaffolding. This value also controls the name of the root configuration file to search for when trying to determine Catalog urls.
55-
- `--output-folder` - Location for generated `terragrunt.hcl`. If flag is not provided current working directory is selected.
65+
- `--output-folder` - Location for generated `terragrunt.hcl`. If flag is not provided current working directory is selected.

docs/_docs/02_features/06-scaffold.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,11 @@ Important notes:
6868

6969
## Custom templates for scaffolding
7070

71-
Terragrunt has a basic template built-in for rendering `terragrunt.hcl` files, but you can provide your own templates to customize what code is generated! Scaffolding is done via [boilerplate](https://github.com/gruntwork-io/boilerplate), and Terragrunt allows you to specify custom boilerplate templates via two mechanisms:
71+
Terragrunt has a basic template built-in for rendering `terragrunt.hcl` files, but you can provide your own templates to customize what code is generated! Scaffolding is done via [boilerplate](https://github.com/gruntwork-io/boilerplate), and Terragrunt allows you to specify custom boilerplate templates via three mechanisms - listed in order of priority:
7272

7373
1. You can specify a custom boilerplate template to use as the second argument of the `scaffold` command.
74-
1. You can define a custom boilerplate template in a `.boilerplate` subfolder of your module.
74+
1. You can define a custom boilerplate template in a `.boilerplate` subfolder of your Terraform module.
75+
1. You can define a default custom boilerplate template in the [catalog config]({{site.baseurl}}/docs/features/catalog/#configuration)
7576

7677
If you define input variables in your boilerplate template, Terragrunt will prompt users for the values. Those values can also be passed in via `--var` and `--var-file` arguments.
7778
There are also a set of variables that Terragrunt will automatically expose to your boilerplate templates for rendering:

test/fixtures/catalog/config4.hcl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
locals {
2+
baseRepo = "github.com/gruntwork-io"
3+
}
4+
5+
catalog {
6+
default_template = "/test/fixtures/scaffold/external-template"
7+
}

0 commit comments

Comments
 (0)