Skip to content

Conversation

pseudomorph
Copy link
Contributor

@pseudomorph pseudomorph commented Sep 10, 2025

Description

There are cases where parallelization might affect an init-ed dir during read, such as when using Terragrunt with Atlantis. In these cases, when a user passes the option to fetch dependency output from (remote) state, it should not use output from init-ed dirs.

TODOs

Read the Gruntwork contribution guidelines.

  • I authored this code entirely myself
  • I am submitting code based on open source software (e.g. MIT, MPL-2.0, Apache)]
  • I am adding or upgrading a dependency or adapted code and confirm it has a compatible open source license
  • Update the docs.
  • Run the relevant tests successfully, including pre-commit checks.
  • Include release notes. If this PR is backward incompatible, include a migration guide.

Release Notes (draft)

Updated: --fetch-dependency-output-from-state now will always pull directly from state, even when dependency dirs are init-ed.

Migration Guide

Summary by CodeRabbit

  • Bug Fixes
    • When the “fetch outputs from remote state” option is enabled, outputs are now always read from remote state even if the dependency was initialized locally. Previously, it could read from the local init folder, causing inconsistent results. This change improves predictability, aligns behavior with user configuration, and reduces unexpected output mismatches in automated workflows. No breaking changes.

Copy link

vercel bot commented Sep 10, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
terragrunt-docs Ready Ready Preview Comment Sep 10, 2025 7:56pm

Copy link
Contributor

coderabbitai bot commented Sep 10, 2025

📝 Walkthrough

Walkthrough

The function getTerragruntOutputJSON now conditions the “init-folder” output path on both isInit and FetchDependencyOutputFromState. If FetchDependencyOutputFromState is true, it bypasses reading from the init folder and proceeds to fetch outputs from remote state. No exported/public signatures changed.

Changes

Cohort / File(s) Summary
Dependency output selection logic
config/dependency.go
Updated conditional in getTerragruntOutputJSON: return getTerragruntOutputJSONFromInitFolder only when isInit is true and FetchDependencyOutputFromState is false; otherwise proceed to fetch from remote state. Added clarifying comment.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Caller
  participant Config as config/dependency.go
  participant InitFolder as Init Folder Reader
  participant RemoteState as Remote State

  Caller->>Config: getTerragruntOutputJSON(isInit, FetchDependencyOutputFromState)
  alt isInit == true AND FetchDependencyOutputFromState == false
    note over Config,InitFolder: Use init-folder outputs
    Config->>InitFolder: getTerragruntOutputJSONFromInitFolder()
    InitFolder-->>Config: Outputs JSON
    Config-->>Caller: Outputs JSON
  else
    note over Config,RemoteState: Fetch from remote state
    Config->>RemoteState: Read outputs from state
    RemoteState-->>Config: Outputs JSON
    Config-->>Caller: Outputs JSON
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description follows the general structure but omits the required “Fixes #000” issue reference and leaves placeholder sections unfilled, so it does not fully conform to the repository’s template. Add the “Fixes #<issue_number>” line under the Description heading, complete or remove placeholder sections such as the migration guide, and ensure all checklist items like license confirmation and documentation updates are addressed.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title concisely and accurately summarizes the primary change by stating that when the remote state option is selected, dependency outputs will always be fetched from remote state, making it clear and specific to reviewers.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
config/dependency.go (2)

797-816: Skip unnecessary isInit check when always-fetch-from-state is set

Minor perf/IO win: we still call terragruntAlreadyInit even though the result is ignored when FetchDependencyOutputFromState is true. Short-circuit earlier and only compute isInit/workingDir when the flag is false.

Apply this refactor:

