Skip to content

Commit bf84c7b

Browse files
committed
chore: Bumping boilerplate (#4805)
* chore: Bumping Boilerplate to `v0.10.0` * fix: Adding support for `no-shell` and `no-hooks` * feat: Adding tests for scaffold * fix: Fixing initialization of BoilerplateOptions * chore: Refactoring out shared `scaffold` flags to `shared` * docs: Updating docs with additional call-out * chore: Upgrading `boilerplate` * fix: Addressing lints * fix: Fixing test for Boilerplate bump * fix: Addressing lints * fix: Ignoring boilerplated configurations during tests
1 parent c70563d commit bf84c7b

File tree

29 files changed

+1454
-186
lines changed

29 files changed

+1454
-186
lines changed

cli/commands/catalog/cli.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package catalog
55
import (
66
"github.com/gruntwork-io/terragrunt/cli/commands/scaffold"
77
"github.com/gruntwork-io/terragrunt/cli/flags"
8+
"github.com/gruntwork-io/terragrunt/cli/flags/shared"
89
"github.com/gruntwork-io/terragrunt/internal/cli"
910
"github.com/gruntwork-io/terragrunt/options"
1011
"github.com/gruntwork-io/terragrunt/pkg/log"
@@ -15,10 +16,7 @@ const (
1516
)
1617

1718
func NewFlags(opts *options.TerragruntOptions, prefix flags.Prefix) cli.Flags {
18-
return scaffold.NewFlags(opts, prefix).Filter(
19-
scaffold.RootFileNameFlagName,
20-
scaffold.NoIncludeRootFlagName,
21-
)
19+
return shared.NewScaffoldingFlags(opts, prefix)
2220
}
2321

2422
func NewCommand(l log.Logger, opts *options.TerragruntOptions) *cli.Command {

cli/commands/scaffold/cli.go

Lines changed: 12 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import (
88

99
runCmd "github.com/gruntwork-io/terragrunt/cli/commands/run"
1010
"github.com/gruntwork-io/terragrunt/cli/flags"
11+
"github.com/gruntwork-io/terragrunt/cli/flags/shared"
1112
"github.com/gruntwork-io/terragrunt/config"
1213
"github.com/gruntwork-io/terragrunt/internal/cli"
13-
"github.com/gruntwork-io/terragrunt/internal/errors"
1414
"github.com/gruntwork-io/terragrunt/internal/strict/controls"
1515
"github.com/gruntwork-io/terragrunt/options"
1616
"github.com/gruntwork-io/terragrunt/pkg/log"
@@ -19,49 +19,20 @@ import (
1919
const (
2020
CommandName = "scaffold"
2121

22-
RootFileNameFlagName = "root-file-name"
23-
NoIncludeRootFlagName = "no-include-root"
24-
OutputFolderFlagName = "output-folder"
25-
VarFlagName = "var"
26-
VarFileFlagName = "var-file"
27-
NoDependencyPrompt = "no-dependency-prompt"
22+
OutputFolderFlagName = "output-folder"
23+
VarFlagName = "var"
24+
VarFileFlagName = "var-file"
25+
NoDependencyPrompt = "no-dependency-prompt"
2826
)
2927

3028
func NewFlags(opts *options.TerragruntOptions, prefix flags.Prefix) cli.Flags {
3129
tgPrefix := prefix.Prepend(flags.TgPrefix)
3230

33-
return cli.Flags{
34-
flags.NewFlag(&cli.GenericFlag[string]{
35-
Name: RootFileNameFlagName,
36-
EnvVars: tgPrefix.EnvVars(RootFileNameFlagName),
37-
Destination: &opts.ScaffoldRootFileName,
38-
Usage: "Name of the root Terragrunt configuration file, if used.",
39-
Action: func(ctx *cli.Context, value string) error {
40-
if value == "" {
41-
return errors.New("root-file-name flag cannot be empty")
42-
}
43-
44-
if value != opts.TerragruntConfigPath {
45-
opts.ScaffoldRootFileName = value
46-
47-
return nil
48-
}
49-
50-
if err := opts.StrictControls.FilterByNames(controls.RootTerragruntHCL).Evaluate(ctx); err != nil {
51-
return cli.NewExitError(err, cli.ExitCodeGeneralError)
52-
}
53-
54-
return nil
55-
},
56-
}),
57-
58-
flags.NewFlag(&cli.BoolFlag{
59-
Name: NoIncludeRootFlagName,
60-
EnvVars: tgPrefix.EnvVars(NoIncludeRootFlagName),
61-
Destination: &opts.ScaffoldNoIncludeRoot,
62-
Usage: "Do not include root unit in scaffolding done by catalog.",
63-
}),
31+
// Start with shared scaffolding flags
32+
scaffoldFlags := shared.NewScaffoldingFlags(opts, prefix)
6433

34+
// Add scaffold-specific flags
35+
scaffoldFlags = append(scaffoldFlags,
6536
flags.NewFlag(&cli.GenericFlag[string]{
6637
Name: OutputFolderFlagName,
6738
Destination: &opts.ScaffoldOutputFolder,
@@ -88,7 +59,9 @@ func NewFlags(opts *options.TerragruntOptions, prefix flags.Prefix) cli.Flags {
8859
Destination: &opts.NoDependencyPrompt,
8960
Usage: "Do not prompt for confirmation to include dependencies.",
9061
}),
91-
}
62+
)
63+
64+
return scaffoldFlags
9265
}
9366

9467
func NewCommand(l log.Logger, opts *options.TerragruntOptions) *cli.Command {

cli/commands/scaffold/scaffold.go

Lines changed: 132 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111

1212
"github.com/gruntwork-io/terragrunt/cli/commands/hcl/format"
13+
"github.com/gruntwork-io/terragrunt/cli/flags/shared"
1314
"github.com/gruntwork-io/terragrunt/config"
1415
"github.com/gruntwork-io/terragrunt/pkg/log"
1516
"github.com/gruntwork-io/terragrunt/shell"
@@ -106,7 +107,31 @@ const (
106107
rootFileName = "RootFileName"
107108
)
108109

110+
// NewBoilerplateOptions creates a new BoilerplateOptions struct
111+
func NewBoilerplateOptions(
112+
templateFolder,
113+
outputFolder string,
114+
vars map[string]any,
115+
terragruntOpts *options.TerragruntOptions,
116+
) *boilerplate_options.BoilerplateOptions {
117+
return &boilerplate_options.BoilerplateOptions{
118+
TemplateFolder: templateFolder,
119+
OutputFolder: outputFolder,
120+
OnMissingKey: boilerplate_options.DefaultMissingKeyAction,
121+
OnMissingConfig: boilerplate_options.DefaultMissingConfigAction,
122+
Vars: vars,
123+
ShellCommandAnswers: map[string]bool{},
124+
NoShell: terragruntOpts.NoShell,
125+
NoHooks: terragruntOpts.NoHooks,
126+
NonInteractive: terragruntOpts.NonInteractive,
127+
DisableDependencyPrompt: terragruntOpts.NoDependencyPrompt,
128+
}
129+
}
130+
109131
func Run(ctx context.Context, l log.Logger, opts *options.TerragruntOptions, moduleURL, templateURL string) error {
132+
// Apply catalog configuration settings, with CLI flags taking precedence
133+
applyCatalogConfigToScaffold(ctx, l, opts)
134+
110135
// download remote repo to local
111136
var dirsToClean []string
112137
// clean all temp dirs
@@ -186,27 +211,25 @@ func Run(ctx context.Context, l log.Logger, opts *options.TerragruntOptions, mod
186211
if _, found := vars[enableRootInclude]; !found {
187212
vars[enableRootInclude] = !opts.ScaffoldNoIncludeRoot
188213
} else {
189-
l.Warnf("The %s variable is already set in the var flag(s). The --%s flag will be ignored.", enableRootInclude, NoIncludeRootFlagName)
214+
l.Warnf(
215+
"The %s variable is already set in the var flag(s). The --%s flag will be ignored.",
216+
enableRootInclude,
217+
shared.NoIncludeRootFlagName,
218+
)
190219
}
191220

192221
if _, found := vars[rootFileName]; !found {
193222
vars[rootFileName] = opts.ScaffoldRootFileName
194223
} else {
195-
l.Warnf("The %s variable is already set in the var flag(s). The --%s flag will be ignored.", rootFileName, NoIncludeRootFlagName)
224+
l.Warnf(
225+
"The %s variable is already set in the var flag(s). The --%s flag will be ignored.",
226+
rootFileName,
227+
shared.NoIncludeRootFlagName,
228+
)
196229
}
197230

198231
l.Infof("Running boilerplate generation to %s", outputDir)
199-
boilerplateOpts := &boilerplate_options.BoilerplateOptions{
200-
OutputFolder: outputDir,
201-
OnMissingKey: boilerplate_options.DefaultMissingKeyAction,
202-
OnMissingConfig: boilerplate_options.DefaultMissingConfigAction,
203-
Vars: vars,
204-
DisableShell: true,
205-
DisableHooks: true,
206-
NonInteractive: opts.NonInteractive,
207-
DisableDependencyPrompt: opts.NoDependencyPrompt,
208-
TemplateFolder: boilerplateDir,
209-
}
232+
boilerplateOpts := NewBoilerplateOptions(boilerplateDir, outputDir, vars, opts)
210233

211234
emptyDep := variables.Dependency{}
212235
if err := templates.ProcessTemplate(boilerplateOpts, boilerplateOpts, emptyDep); err != nil {
@@ -224,22 +247,72 @@ func Run(ctx context.Context, l log.Logger, opts *options.TerragruntOptions, mod
224247
return nil
225248
}
226249

250+
// applyCatalogConfigToScaffold applies catalog configuration settings to scaffold options.
251+
// CLI flags take precedence over config file settings.
252+
func applyCatalogConfigToScaffold(ctx context.Context, l log.Logger, opts *options.TerragruntOptions) {
253+
catalogCfg, err := config.ReadCatalogConfig(ctx, l, opts)
254+
if err != nil {
255+
// Don't fail if catalog config can't be read - it's optional
256+
l.Debugf("Could not read catalog config for scaffold: %v", err)
257+
return
258+
}
259+
260+
if catalogCfg == nil {
261+
return
262+
}
263+
264+
// Apply config settings only if CLI flags weren't explicitly set
265+
// Since both NoShell and NoHooks default to false, we apply the config value
266+
// only if it's true (enabling the restriction)
267+
if catalogCfg.NoShell != nil && *catalogCfg.NoShell && !opts.NoShell {
268+
l.Debugf("Applying catalog config: no_shell = true")
269+
270+
opts.NoShell = true
271+
}
272+
273+
if catalogCfg.NoHooks != nil && *catalogCfg.NoHooks && !opts.NoHooks {
274+
l.Debugf("Applying catalog config: no_hooks = true")
275+
276+
opts.NoHooks = true
277+
}
278+
}
279+
227280
// generateDefaultTemplate - write default template to provided dir
228281
func generateDefaultTemplate(boilerplateDir string) (string, error) {
229282
const ownerWriteGlobalReadPerms = 0644
230-
if err := os.WriteFile(util.JoinPath(boilerplateDir, config.DefaultTerragruntConfigPath), []byte(DefaultTerragruntTemplate), ownerWriteGlobalReadPerms); err != nil {
283+
if err := os.WriteFile(
284+
util.JoinPath(
285+
boilerplateDir,
286+
config.DefaultTerragruntConfigPath,
287+
),
288+
[]byte(DefaultTerragruntTemplate),
289+
ownerWriteGlobalReadPerms,
290+
); err != nil {
231291
return "", errors.New(err)
232292
}
233293

234-
if err := os.WriteFile(util.JoinPath(boilerplateDir, "boilerplate.yml"), []byte(DefaultBoilerplateConfig), ownerWriteGlobalReadPerms); err != nil {
294+
if err := os.WriteFile(
295+
util.JoinPath(
296+
boilerplateDir,
297+
"boilerplate.yml",
298+
),
299+
[]byte(DefaultBoilerplateConfig),
300+
ownerWriteGlobalReadPerms,
301+
); err != nil {
235302
return "", errors.New(err)
236303
}
237304

238305
return boilerplateDir, nil
239306
}
240307

241308
// downloadTemplate - parse URL, download files, and handle subfolders
242-
func downloadTemplate(ctx context.Context, l log.Logger, opts *options.TerragruntOptions, templateURL string, tempDir string) (string, error) {
309+
func downloadTemplate(
310+
ctx context.Context,
311+
l log.Logger,
312+
opts *options.TerragruntOptions,
313+
templateURL,
314+
tempDir string,
315+
) (string, error) {
243316
parsedTemplateURL, err := tf.ToSourceURL(templateURL, tempDir)
244317
if err != nil {
245318
return "", errors.New(err)
@@ -278,15 +351,25 @@ func downloadTemplate(ctx context.Context, l log.Logger, opts *options.Terragrun
278351
templateDir = filepath.Join(templateDir, subFolder)
279352
// Verify that subfolder exists
280353
if _, err := os.Stat(templateDir); os.IsNotExist(err) {
281-
return "", errors.Errorf("subfolder \"//%s\" not found in downloaded template from %s", subFolder, templateURL)
354+
return "", errors.Errorf(
355+
"subfolder \"//%s\" not found in downloaded template from %s",
356+
subFolder,
357+
templateURL,
358+
)
282359
}
283360
}
284361

285362
return templateDir, nil
286363
}
287364

288365
// prepareBoilerplateFiles - prepare boilerplate files from provided template, tf module, or (custom) default template
289-
func prepareBoilerplateFiles(ctx context.Context, l log.Logger, opts *options.TerragruntOptions, templateURL string, tempDir string) (string, error) {
366+
func prepareBoilerplateFiles(
367+
ctx context.Context,
368+
l log.Logger,
369+
opts *options.TerragruntOptions,
370+
templateURL,
371+
tempDir string,
372+
) (string, error) {
290373
boilerplateDir := util.JoinPath(tempDir, util.DefaultBoilerplateDir)
291374

292375
// process template url if it was passed. This overrides the .boilerplate folder in the OpenTofu/Terraform module
@@ -335,7 +418,11 @@ func prepareBoilerplateFiles(ctx context.Context, l log.Logger, opts *options.Te
335418
}
336419

337420
// parseVariables - parse variables from tf files.
338-
func parseVariables(l log.Logger, opts *options.TerragruntOptions, moduleDir string) ([]*config.ParsedVariable, []*config.ParsedVariable, error) {
421+
func parseVariables(
422+
l log.Logger,
423+
opts *options.TerragruntOptions,
424+
moduleDir string,
425+
) ([]*config.ParsedVariable, []*config.ParsedVariable, error) {
339426
inputs, err := config.ParseVariables(l, opts, moduleDir)
340427
if err != nil {
341428
return nil, nil, errors.New(err)
@@ -359,7 +446,13 @@ func parseVariables(l log.Logger, opts *options.TerragruntOptions, moduleDir str
359446
}
360447

361448
// parseModuleURL - parse module url and rewrite it if required
362-
func parseModuleURL(ctx context.Context, l log.Logger, opts *options.TerragruntOptions, vars map[string]any, moduleURL string) (string, error) {
449+
func parseModuleURL(
450+
ctx context.Context,
451+
l log.Logger,
452+
opts *options.TerragruntOptions,
453+
vars map[string]any,
454+
moduleURL string,
455+
) (string, error) {
363456
parsedModuleURL, err := tf.ToSourceURL(moduleURL, opts.WorkingDir)
364457
if err != nil {
365458
return "", errors.New(err)
@@ -385,7 +478,12 @@ func parseModuleURL(ctx context.Context, l log.Logger, opts *options.TerragruntO
385478

386479
// rewriteModuleURL rewrites module url to git ssh if required
387480
// github.com/gruntwork-io/terragrunt.git//test/fixtures/inputs => git::https://github.com/gruntwork-io/terragrunt.git//test/fixtures/inputs
388-
func rewriteModuleURL(l log.Logger, opts *options.TerragruntOptions, vars map[string]any, moduleURL string) (*url.URL, error) {
481+
func rewriteModuleURL(
482+
l log.Logger,
483+
opts *options.TerragruntOptions,
484+
vars map[string]any,
485+
moduleURL string,
486+
) (*url.URL, error) {
389487
var updatedModuleURL = moduleURL
390488

391489
sourceURLType := sourceURLTypeHTTPS
@@ -428,7 +526,12 @@ func rewriteModuleURL(l log.Logger, opts *options.TerragruntOptions, vars map[st
428526

429527
// rewriteTemplateURL rewrites template url with reference to tag
430528
// github.com/denis256/terragrunt-tests.git//scaffold/base-template => github.com/denis256/terragrunt-tests.git//scaffold/base-template?ref=v0.53.8
431-
func rewriteTemplateURL(ctx context.Context, l log.Logger, opts *options.TerragruntOptions, parsedTemplateURL *url.URL) (*url.URL, error) {
529+
func rewriteTemplateURL(
530+
ctx context.Context,
531+
l log.Logger,
532+
opts *options.TerragruntOptions,
533+
parsedTemplateURL *url.URL,
534+
) (*url.URL, error) {
432535
var (
433536
updatedTemplateURL = parsedTemplateURL
434537
templateParams = updatedTemplateURL.Query()
@@ -459,7 +562,13 @@ func rewriteTemplateURL(ctx context.Context, l log.Logger, opts *options.Terragr
459562
}
460563

461564
// addRefToModuleURL adds ref to module url if is passed through variables or find it from git tags
462-
func addRefToModuleURL(ctx context.Context, l log.Logger, opts *options.TerragruntOptions, parsedModuleURL *url.URL, vars map[string]any) (*url.URL, error) {
565+
func addRefToModuleURL(
566+
ctx context.Context,
567+
l log.Logger,
568+
opts *options.TerragruntOptions,
569+
parsedModuleURL *url.URL,
570+
vars map[string]any,
571+
) (*url.URL, error) {
463572
var moduleURL = parsedModuleURL
464573
// append ref to source url, if is passed through variables or find it from git tags
465574
params := moduleURL.Query()

0 commit comments

Comments
 (0)