Skip to content

Commit a5b4751

Browse files
committed
chore: tests improvements
1 parent 730e4df commit a5b4751

6 files changed

Lines changed: 60 additions & 22 deletions

File tree

docs/src/data/changelog/v1.0.3/stack-dependencies.mdx renamed to docs/src/data/changelog/v1.0.4/stack-dependencies.mdx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
version: "v1.0.3"
2+
version: "v1.0.4"
33
category: "experiments-updated"
44
---
55

@@ -36,21 +36,19 @@ unit "app" {
3636
}
3737
```
3838

39-
Nested stacks with `no_dot_terragrunt_stack = true` are also handled correctly, resolving to the stack's own directory rather than under `.terragrunt-stack/`.
40-
4139
**Discovery commands surface stack dependencies**
4240

4341
The `terragrunt find` and `terragrunt list` discovery commands now reflect stack dependencies generated by the `autoinclude` block. The DAG output correctly orders units by their autoinclude dependencies and shows dependency relationships in JSON, tree, and long formats.
4442

4543
```bash
4644
# JSON output includes dependency relationships from autoinclude
47-
terragrunt find --json --dag --dependencies --experiment stack-dependencies
45+
$ terragrunt find --json --dag --dependencies --experiment stack-dependencies
4846

4947
# Long list format shows a Dependencies column
50-
terragrunt list --long --dependencies --dag --experiment stack-dependencies
48+
$ terragrunt list --long --dependencies --dag --experiment stack-dependencies
5149

