Skip to content

Commit cd0de16

Browse files
committed
fix: fixed parsing of configs
1 parent ceef029 commit cd0de16

3 files changed

Lines changed: 92 additions & 4 deletions

File tree

internal/discovery/helpers.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import (
1212
intHclparse "github.com/gruntwork-io/terragrunt/internal/hclparse"
1313
"github.com/gruntwork-io/terragrunt/internal/util"
1414
"github.com/gruntwork-io/terragrunt/pkg/config"
15+
"github.com/hashicorp/hcl/v2"
16+
"github.com/hashicorp/hcl/v2/hclsyntax"
17+
"github.com/zclconf/go-cty/cty"
1518
)
1619

1720
const (
@@ -221,6 +224,8 @@ func sanitizeReadFiles(files []string) []string {
221224
}
222225

223226
// extractDependencyPaths extracts all dependency paths from a Terragrunt configuration.
227+
// It also checks for terragrunt.autoinclude.hcl in the same directory and extracts
228+
// dependency config_path values from it, so the DAG correctly orders units.
224229
func extractDependencyPaths(cfg *config.TerragruntConfig, c component.Component) ([]string, error) {
225230
if cfg == nil {
226231
return nil, nil
@@ -233,6 +238,12 @@ func extractDependencyPaths(cfg *config.TerragruntConfig, c component.Component)
233238

234239
deduped := make(map[string]struct{}, maxDedupLen)
235240

241+
// Check for autoinclude file and extract its dependency paths for the DAG.
242+
autoIncludeDeps := extractAutoIncludeDependencyPaths(c.Path())
243+
for _, dep := range autoIncludeDeps {
244+
deduped[dep] = struct{}{}
245+
}
246+
236247
errs := make([]error, 0, maxDedupLen)
237248

238249
for _, dependency := range cfg.TerragruntDependencies {
@@ -326,3 +337,59 @@ func ExtractUnitPathsFromStackFile(data []byte, stackDir string) []string {
326337

327338
return paths
328339
}
340+
341+
// extractAutoIncludeDependencyPaths checks for a terragrunt.autoinclude.hcl file
342+
// in the given unit directory and extracts dependency config_path values.
343+
// This ensures the DAG sees dependencies defined in autoinclude files during
344+
// graph construction, even though they're not in the main terragrunt.hcl.
345+
func extractAutoIncludeDependencyPaths(unitDir string) []string {
346+
autoIncludePath := filepath.Join(unitDir, config.DefaultAutoIncludeFile)
347+
348+
if !util.FileExists(autoIncludePath) {
349+
return nil
350+
}
351+
352+
data, err := os.ReadFile(autoIncludePath)
353+
if err != nil {
354+
return nil
355+
}
356+
357+
// Minimal HCL parse — just extract dependency blocks and their config_path.
358+
file, diags := hclsyntax.ParseConfig(data, autoIncludePath, hcl.Pos{Line: 1, Column: 1})
359+
if diags.HasErrors() {
360+
return nil
361+
}
362+
363+
body, ok := file.Body.(*hclsyntax.Body)
364+
if !ok {
365+
return nil
366+
}
367+
368+
var paths []string
369+
370+
for _, block := range body.Blocks {
371+
if block.Type != "dependency" || len(block.Labels) == 0 {
372+
continue
373+
}
374+
375+
configPathAttr, exists := block.Body.Attributes["config_path"]
376+
if !exists {
377+
continue
378+
}
379+
380+
// Evaluate config_path — it's already a resolved string literal in the generated file.
381+
val, valDiags := configPathAttr.Expr.Value(nil)
382+
if valDiags.HasErrors() || val.Type() != cty.String {
383+
continue
384+
}
385+
386+
depPath := val.AsString()
387+
if !filepath.IsAbs(depPath) {
388+
depPath = filepath.Clean(filepath.Join(unitDir, depPath))
389+
}
390+
391+
paths = append(paths, util.ResolvePath(depPath))
392+
}
393+
394+
return paths
395+
}

pkg/config/config.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1459,7 +1459,18 @@ func mergeAutoIncludeIfPresent(
14591459

14601460
l.Debugf("Found %s, auto-merging into unit config", autoIncludePath)
14611461

1462-
autoIncludeConfig, err := ParseConfigFile(ctx, pctx, l, autoIncludePath, nil)
1462+
// Clone the parsing context and reset DecodedDependencies so the
1463+
// autoinclude file gets its own dependency resolution pass.
1464+
//
1465+
// PartialParseDecodeList is intentionally inherited from the parent:
1466+
// - During DAG construction (partial mode): extracts dependency config_path
1467+
// for graph ordering WITHOUT resolving outputs (which aren't available yet)
1468+
// - During actual unit run (full mode): resolves dependency outputs normally
1469+
// because the DAG already ensured dependencies ran first
1470+
autoIncludePctx := pctx.Clone()
1471+
autoIncludePctx.DecodedDependencies = nil
1472+
1473+
autoIncludeConfig, err := ParseConfigFile(ctx, autoIncludePctx, l, autoIncludePath, nil)
14631474
if err != nil {
14641475
return nil, errors.Errorf("failed to parse %s: %w", autoIncludePath, err)
14651476
}

pkg/config/stack.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818

1919
"github.com/hashicorp/go-getter/v2"
2020

21+
"github.com/hashicorp/hcl/v2"
2122
"github.com/hashicorp/hcl/v2/hclsyntax"
2223

2324
"github.com/gruntwork-io/terragrunt/internal/util"
@@ -39,9 +40,16 @@ const (
3940

4041
// StackConfigFile represents the structure of terragrunt.stack.hcl stack file.
4142
type StackConfigFile struct {
42-
Locals *terragruntLocal `hcl:"locals,block"`
43-
Stacks []*Stack `hcl:"stack,block"`
44-
Units []*Unit `hcl:"unit,block"`
43+
Locals *terragruntLocal `hcl:"locals,block"`
44+
Includes []*StackIncludeFile `hcl:"include,block"`
45+
Stacks []*Stack `hcl:"stack,block"`
46+
Units []*Unit `hcl:"unit,block"`
47+
}
48+
49+
// StackIncludeFile represents an include block in a stack file.
50+
type StackIncludeFile struct {
51+
Name string `hcl:",label"`
52+
Path string `hcl:"path,attr"`
4553
}
4654

4755
// StackConfig represents the structure of terragrunt.stack.hcl stack file.
@@ -53,6 +61,7 @@ type StackConfig struct {
5361

5462
// Unit represents unit from a stack file.
5563
type Unit struct {
64+
Remain hcl.Body `hcl:",remain"`
5665
NoStack *bool `hcl:"no_dot_terragrunt_stack,attr"`
5766
NoValidation *bool `hcl:"no_validation,attr"`
5867
Values *cty.Value `hcl:"values,attr"`
@@ -63,6 +72,7 @@ type Unit struct {
6372

6473
// Stack represents the stack block in the configuration.
6574
type Stack struct {
75+
Remain hcl.Body `hcl:",remain"`
6676
NoStack *bool `hcl:"no_dot_terragrunt_stack,attr"`
6777
NoValidation *bool `hcl:"no_validation,attr"`
6878
Values *cty.Value `hcl:"values,attr"`

0 commit comments

Comments
 (0)