Skip to content

Commit db5f44a

Browse files
authored
fix: Fixing render --write when no --format is supplied (#6724)
1 parent 734718d commit db5f44a

4 files changed

Lines changed: 93 additions & 32 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
version: "v1.1.4"
3+
category: "bug-fixes"
4+
---
5+
6+
#### `render --write` picks a default filename without a format flag
7+
8+
`terragrunt render --write` failed with `is a directory` unless it was paired with `--format` or `--json`. Only those flags set the default filename, so a bare `--write` had no output path and Terragrunt tried to write to the unit directory itself.
9+
10+
The default now follows the format in use. `terragrunt render --write` writes `terragrunt.rendered.hcl` next to the unit configuration, and `--json` or `--format=json` writes `terragrunt.rendered.json`. An explicit `--out` still takes precedence.

internal/cli/commands/render/cli.go

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ package render
44
import (
55
"context"
66

7-
"errors"
8-
97
runcmd "github.com/gruntwork-io/terragrunt/internal/cli/commands/run"
108
"github.com/gruntwork-io/terragrunt/internal/cli/flags"
119
"github.com/gruntwork-io/terragrunt/internal/cli/flags/shared"
@@ -37,39 +35,19 @@ func NewFlags(opts *Options, prefix flags.Prefix) clihelper.Flags {
3735
Name: FormatFlagName,
3836
EnvVars: tgPrefix.EnvVars(FormatFlagName),
3937
Destination: &opts.Format,
40-
Usage: "The output format to render the config in. Currently supports: json",
38+
Usage: "The output format to render the config in. Supported values: hcl, json.",
4139
Action: func(_ context.Context, _ *clihelper.Context, value string) error {
42-
// Set the default output path based on the format.
43-
switch value {
44-
case FormatJSON:
45-
if opts.OutputPath == "" {
46-
opts.OutputPath = "terragrunt.rendered.json"
47-
}
48-
49-
return nil
50-
case FormatHCL:
51-
if opts.OutputPath == "" {
52-
opts.OutputPath = "terragrunt.rendered.hcl"
53-
}
54-
55-
return nil
56-
default:
57-
return errors.New("invalid format: " + value)
58-
}
40+
return validateFormat(value)
5941
},
6042
}),
6143

6244
flags.NewFlag(&clihelper.BoolFlag{
6345
Name: JSONFlagName,
6446
EnvVars: tgPrefix.EnvVars(JSONFlagName),
6547
Usage: "Render the config in JSON format. Equivalent to --format=json.",
66-
Action: func(_ context.Context, _ *clihelper.Context, value bool) error {
48+
Action: func(_ context.Context, _ *clihelper.Context, _ bool) error {
6749
opts.Format = FormatJSON
6850

69-
if opts.OutputPath == "" {
70-
opts.OutputPath = "terragrunt.rendered.json"
71-
}
72-
7351
return nil
7452
},
7553
}),

internal/cli/commands/render/options.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ const (
1212

1313
// FormatJSON outputs the config in JSON format.
1414
FormatJSON = "json"
15+
16+
hclOutputName = "terragrunt.rendered.hcl"
17+
jsonOutputName = "terragrunt.rendered.json"
1518
)
1619

1720
type Options struct {
@@ -50,21 +53,33 @@ func (o *Options) Clone() *Options {
5053
}
5154
}
5255

56+
// Validate rejects an unrecognized format. It also fills in [Options.OutputPath] with a
57+
// name derived from the format when a write is requested without one.
5358
func (o *Options) Validate() error {
54-
if err := o.validateFormat(); err != nil {
59+
if err := validateFormat(o.Format); err != nil {
5560
return err
5661
}
5762

63+
if o.Write && o.OutputPath == "" {
64+
o.OutputPath = defaultOutputName(o.Format)
65+
}
66+
5867
return nil
5968
}
6069

61-
func (o *Options) validateFormat() error {
62-
switch o.Format {
63-
case FormatHCL:
64-
return nil
65-
case FormatJSON:
70+
func validateFormat(format string) error {
71+
switch format {
72+
case FormatHCL, FormatJSON:
6673
return nil
6774
default:
68-
return errors.New("invalid format: " + o.Format)
75+
return errors.New("invalid format: " + format)
6976
}
7077
}
78+
79+
func defaultOutputName(format string) string {
80+
if format == FormatJSON {
81+
return jsonOutputName
82+
}
83+
84+
return hclOutputName
85+
}

internal/cli/commands/render/render_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,64 @@ func TestRenderJSON_WriteToFile(t *testing.T) {
104104
validateRenderedJSON(t, result, false)
105105
}
106106

107+
func TestRenderWriteWithoutOutputPath(t *testing.T) {
108+
t.Parallel()
109+
110+
testCases := []struct {
111+
check func(t *testing.T, content []byte)
112+
name string
113+
format string
114+
filename string
115+
}{
116+
{
117+
name: "hcl",
118+
format: render.FormatHCL,
119+
filename: "terragrunt.rendered.hcl",
120+
check: func(t *testing.T, content []byte) {
121+
t.Helper()
122+
123+
assert.Equal(t, testTerragruntConfigFixture, string(content))
124+
},
125+
},
126+
{
127+
name: "json",
128+
format: render.FormatJSON,
129+
filename: "terragrunt.rendered.json",
130+
check: func(t *testing.T, content []byte) {
131+
t.Helper()
132+
133+
var result map[string]any
134+
135+
require.NoError(t, json.Unmarshal(content, &result))
136+
validateRenderedJSON(t, result, false)
137+
},
138+
},
139+
}
140+
141+
for _, tc := range testCases {
142+
t.Run(tc.name, func(t *testing.T) {
143+
t.Parallel()
144+
145+
opts, configPath := setupTest(t)
146+
opts.Format = tc.format
147+
opts.Write = true
148+
149+
err := render.Run(
150+
t.Context(),
151+
logger.CreateLogger(),
152+
venvtest.NewOSWithEmptyEnv().WithWriter(io.Discard),
153+
opts,
154+
)
155+
require.NoError(t, err)
156+
157+
content, err := os.ReadFile(filepath.Join(filepath.Dir(configPath), tc.filename))
158+
require.NoError(t, err)
159+
160+
tc.check(t, content)
161+
})
162+
}
163+
}
164+
107165
func TestRenderJSON_InvalidFormat(t *testing.T) {
108166
t.Parallel()
109167

0 commit comments

Comments
 (0)