5250
# Tree format visualizes the dependency hierarchy
53-
terragrunt list --tree --dag --experiment stack-dependencies
51+
$ terragrunt list --tree --dag --experiment stack-dependencies
5452
```
5553

5654
Multi-level dependency trees (for example, `A → B,C` where `B → D,E`) are ordered correctly in DAG mode: leaf units appear first, parents appear after all their dependencies.

docs/src/data/experiments/stack-dependencies.mdx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,22 @@ Provide your feedback on the [Stack Dependencies RFC](https://github.com/gruntwo
143143
- [ ] Performance validation at scale
144144

145145
</Since>
146+
147+
<Since version="1.0.4">
148+
149+
- [x] Create `internal/hclparse` package with two-phase stack file parser and autoinclude data structures
150+
- [x] Implement `terragrunt.autoinclude.hcl` file generator with relative path resolution
151+
- [x] Implement AST-walking partial evaluator for mixed `local.*` + `dependency.*` expressions
152+
- [x] Integrate autoinclude parsing and generation into `pkg/config/stack.go` stack generation pipeline
153+
- [x] Auto-merge `terragrunt.autoinclude.hcl` into unit config during `ParseConfig()` with deep merge
154+
- [x] Add `tryGetStackOutput()` for dependency blocks targeting stack directories
155+
- [x] Add `ExtractAutoIncludeDependencyPaths()` so DAG sees dependencies from autoinclude files
156+
- [x] Add integration tests and test fixtures for end-to-end validation
157+
- [x] E2E validation with `stack run apply` and `stack run destroy`
158+
- [x] Resolve `stack.<name>.<nested_stack>.path` at arbitrary nesting depth
159+
- [x] Surface autoinclude dependencies in `terragrunt find` and `terragrunt list` (JSON, tree, and long formats)
160+
- [ ] Community feedback on `autoinclude` syntax and merge behavior
161+
- [ ] Integration with CAS for generated files
162+
- [ ] Performance validation at scale
163+
164+
</Since>

internal/hclparse/autoinclude.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,15 @@ func BuildAutoIncludeEvalContext(unitRefs, stackRefs []ComponentRef) *hcl.EvalCo
180180
// AutoIncludeDependencyPaths reads the terragrunt.autoinclude.hcl file in
181181
// unitDir and returns resolved dependency config_path values.
182182
// Returns (nil, nil) if the file does not exist or has no dependencies.
183+
// Panics when fs is nil (programmer error). Returns EmptyArgError when unitDir
184+
// is empty so callers can distinguish bad input from a missing file.
183185
func AutoIncludeDependencyPaths(fs vfs.FS, unitDir string) ([]string, error) {
184186
if fs == nil {
185187
panic(fmt.Sprintf("hclparse.AutoIncludeDependencyPaths: fs is nil (unitDir=%q)", unitDir))
186188
}
187189

188190
if unitDir == "" {
189-
panic("hclparse.AutoIncludeDependencyPaths: unitDir is empty")
191+
return nil, EmptyArgError{Func: "AutoIncludeDependencyPaths", Arg: "unitDir"}
190192
}
191193

192194
unitDir = util.ResolvePath(unitDir)
@@ -197,7 +199,7 @@ func AutoIncludeDependencyPaths(fs vfs.FS, unitDir string) ([]string, error) {
197199
return nil, err
198200
}
199201

200-
var paths []string
202+
paths := make([]string, 0, len(body.Blocks))
201203

202204
for _, block := range body.Blocks {
203205
if depPath, ok := extractDependencyConfigPath(block, unitDir); ok {

internal/hclparse/errors.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,13 @@ type LocalsMaxIterError struct {
133133
func (e LocalsMaxIterError) Error() string {
134134
return fmt.Sprintf("locals evaluation exceeded %d iterations with %d unresolved locals", e.MaxIterations, e.Remaining)
135135
}
136+
137+
// EmptyArgError indicates that a required string argument was empty.
138+
type EmptyArgError struct {
139+
Func string
140+
Arg string
141+
}
142+
143+
func (e EmptyArgError) Error() string {
144+
return fmt.Sprintf("hclparse.%s: %s is empty", e.Func, e.Arg)
145+
}

internal/hclparse/fuzz_test.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package hclparse_test
22

33
import (
4+
"errors"
45
"testing"
56

67
"github.com/gruntwork-io/terragrunt/internal/hclparse"
@@ -372,10 +373,11 @@ func FuzzUnitPathsFromStackDir_ArgPanics(f *testing.F) {
372373
})
373374
}
374375

375-
// FuzzAutoIncludeDependencyPaths_ArgPanics fuzzes unitDir plus arbitrary file
376+
// FuzzAutoIncludeDependencyPaths_ArgErrors fuzzes unitDir plus arbitrary file
376377
// contents written to the in-memory FS, exercising both the argument-validation
377-
// panic and the HCL parsing path.
378-
func FuzzAutoIncludeDependencyPaths_ArgPanics(f *testing.F) {
378+
// error and the HCL parsing path. Empty unitDir must return EmptyArgError;
379+
// non-empty unitDir must not panic regardless of file content.
380+
func FuzzAutoIncludeDependencyPaths_ArgErrors(f *testing.F) {
379381
f.Add("/unit", `dependency "vpc" { config_path = "../vpc" }`)
380382
f.Add("", `dependency "x" {}`)
381383
f.Add("/unit", ``)
@@ -392,17 +394,19 @@ func FuzzAutoIncludeDependencyPaths_ArgPanics(f *testing.F) {
392394
}
393395

394396
defer func() {
395-
r := recover()
396-
397-
switch {
398-
case unitDir == "" && r == nil:
399-
t.Errorf("expected panic for empty unitDir, got none")
400-
case unitDir != "" && r != nil:
397+
if r := recover(); r != nil {
401398
t.Errorf("unexpected panic for unitDir=%q: %v", unitDir, r)
402399
}
403400
}()
404401

405-
_, _ = hclparse.AutoIncludeDependencyPaths(fs, unitDir)
402+
_, err := hclparse.AutoIncludeDependencyPaths(fs, unitDir)
403+
404+
if unitDir == "" {
405+
var emptyErr hclparse.EmptyArgError
406+
if !errors.As(err, &emptyErr) {
407+
t.Errorf("expected EmptyArgError for empty unitDir, got %v", err)
408+
}
409+
}
406410
})
407411
}
408412

internal/hclparse/panic_test.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,16 @@ func TestAutoIncludeDependencyPaths_NilFS_Panics(t *testing.T) {
103103
})
104104
}
105105

106-
func TestAutoIncludeDependencyPaths_EmptyUnitDir_Panics(t *testing.T) {
106+
func TestAutoIncludeDependencyPaths_EmptyUnitDir_ReturnsError(t *testing.T) {
107107
t.Parallel()
108-
assertPanicsContaining(t, "hclparse.AutoIncludeDependencyPaths: unitDir is empty", func() {
109-
_, _ = hclparse.AutoIncludeDependencyPaths(vfs.NewMemMapFS(), "")
110-
})
108+
109+
paths, err := hclparse.AutoIncludeDependencyPaths(vfs.NewMemMapFS(), "")
110+
require.Nil(t, paths)
111+
112+
var emptyErr hclparse.EmptyArgError
113+
require.ErrorAs(t, err, &emptyErr)
114+
assert.Equal(t, "AutoIncludeDependencyPaths", emptyErr.Func)
115+
assert.Equal(t, "unitDir", emptyErr.Arg)
111116
}
112117

113118
func TestGenerateAutoIncludeFile_NilFS_Panics(t *testing.T) {

0 commit comments

Comments
 (0)