Skip to content

Commit 7afdf64

Browse files
yhakbardenis256
andauthored
fix: Fixing --queue-strict-include integration with --queue-include-units-reading (#5222)
* fix: Fixing `--queue-strict-include` integration with `--queue-include-units-reading` * chore: pattern inclusion fix * chore: updated normalizePaths * chore: lint fixes * chore: docs update --------- Co-authored-by: Denis O <denis.o@linux.com>
1 parent eaca86e commit 7afdf64

7 files changed

Lines changed: 97 additions & 15 deletions

File tree

internal/discovery/discovery.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -831,13 +831,12 @@ func (d *Discovery) processFile(
831831
// Everything after this point is only relevant when the filter flag is disabled.
832832
// It should be removed once the filter flag is generally available.
833833

834-
// Enforce include patterns only when strictInclude or excludeByDefault are set AND patterns exist.
835-
// When excludeByDefault is true without include patterns (e.g., --units-that-include), or when readFiles
836-
// filtering is enabled, we keep configs so downstream filtering can mark exclusions based on read files.
837-
// Consolidated include enforcement: only apply when we have include patterns and either:
838-
// - strictInclude is enabled, or
839-
// - excludeByDefault is enabled and readFiles filtering is NOT enabled
840-
enforceInclude := (d.strictInclude || (d.excludeByDefault && !d.readFiles)) && len(d.compiledIncludePatterns) > 0
834+
// Enforce include patterns early only when patterns exist AND readFiles is NOT enabled.
835+
// When readFiles is enabled (--queue-include-units-reading/--modules-that-include),
836+
// we must discover all configs first so that flagUnitsThatRead can filter based on
837+
// which files each unit reads. Without this bypass, units outside the default include
838+
// patterns (.terragrunt-stack/**) would be dropped before flagUnitsThatRead runs.
839+
enforceInclude := !d.readFiles && (d.strictInclude || d.excludeByDefault) && len(d.compiledIncludePatterns) > 0
841840
if enforceInclude && !d.matchesIncludePath(canonicalDir) {
842841
return nil
843842
}
@@ -1264,8 +1263,10 @@ func (d *Discovery) Discover(
12641263

12651264
// Apply strictInclude filtering: when strictInclude is true, remove dependencies
12661265
// that don't match the include patterns (they shouldn't be included just because
1267-
// they are dependencies of included units)
1268-
if d.strictInclude && len(d.compiledIncludePatterns) > 0 {
1266+
// they are dependencies of included units).
1267+
// Skip this filtering when readFiles is enabled, as we need all components
1268+
// to remain available for flagUnitsThatRead to process in applyQueueFilters.
1269+
if d.strictInclude && len(d.compiledIncludePatterns) > 0 && !d.readFiles {
12691270
components = d.filterByStrictInclude(l, components)
12701271
}
12711272

internal/discovery/queue_filters.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@ func unexcludeUnitsReading(components component.Components, normalizedReading []
194194

195195
readPath = util.CleanPath(readPath)
196196

197+
// Also resolve symlinks for consistent comparison
198+
readPath = resolvePath(readPath)
199+
197200
if _, ok := readingSet[readPath]; ok {
198201
unit.SetExcluded(false)
199202

@@ -259,9 +262,10 @@ func (d *Discovery) flagUnitsThatRead(opts *options.TerragruntOptions, component
259262
return components
260263
}
261264

262-
// Normalize paths
263-
normalizedReading := normalizePaths(opts.WorkingDir, opts.UnitsReading)
264-
normalizedIncluding := normalizePaths(opts.WorkingDir, opts.ModulesThatInclude)
265+
// Normalize paths using discoveryContext.WorkingDir which is always set correctly
266+
workingDir := d.discoveryContext.WorkingDir
267+
normalizedReading := normalizePaths(workingDir, opts.UnitsReading)
268+
normalizedIncluding := normalizePaths(workingDir, opts.ModulesThatInclude)
265269

266270
// Capture pre-included units before resetting
267271
preIncluded := capturePreIncluded(components)
@@ -270,10 +274,10 @@ func (d *Discovery) flagUnitsThatRead(opts *options.TerragruntOptions, component
270274
resetAllUnitsExcluded(components)
271275

272276
// Un-exclude units that read the requested files
273-
unexcludeUnitsReading(components, normalizedReading, opts.WorkingDir)
277+
unexcludeUnitsReading(components, normalizedReading, workingDir)
274278

275279
// Un-exclude units that include the requested files
276-
unexcludeModulesThatInclude(components, normalizedIncluding, opts.WorkingDir)
280+
unexcludeModulesThatInclude(components, normalizedIncluding, workingDir)
277281

278282
// Restore prior inclusions
279283
restorePreIncluded(components, preIncluded)

test/fixtures/queue-strict-include-units-reading/live/foo/main.tf

Whitespace-only changes.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
terraform {
2+
source = "."
3+
}
4+
5+
locals {
6+
source_config = read_terragrunt_config("../../sources/source.hcl")
7+
}
8+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
locals {
2+
common_value = "from-source"
3+
}
4+

test/integration_queue_strict_include_test.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import (
1111
)
1212

1313
const (
14-
testFixtureQueueStrictInclude = "fixtures/queue-strict-include"
14+
testFixtureQueueStrictInclude = "fixtures/queue-strict-include"
15+
testFixtureQueueStrictIncludeUnitsReading = "fixtures/queue-strict-include-units-reading"
1516
)
1617

1718
// TestQueueStrictIncludeWithDependencyNotInQueue tests that when using --queue-strict-include
@@ -137,3 +138,29 @@ func TestQueueStrictIncludeWithDependencyNotInQueue(t *testing.T) {
137138
"Should show 'found 1 readyEntries tasks' - dependency should run even though transitive-dependency is not in queue")
138139
})
139140
}
141+
142+
// TestQueueStrictIncludeWithUnitsReadingWithoutIncludeDir reproduces the bug where
143+
// --queue-strict-include with --queue-include-units-reading (but no --queue-include-dir)
144+
// fails to discover units that read the specified file.
145+
func TestQueueStrictIncludeWithUnitsReadingWithoutIncludeDir(t *testing.T) {
146+
t.Parallel()
147+
148+
tmpEnvPath := helpers.CopyEnvironment(t, testFixtureQueueStrictIncludeUnitsReading)
149+
helpers.CleanupTerraformFolder(t, tmpEnvPath)
150+
testPath := util.JoinPath(tmpEnvPath, testFixtureQueueStrictIncludeUnitsReading)
151+
152+
// This reproduces the bug: --queue-strict-include + --queue-include-units-reading
153+
// without --queue-include-dir should still include units that read the file
154+
cmd := fmt.Sprintf("terragrunt run --all --non-interactive --working-dir %s --queue-strict-include --queue-include-units-reading=sources/source.hcl -- plan", testPath)
155+
stdout, stderr, err := helpers.RunTerragruntCommandWithOutput(t, cmd)
156+
157+
// The command should succeed and discover the unit
158+
require.NoError(t, err, "Command should succeed\nstdout: %s\nstderr: %s", stdout, stderr)
159+
160+
output := stdout + stderr
161+
162+
// Verify that the unit reading sources/source.hcl is discovered and included
163+
// This should pass after the fix, but currently fails due to the bug
164+
assert.Contains(t, output, "live/foo", "Unit live/foo that reads sources/source.hcl should be included")
165+
assert.NotContains(t, output, "No units discovered", "Should discover units reading sources/source.hcl")
166+
}

test/integration_units_reading_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,3 +345,41 @@ func TestUnitsReadingWithFilter(t *testing.T) {
345345
})
346346
}
347347
}
348+
349+
// TestQueueStrictIncludeWithUnitsReading tests that --queue-strict-include works correctly
350+
// with --queue-include-units-reading when no --queue-include-dir is specified.
351+
// This reproduces the bug where units reading the specified file were not included.
352+
func TestQueueStrictIncludeWithUnitsReading(t *testing.T) {
353+
t.Parallel()
354+
355+
cleanupTerraformFolder(t, testFixtureUnitsReading)
356+
357+
tmpEnvPath := helpers.CopyEnvironment(t, testFixtureUnitsReading)
358+
rootPath := util.JoinPath(tmpEnvPath, testFixtureUnitsReading)
359+
rootPath, err := filepath.EvalSymlinks(rootPath)
360+
require.NoError(t, err)
361+
362+
// Test the bug scenario: --queue-strict-include + --queue-include-units-reading
363+
// without --queue-include-dir. Units reading shared.hcl should be included.
364+
cmd := "terragrunt run --all plan --non-interactive --log-level trace --working-dir " + rootPath +
365+
" --queue-strict-include --queue-include-units-reading shared.hcl"
366+
367+
_, stderr, err := helpers.RunTerragruntCommandWithOutput(t, cmd)
368+
require.NoError(t, err, "Command should succeed and include units reading shared.hcl")
369+
370+
includedLogEntryRegex := regexp.MustCompile(`=> Unit ([^ ]+) \(excluded: false`)
371+
includedUnits := []string{}
372+
for _, line := range strings.Split(stderr, "\n") {
373+
if includedLogEntryRegex.MatchString(line) {
374+
includedUnits = append(includedUnits, includedLogEntryRegex.FindStringSubmatch(line)[1])
375+
}
376+
}
377+
378+
// Units that read shared.hcl should be included
379+
expectedUnits := []string{
380+
"reading-hcl",
381+
"reading-hcl-and-tfvars",
382+
}
383+
assert.ElementsMatch(t, expectedUnits, includedUnits,
384+
"Units reading shared.hcl should be included when using --queue-strict-include with --queue-include-units-reading")
385+
}

0 commit comments

Comments
 (0)