@@
-	// In optimization mode, see if there is already an init-ed folder that terragrunt can use, and if so, run
-	// `terraform output` in the working directory.
-	isInit, workingDir, err := terragruntAlreadyInit(l, targetTGOptions, targetConfig, ctx)
-	if err != nil {
-		return nil, err
-	}
+	// NOTE: When FetchDependencyOutputFromState is set, skip checking for an init-ed folder entirely.
@@
-	// Do not fetch from init-ed folder if user has explicitly asked to fetch from state.
-	if isInit && !ctx.TerragruntOptions.FetchDependencyOutputFromState {
-		return getTerragruntOutputJSONFromInitFolder(ctx, l, workingDir, remoteStateTGConfig.GetIAMRoleOptions())
-	}
+	if ctx.TerragruntOptions.FetchDependencyOutputFromState {
+		return getTerragruntOutputJSONFromRemoteState(
+			ctx,
+			l,
+			targetConfig,
+			remoteStateTGConfig.RemoteState,
+			remoteStateTGConfig.GetIAMRoleOptions(),
+		)
+	}
+
+	// In optimization mode, see if there is already an init-ed folder; if so, read outputs directly.
+	isInit, workingDir, err := terragruntAlreadyInit(l, targetTGOptions, targetConfig, ctx)
+	if err != nil {
+		return nil, err
+	}
+	if isInit {
+		return getTerragruntOutputJSONFromInitFolder(ctx, l, workingDir, remoteStateTGConfig.GetIAMRoleOptions())
+	}

1039-1053: Typos + avoid extra allocation in S3 path

Rename steateBody → stateBody and unmarshal directly from bytes to reduce a string alloc/copy.

-	steateBody, err := io.ReadAll(result.Body)
+	stateBody, err := io.ReadAll(result.Body)
@@
-	jsonState := string(steateBody)
-	jsonMap := make(map[string]any)
-
-	err = json.Unmarshal([]byte(jsonState), &jsonMap)
+	var jsonMap map[string]any
+	err = json.Unmarshal(stateBody, &jsonMap)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 273e740 and 7c45281.

📒 Files selected for processing (1)
  • config/dependency.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

Review the Go code for quality and correctness. Make sure that the Go code follows best practices, is performant, and is easy to understand and maintain.

Files:

  • config/dependency.go
🧬 Code graph analysis (1)
config/dependency.go (1)
options/options.go (1)
  • TerragruntOptions (100-332)
🔇 Additional comments (1)
config/dependency.go (1)

812-815: Flag now correctly bypasses init-ed folder — LGTM

This enforces the intended behavior: when FetchDependencyOutputFromState is true, we no longer read from an init-ed working dir. Matches the PR objective without affecting the false case.

@BradBarnich
Copy link

This can also happen when there is a terragrunt unit that is using loose tf files instead of a terraform { source = ... } block. In this case the init happens in the unit directory (not in .terragrunt-cache, see screenshot).

Screenshot 2025-09-11 143019

When this unit is used as dependency, the code path in question is triggered, and then even with the 'fetch output from state` option set, it instead calls terraform output

brad@DESKTOP-608UVN8 ~/c/S/s/a/p/d/c/u/p/ssh_bastion (master)> env | grep TG_DEPENDENCY_FETCH_OUTPUT_FROM_STATE
TG_DEPENDENCY_FETCH_OUTPUT_FROM_STATE=true
brad@DESKTOP-608UVN8 ~/c/S/s/a/p/d/c/u/p/ssh_bastion (master)> terragrunt plan -log-level debug
...
14:35:30.488 DEBUG  [../vpc] Detected module ../vpc/terragrunt.hcl is already init-ed. Retrieving outputs directly from working directory.
14:35:30.488 DEBUG  [../vpc] Running command: terraform output -json

I think the proposed fix is fine, I had dabbed with reordering the operations to reach for remote state before running output in an init'd dependency d69eed7

I'm not familiar enough with terragrunt to know which is a more appropriate fix. Because the init check doesn't hit on the more common path terraform { source = ... }, it feels like it is more or less dead code

@yhakbar
Copy link
Collaborator

yhakbar commented Sep 12, 2025

@pseudomorph thanks for opening this PR. It seems entirely reasonable. Do you mind adding some testing to confirm that this resolves the issue we're looking to address here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants