fix(terraform): select module callers, not every stack [NOJIRA] - #345
Merged
Merged
Conversation
A change under modules/ was owned by no stack, so discovery fell back to selecting all of them. In terraform-core that turns a modules/route53 change into 11 plans and, on merge, 11 applies - the 7 unrelated stacks apply whatever their plan happened to contain, which for a drifted stack means applying that drift under a review that never mentioned it. Terraform already declares these edges. Discovery now reads the local source = "..." paths, takes the transitive closure, and selects the stacks that actually call the changed module. Paths that are neither a stack nor Terraform code still fan out. terraform-core: route53 11 -> 4, vpc-endpoints 11 -> 3, iam-role 11 -> 1, and the two modules nothing calls 11 -> 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
tobias0106
marked this pull request as ready for review
September 15, 2026 11:37
tobias0106
requested review from
prasad-manu
and removed request for
a team
September 15, 2026 11:37
joscdk
approved these changes
Sep 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft — the fan-out issue from #341, fixed for the case that actually hurts.
The problem
A changed file under
modules/is owned by no stack, so discovery falls back to selecting every stack. Onterraform-corethat means a one-line change tomodules/route53runs 11 plans and, on merge, 11 applies:accounts/{internal,monta,production,staging}/route53accounts/{internal,staging}/iamaccounts/{internal,staging}/s3accounts/{internal,production,staging}/vpc-endpointsThe seven extras are no-ops if they are clean. If one has drifted, an unrelated module PR is what applies that drift, under a review that never mentioned it.
Why not the nearest-ancestor fix
#341 proposed scoping fan-out to the changed file's nearest ancestor directory. That is right for
terraform-db-access, where_shared-users-*.tfsits above the stacks it affects. It is wrong here:modules/route53has no stacks underneath it, so it would select zero and silently skip the four stacks that do need replanning. Worse than fanning out.What this does instead
Terraform already declares the edges — every
source = "../../modules/route53"is a dependency. Discovery now reads them:source = "..."paths relative to the file that declares them.stack -> modules/a -> modules/bmeans a change tomodules/bselects that stack.modules/X/**selects every stack whose closure contains it..github/**— still fans out to everything.Nothing to declare, nothing to maintain, and it stays correct when a module is added or wired into a new stack. Registry and git sources are ignored; only paths inside the repository count.
Verified against all three repos
Run against the real trees,
terraform-core:modules/route53/**modules/vpc-endpoints/**modules/iam-role/**modules/s3-bucket/**,modules/vpc/**accounts/internal/route53/zones/monta-me/records.tf.github/**, root filesIt reproduces the hand-maintained
paths-filtermapping exactly, without the list.monta-app/terraform:modules/cloudflare/tunnel/**now selectsglobal/cloudflarealone instead of Grafana and both ClickHouse stacks — the second example named in #341.The zero rows are a real finding, not a rounding error:
modules/s3-bucketandmodules/vpcare called by nothing.accounts/{staging,internal}/s3contain only abackend.tf, aversions.tfand a comment naming the module they were meant to use.Still to do for
terraform-db-accessThis does not cover
_shared-users-*.tfin an environment root — those are not modules, and no stack references them. That repo still needs the nearest-ancestor rule, as its own change, alongside the tailscale and parallelism inputs it is waiting on.Notes for review
The resolver is ~80 lines of Python written to
$RUNNER_TEMPby a preceding step. It has to be inline: a reusable workflow checks out the caller's repository, so a script committed here would not be on disk when it runs. There is no test harness for these workflows, so the verification above was run against real checkouts ofterraform-coreandterraform, using the script extracted back out of the YAML.🤖 Generated with Claude Code