Skip to content

fix: YAML functions fail with templated args when custom delimiters include quotes#2098

Merged
aknysh merged 4 commits intomainfrom
aknysh/fix-terraform-state-7
Feb 22, 2026
Merged

fix: YAML functions fail with templated args when custom delimiters include quotes#2098
aknysh merged 4 commits intomainfrom
aknysh/fix-terraform-state-7

Conversation

@aknysh
Copy link
Copy Markdown
Member

@aknysh aknysh commented Feb 21, 2026

what

  • Fixed a bug where all YAML functions (!terraform.state, !terraform.output, !store, !store.get, !env, !exec, !include, !include.raw, !random, etc.) fail with yaml: line NNN: did not find expected key when custom template delimiters contain single-quote characters (e.g., ["'{{", "}}'"])
  • Added ConvertToYAMLPreservingDelimiters function that detects delimiter/YAML quoting conflicts and forces double-quoted YAML style for affected scalar values, preserving delimiter characters literally in the serialized output
  • Updated all 5 template processing call sites (internal/exec/utils.go, internal/exec/describe_stacks.go (3 places), internal/exec/terraform_generate_varfiles.go, internal/exec/terraform_generate_backends.go) to use the new function

why

  • When custom delimiters include single quotes, the yaml.v3 encoder's single-quoted style escapes internal ' as ''. For example, !terraform.state vpc '{{ .stack }}' vpc_id becomes '!terraform.state vpc ''{{ .stack }}'' vpc_id'
  • The Go template engine with delimiters '{{ and }}' then finds '{{ within the ''{{ sequence and performs template replacement, producing '!terraform.state vpc 'nonprod' vpc_id' — which is invalid YAML because unescaped single quotes break the single-quoted string
  • The fix uses double-quoted YAML style instead, which does not escape single quotes: "!terraform.state vpc '{{ .stack }}' vpc_id". After template replacement: "!terraform.state vpc nonprod vpc_id" — valid YAML
  • The fix is generic at the YAML serialization level (any scalar containing ' gets double-quoted when delimiters contain '), so it automatically protects all current and future YAML functions
  • Static arguments and default delimiters ({{/}}) are unaffected — the function falls back to standard encoding when there is no conflict

Tests

Unit tests (pkg/utils/yaml_utils_delimiter_test.go) — 63 subtests:

  • TestDelimiterConflictsWithYAMLQuoting — 8 subtests for delimiter conflict detection (nil, empty, single-element, default, custom with quotes)
  • TestEnsureDoubleQuotedForDelimiterSafety — 6 subtests for yaml.Node style modification (scalars, mappings, sequences, document nodes, nil safety)
  • TestConvertToYAMLPreservingDelimiters — 10 subtests for end-to-end serialization (round-trip value preservation, template replacement producing valid YAML, standard encoding breaking with custom delimiters, nested maps, lists, custom indent, empty data)
  • TestAllYAMLFunctionsPreservedWithCustomDelimiters — 12 subtests verifying every YAML function prefix (!terraform.state, !terraform.output, !store, !store.get, !env, !exec, !template, !include, !include.raw, !repo-root, !cwd, !random)
  • TestAllYAMLFunctionsTemplateReplacementWithCustomDelimiters — 9 subtests simulating the full pipeline (serialize → template replace → parse) for each function
  • TestStandardEncodingBreaksAllYAMLFunctionsWithCustomDelimiters — 18 subtests proving standard encoding breaks AND delimiter-safe encoding works for each of the 9 affected functions

Integration tests (tests/yaml_functions_custom_delimiters_test.go) — 4 subtests:

  • TestTerraformStateWithCustomDelimiters — Regular templates with custom delimiters, !terraform.state with static args, !terraform.state with templated stack arg (core issue reproduction)
  • TestCustomDelimitersTemplateProcessing — Settings template resolution with custom delimiters

Test fixture (tests/fixtures/scenarios/atmos-terraform-state-custom-delimiters/):

  • atmos.yaml with custom delimiters ["'{{", "}}'"]
  • Stack file with components using both template expressions and !terraform.state with templated args
  • Mock Terraform component with local state file

references

Summary by CodeRabbit

  • Bug Fixes

    • Fixed YAML function failures for templated arguments when custom delimiters contain quotes (resolves terraform.state-related templating issues).
  • Documentation

    • Added comprehensive docs describing the issue, root cause, and delimiter-safe YAML behavior.
  • Tests

    • Added extensive unit/integration tests and fixtures covering custom-delimiter templates, YAML quoting, and terraform state/output scenarios.
  • Chores

    • Updated CLI snapshot data affecting the displayed available versions list.

…nclude quotes (#2052)

When custom template delimiters contain single-quote characters (e.g., ["'{{", "}}'"]),
yaml.v3's single-quote escaping ('') interferes with delimiter patterns, producing
invalid YAML after template replacement. This affects all YAML functions (!terraform.state,
!terraform.output, !store, !exec, !env, !include, etc.) since they all start with '!'
which triggers single-quoted encoding.

Add ConvertToYAMLPreservingDelimiters that detects delimiter/quoting conflicts and forces
double-quoted YAML style for affected scalars, preserving delimiter characters literally.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@aknysh aknysh requested a review from a team as a code owner February 21, 2026 04:45
@aknysh aknysh added the patch A minor, backward compatible change label Feb 21, 2026
@aknysh aknysh requested a review from a team as a code owner February 21, 2026 04:45
@aknysh aknysh added the patch A minor, backward compatible change label Feb 21, 2026
@github-actions github-actions bot added the size/xl Extra large size PR label Feb 21, 2026
@aknysh aknysh self-assigned this Feb 21, 2026
@aknysh aknysh requested a review from osterman February 21, 2026 04:45
@mergify
Copy link
Copy Markdown

mergify bot commented Feb 21, 2026

Warning

This PR exceeds the recommended limit of 1,000 lines.

Large PRs are difficult to review and may be rejected due to their size.

Please verify that this PR does not address multiple issues.
Consider refactoring it into smaller, more focused PRs to facilitate a smoother review process.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Feb 21, 2026

Dependency Review

✅ No vulnerabilities or license issues found.

Scanned Files

None

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 21, 2026

📝 Walkthrough

Walkthrough

Adds delimiter-safe YAML serialization and tests to preserve custom template delimiters (including those with single quotes) during YAML encoding. Replaces prior serialization calls with the new ConvertToYAMLPreservingDelimiters across template-processing call sites and adds fixtures, integration tests, and documentation describing the fix.

Changes

Cohort / File(s) Summary
Core Delimiter-Safe YAML Utility
pkg/yaml/delimiter.go, pkg/yaml/delimiter_test.go
New utilities: ConvertToYAMLPreservingDelimiters, DelimiterConflictsWithYAMLQuoting, EnsureDoubleQuotedForDelimiterSafety, buffer pool, and a comprehensive test suite validating quoting, edge cases, and regression scenarios.
Call Site Updates
internal/exec/describe_stacks.go, internal/exec/terraform_generate_backends.go, internal/exec/terraform_generate_varfiles.go, internal/exec/utils.go
Replaced prior ConvertToYAML calls with atmosYaml.ConvertToYAMLPreservingDelimiters(..., atmosConfig.Templates.Settings.Delimiters) to propagate delimiter-aware serialization; control flow and error handling unchanged.
Test Fixtures & Integration Tests
tests/fixtures/scenarios/atmos-terraform-state-custom-delimiters/..., tests/yaml_functions_custom_delimiters_test.go
Adds a fixture scenario using single-quote-containing delimiters, a Terraform mock component and stack config exercising !terraform.state with templated args, and integration tests asserting correct template resolution and state lookups.
Documentation & Snapshots
docs/fixes/2026-02-20-terraform-state-custom-delimiters.md, tests/snapshots/...
New docs describing issue, root cause, and fix; minor snapshot update (one row change) to reflect expected output differences.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • osterman
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main fix: YAML functions failing with custom delimiters containing quotes in templated arguments.
Linked Issues check ✅ Passed All objectives from issue #2052 are met: the fix handles YAML function failures with custom delimiters including quotes, preserves delimiter characters through new ConvertToYAMLPreservingDelimiters utility, maintains backward compatibility for default delimiters, and includes comprehensive unit and integration tests.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing the custom delimiter issue: documentation, new YAML utility functions, updated call sites in template processing, test fixtures, and snapshot updates. No unrelated modifications present.
Docstring Coverage ✅ Passed Docstring coverage is 96.15% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch aknysh/fix-terraform-state-7

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
Copy Markdown
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: 4

🧹 Nitpick comments (3)
pkg/utils/yaml_utils.go (1)

639-717: Consider moving YAML-encoding helpers to a dedicated package.

delimiterConflictsWithYAMLQuoting, ensureDoubleQuotedForDelimiterSafety, and ConvertToYAMLPreservingDelimiters are tightly cohesive — a pkg/yamlenc/ package would keep pkg/utils/ leaner and satisfy the no-new-functions-in-utils guideline. The existing //nolint:revive // File length justified… at the bottom of the file signals this file is already pushing its size budget.

As per coding guidelines: "Avoid utils package bloat — don't add new functions to pkg/utils/. Create purpose-built packages for new functionality."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/utils/yaml_utils.go` around lines 639 - 717, The three cohesive helpers
ConvertToYAMLPreservingDelimiters, delimiterConflictsWithYAMLQuoting, and
ensureDoubleQuotedForDelimiterSafety should be moved out of pkg/utils into a new
purpose-built package (e.g., pkg/yamlenc) to avoid utils bloat; create
pkg/yamlenc with the same implementations, move any tightly-coupled dependencies
they need (e.g., YAMLOptions, DefaultYAMLIndent, yamlBufferPool or expose
internal helpers from existing packages), update imports where
ConvertToYAMLPreservingDelimiters is called to use the new package, keep
function names or export them as needed, and remove these functions from
pkg/utils (and adjust/remove the file-length nolint comment if it’s no longer
needed). Ensure package-level tests and build pass after updating references.
tests/yaml_functions_custom_delimiters_test.go (2)

53-99: Two subtests run identical ExecuteDescribeComponent calls — merge to eliminate duplicate work.

Both "component-2 with terraform.state static args" (lines 53–73) and "component-2 with terraform.state templated stack arg" (lines 75–99) call ExecuteDescribeComponent with exactly the same parameters. This triggers the full stack-processing pipeline twice for the same result. Merge them into a single subtest and assert all three vars together.

♻️ Suggested refactor
-	t.Run("component-2 with terraform.state static args", func(t *testing.T) {
-		componentSection, err := e.ExecuteDescribeComponent(
-			&e.ExecuteDescribeComponentParams{
-				Component:            "component-2",
-				Stack:                "test",
-				ProcessTemplates:     true,
-				ProcessYamlFunctions: true,
-			},
-		)
-
-		require.NoError(t, err, "component-2 with static terraform.state args should load without errors")
-		require.NotNil(t, componentSection)
-
-		vars, ok := componentSection["vars"].(map[string]interface{})
-		require.True(t, ok, "vars should be a map")
-
-		// Static arg: !terraform.state component-1 foo
-		assert.Equal(t, "foo-from-state", vars["foo"], "static terraform.state should resolve from state file")
-	})
-
-	t.Run("component-2 with terraform.state templated stack arg", func(t *testing.T) {
-		// This is the core test for GitHub issue `#2052`.
-		componentSection, err := e.ExecuteDescribeComponent(
-			&e.ExecuteDescribeComponentParams{
-				Component:            "component-2",
-				Stack:                "test",
-				ProcessTemplates:     true,
-				ProcessYamlFunctions: true,
-			},
-		)
-
-		require.NoError(t, err, "component-2 with templated terraform.state args should NOT fail with 'did not find expected key' (issue `#2052`)")
-		require.NotNil(t, componentSection)
-
-		vars, ok := componentSection["vars"].(map[string]interface{})
-		require.True(t, ok, "vars should be a map")
-
-		assert.Equal(t, "bar-from-state", vars["bar"], "templated terraform.state with custom delimiters should resolve correctly")
-		assert.Equal(t, "baz-from-state", vars["baz"], "templated terraform.state with custom delimiters should resolve correctly")
-	})
+	t.Run("component-2 with static and templated terraform.state args (issue `#2052`)", func(t *testing.T) {
+		componentSection, err := e.ExecuteDescribeComponent(
+			&e.ExecuteDescribeComponentParams{
+				Component:            "component-2",
+				Stack:                "test",
+				ProcessTemplates:     true,
+				ProcessYamlFunctions: true,
+			},
+		)
+
+		require.NoError(t, err, "component-2 should NOT fail with 'did not find expected key' (issue `#2052`)")
+		require.NotNil(t, componentSection)
+
+		vars, ok := componentSection["vars"].(map[string]interface{})
+		require.True(t, ok, "vars should be a map")
+
+		// Static arg: !terraform.state component-1 foo
+		assert.Equal(t, "foo-from-state", vars["foo"], "static terraform.state should resolve from state file")
+		// Templated arg: !terraform.state component-1 '{{ .stack }}' bar/baz
+		assert.Equal(t, "bar-from-state", vars["bar"], "templated terraform.state with custom delimiters should resolve correctly")
+		assert.Equal(t, "baz-from-state", vars["baz"], "templated terraform.state with custom delimiters should resolve correctly")
+	})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/yaml_functions_custom_delimiters_test.go` around lines 53 - 99, The two
subtests both call e.ExecuteDescribeComponent with identical params; call
ExecuteDescribeComponent once (e.g., in a single t.Run combining "component-2
with terraform.state ...") and reuse the returned componentSection for all
assertions: check vars map existence and assert vars["foo"] == "foo-from-state"
and vars["bar"] == "bar-from-state" and vars["baz"] == "baz-from-state"; keep
ProcessTemplates and ProcessYamlFunctions true and preserve the original
assertions' messages, but remove the duplicated second ExecuteDescribeComponent
invocation.

104-131: TestCustomDelimitersTemplateProcessing duplicates the component-1 subtest already in TestTerraformStateWithCustomDelimiters.

The single subtest here ("settings templates resolve with custom delimiters", lines 111–131) navigates to the same fixture, calls ExecuteDescribeComponent for the same component/stack, and asserts the same three vars as lines 29–51. Consider removing this function or folding any genuinely distinct scenario into TestTerraformStateWithCustomDelimiters.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/yaml_functions_custom_delimiters_test.go` around lines 104 - 131, The
test TestCustomDelimitersTemplateProcessing duplicates the subtest "settings
templates resolve with custom delimiters" (same fixture, same
ExecuteDescribeComponent call and same assertions) already covered by
TestTerraformStateWithCustomDelimiters; remove
TestCustomDelimitersTemplateProcessing entirely or fold any unique assertions
into TestTerraformStateWithCustomDelimiters, ensuring you keep the
ExecuteDescribeComponent call and the three asserts for vars["foo"],
vars["bar"], and vars["baz"] in the remaining test and delete the redundant
subtest.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/fixes/2026-02-20-terraform-state-custom-delimiters.md`:
- Around line 92-97: The YAML example has a misplaced closing single-quote in
the data value causing incorrect quoting: fix the string for the key `data` so
the outer single-quoted scalar encloses the entire value `!store my-store ''{{
.stack }}'' key` (i.e., move the closing quote to after `key`) so all internal
single-quotes remain escaped and the outer quote wraps the whole value; check
the similar patterns for `test` and `cmd` to ensure each value uses matching
outer single-quotes with internal quotes escaped as `''`.

In `@pkg/utils/yaml_utils.go`:
- Around line 639-644: Fix two typos in the doc comments: in the
ConvertToYAMLPreservingDelimiters comment update the example delimiter to
["'{{", "}}'"] (replace the stray trailing double-quote) and change the
description that currently says internal single quotes are escaped as a
double-quote to correctly state they are escaped by doubling (''), and make the
same correction in the ensureDoubleQuotedForDelimiterSafety comment where it
currently shows (") — replace with ('').
- Around line 645-684: Two call sites still invoke ConvertToYAML before template
expansion; replace those calls with ConvertToYAMLPreservingDelimiters and pass
the active template delimiters so single-quote conflicts are avoided.
Specifically, in the backend config template path (the call that currently does
ProcessTmpl(ConvertToYAML(...), ...)) and in the stack manifest template path
(the call that feeds a YAML string directly into ProcessTmpl), swap
ConvertToYAML(...) for ConvertToYAMLPreservingDelimiters(..., delimiters) where
"delimiters" is the same delimiter slice/variable used for template processing;
preserve existing opts/indent/error handling and return semantics. Ensure you
import/propagate any delimiter variable if not already in scope and keep
ProcessTmpl usage unchanged.

In `@tests/yaml_functions_custom_delimiters_test.go`:
- Line 22: Replace the meaningless require.NotNil(t, atmosConfig) (since
atmosConfig is a value of type schema.AtmosConfiguration) with a meaningful
assertion: remove the NotNil call and instead assert the Initialized field
(e.g., require.True(t, atmosConfig.Initialized)) or another relevant
field/property; locate the assertion near the existing require.NoError that
checks err in tests/yaml_functions_custom_delimiters_test.go and update both
occurrences referenced (lines around the current checks, variable atmosConfig)
accordingly.

---

Nitpick comments:
In `@pkg/utils/yaml_utils.go`:
- Around line 639-717: The three cohesive helpers
ConvertToYAMLPreservingDelimiters, delimiterConflictsWithYAMLQuoting, and
ensureDoubleQuotedForDelimiterSafety should be moved out of pkg/utils into a new
purpose-built package (e.g., pkg/yamlenc) to avoid utils bloat; create
pkg/yamlenc with the same implementations, move any tightly-coupled dependencies
they need (e.g., YAMLOptions, DefaultYAMLIndent, yamlBufferPool or expose
internal helpers from existing packages), update imports where
ConvertToYAMLPreservingDelimiters is called to use the new package, keep
function names or export them as needed, and remove these functions from
pkg/utils (and adjust/remove the file-length nolint comment if it’s no longer
needed). Ensure package-level tests and build pass after updating references.

In `@tests/yaml_functions_custom_delimiters_test.go`:
- Around line 53-99: The two subtests both call e.ExecuteDescribeComponent with
identical params; call ExecuteDescribeComponent once (e.g., in a single t.Run
combining "component-2 with terraform.state ...") and reuse the returned
componentSection for all assertions: check vars map existence and assert
vars["foo"] == "foo-from-state" and vars["bar"] == "bar-from-state" and
vars["baz"] == "baz-from-state"; keep ProcessTemplates and ProcessYamlFunctions
true and preserve the original assertions' messages, but remove the duplicated
second ExecuteDescribeComponent invocation.
- Around line 104-131: The test TestCustomDelimitersTemplateProcessing
duplicates the subtest "settings templates resolve with custom delimiters" (same
fixture, same ExecuteDescribeComponent call and same assertions) already covered
by TestTerraformStateWithCustomDelimiters; remove
TestCustomDelimitersTemplateProcessing entirely or fold any unique assertions
into TestTerraformStateWithCustomDelimiters, ensuring you keep the
ExecuteDescribeComponent call and the three asserts for vars["foo"],
vars["bar"], and vars["baz"] in the remaining test and delete the redundant
subtest.

…eview feedback

- Move ConvertToYAMLPreservingDelimiters, DelimiterConflictsWithYAMLQuoting,
  and EnsureDoubleQuotedForDelimiterSafety from pkg/utils/ to pkg/yaml/
- Add perf.Track() to all public functions
- Merge duplicate component-2 subtests in integration test
- Remove duplicate TestCustomDelimitersTemplateProcessing
- Replace require.NotNil on struct with require.True on Initialized field
- Fix YAML example typo in docs (mismatched single-quote escaping for !store)
- Update all callers to use atmosYaml import alias

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
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: 1

🧹 Nitpick comments (2)
tests/yaml_functions_custom_delimiters_test.go (1)

32-39: Consider passing the already-loaded atmosConfig to avoid redundant re-initialization.

AtmosConfig is optional but passing &atmosConfig directly to ExecuteDescribeComponentParams avoids a second InitCliConfig call inside ExecuteDescribeComponent and makes the test's configuration dependency explicit. Same applies to the second subtest at Lines 59-66.

♻️ Proposed refactor
 componentSection, err := e.ExecuteDescribeComponent(
     &e.ExecuteDescribeComponentParams{
+        AtmosConfig:          &atmosConfig,
         Component:            "component-1",
         Stack:                "test",
         ProcessTemplates:     true,
         ProcessYamlFunctions: true,
     },
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/yaml_functions_custom_delimiters_test.go` around lines 32 - 39, The
test currently calls e.ExecuteDescribeComponent without supplying the preloaded
atmosConfig, causing ExecuteDescribeComponent to re-run InitCliConfig; update
both subtests to pass the already-loaded atmosConfig by setting the AtmosConfig
field on ExecuteDescribeComponentParams to &atmosConfig so
ExecuteDescribeComponent can use the provided config and avoid redundant
initialization (refer to e.ExecuteDescribeComponent and
ExecuteDescribeComponentParams and the local atmosConfig variable).
pkg/yaml/delimiter.go (1)

83-102: perf.Track deferred on every recursive invocation.

EnsureDoubleQuotedForDelimiterSafety calls itself recursively for every child node. Each call acquires and defers a perf.Track closure. When tracking is disabled (the common case), perf.Track returns a no-op immediately, so runtime impact is minimal. For very large YAML documents, though, the cumulative closure allocation per node is non-zero.

Consider extracting the recursion into a private unexported helper (e.g., ensureDoubleQuotedForDelimiterSafetyRecursive) so the perf.Track fires only once on the public entry point.

♻️ Proposed refactor
 func EnsureDoubleQuotedForDelimiterSafety(node *goyaml.Node) {
 	defer perf.Track(nil, "yaml.EnsureDoubleQuotedForDelimiterSafety")()

-	if node == nil {
-		return
-	}
-
-	switch node.Kind {
-	case goyaml.ScalarNode:
-		if strings.ContainsRune(node.Value, '\'') {
-			node.Style = goyaml.DoubleQuotedStyle
-		}
-	case goyaml.DocumentNode, goyaml.MappingNode, goyaml.SequenceNode:
-		for _, child := range node.Content {
-			EnsureDoubleQuotedForDelimiterSafety(child)
-		}
-	}
+	ensureDoubleQuotedRecursive(node)
+}
+
+func ensureDoubleQuotedRecursive(node *goyaml.Node) {
+	if node == nil {
+		return
+	}
+
+	switch node.Kind {
+	case goyaml.ScalarNode:
+		if strings.ContainsRune(node.Value, '\'') {
+			node.Style = goyaml.DoubleQuotedStyle
+		}
+	case goyaml.DocumentNode, goyaml.MappingNode, goyaml.SequenceNode:
+		for _, child := range node.Content {
+			ensureDoubleQuotedRecursive(child)
+		}
+	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/yaml/delimiter.go` around lines 83 - 102, The perf.Track call is deferred
on every recursive invocation of EnsureDoubleQuotedForDelimiterSafety causing
unnecessary closure allocations for large YAMLs; refactor by making
EnsureDoubleQuotedForDelimiterSafety the public entry that calls perf.Track
once, then delegate recursion to a new unexported helper (e.g.,
ensureDoubleQuotedForDelimiterSafetyRecursive) which contains the current switch
logic and recursive calls, and remove perf.Track from the recursive helper so
only the public function wraps the single perf.Track defer.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/yaml/delimiter_test.go`:
- Around line 27-40: The test "left delimiter with single quote conflicts" is
duplicated with "both delimiters with single quotes conflict" because both use
delimiters []string{"'{{", "}}'"}; update the first test case (name: "left
delimiter with single quote conflicts") so its delimiters slice only has the
left delimiter with a single quote and a normal right delimiter (e.g., change
delimiters from {"'{{", "}}'"} to {"'{{", "}}"}) so it exercises the left-only
conflict path; locate and update the delimiters slice in the test case entry in
delimiter_test.go.

---

Nitpick comments:
In `@pkg/yaml/delimiter.go`:
- Around line 83-102: The perf.Track call is deferred on every recursive
invocation of EnsureDoubleQuotedForDelimiterSafety causing unnecessary closure
allocations for large YAMLs; refactor by making
EnsureDoubleQuotedForDelimiterSafety the public entry that calls perf.Track
once, then delegate recursion to a new unexported helper (e.g.,
ensureDoubleQuotedForDelimiterSafetyRecursive) which contains the current switch
logic and recursive calls, and remove perf.Track from the recursive helper so
only the public function wraps the single perf.Track defer.

In `@tests/yaml_functions_custom_delimiters_test.go`:
- Around line 32-39: The test currently calls e.ExecuteDescribeComponent without
supplying the preloaded atmosConfig, causing ExecuteDescribeComponent to re-run
InitCliConfig; update both subtests to pass the already-loaded atmosConfig by
setting the AtmosConfig field on ExecuteDescribeComponentParams to &atmosConfig
so ExecuteDescribeComponent can use the provided config and avoid redundant
initialization (refer to e.ExecuteDescribeComponent and
ExecuteDescribeComponentParams and the local atmosConfig variable).

…plicate test case

- Extract ensureDoubleQuotedRecursive so perf.Track fires only once
  on the public EnsureDoubleQuotedForDelimiterSafety entry point
- Fix duplicate test input: "left delimiter" case now uses right
  delimiter without single quote, distinct from "both delimiters" case

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
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.

🧹 Nitpick comments (2)
pkg/yaml/delimiter_test.go (2)

230-237: Bare type assertions in tests will panic with an unhelpful stack trace on failure.

Several lines do parsed["vars"].(map[string]interface{}) without the two-value form. If the YAML structure changes or Unmarshal produces an unexpected shape, you get a panic instead of a clear test failure message.

Not blocking, but using require keeps failures readable:

Suggested approach
-		vars := parsed["vars"].(map[string]interface{})
+		varsRaw, ok := parsed["vars"].(map[string]interface{})
+		require.True(t, ok, "expected vars to be map[string]interface{}")
+		vars := varsRaw

Also applies to: 439-445, 531-537

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/yaml/delimiter_test.go` around lines 230 - 237, The test uses a bare type
assertion parsed["vars"].(map[string]interface{}) which will panic on unexpected
shapes; change to the two-value form (vars, ok :=
parsed["vars"].(map[string]interface{})) and add a require.True(t, ok,
"parsed.vars should be map[string]interface{}") (or
require.IsType/require.NotNil) before using vars so failures produce readable
test errors; apply the same fix to the other occurrences around the file (the
other blocks referencing parsed and vars at/near lines 439-445 and 531-537) and
keep the existing assert.Equal checks for individual keys after the guarded
assertion.

407-414: !repo-root and !cwd entries never hit the assertion branch.

These values don't contain single quotes, so the if strings.Contains(fn.value, "'") guard at line 434 skips all meaningful assertions. They only confirm ConvertToYAMLPreservingDelimiters doesn't error — which is already covered by other cases.

Either remove them or add a distinct assertion for the no-quotes path so they carry their weight.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/yaml/delimiter_test.go` around lines 407 - 414, The two table cases with
name "repo-root (no args but with quotes in context)" and "cwd (no args but with
quotes in context)" never exercise the single-quote assertion because fn.value
has no single quotes; update the test around ConvertToYAMLPreservingDelimiters
to either remove those cases or add an explicit assertion for the no-quotes path
(e.g., assert the output is unchanged or contains the same delimiter token) so
they validate behavior — find the test loop that references fn.value and add a
branch for values without "'" that checks the expected output for "!repo-root"
and "!cwd" (or remove the entries if redundant).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/yaml/delimiter_test.go`:
- Around line 230-237: The test uses a bare type assertion
parsed["vars"].(map[string]interface{}) which will panic on unexpected shapes;
change to the two-value form (vars, ok :=
parsed["vars"].(map[string]interface{})) and add a require.True(t, ok,
"parsed.vars should be map[string]interface{}") (or
require.IsType/require.NotNil) before using vars so failures produce readable
test errors; apply the same fix to the other occurrences around the file (the
other blocks referencing parsed and vars at/near lines 439-445 and 531-537) and
keep the existing assert.Equal checks for individual keys after the guarded
assertion.
- Around line 407-414: The two table cases with name "repo-root (no args but
with quotes in context)" and "cwd (no args but with quotes in context)" never
exercise the single-quote assertion because fn.value has no single quotes;
update the test around ConvertToYAMLPreservingDelimiters to either remove those
cases or add an explicit assertion for the no-quotes path (e.g., assert the
output is unchanged or contains the same delimiter token) so they validate
behavior — find the test loop that references fn.value and add a branch for
values without "'" that checks the expected output for "!repo-root" and "!cwd"
(or remove the entries if redundant).

coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 21, 2026
The .gitignore pattern `*.tfstate.*` was matching the directory name
`terraform.tfstate.d` in the fixture path, preventing the test state
file from being committed. Force-add the file so CI can find it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov bot commented Feb 21, 2026

Codecov Report

❌ Patch coverage is 88.46154% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.22%. Comparing base (761d419) to head (f31c7e3).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
pkg/yaml/delimiter.go 90.69% 2 Missing and 2 partials ⚠️
internal/exec/terraform_generate_backends.go 0.00% 1 Missing ⚠️
internal/exec/terraform_generate_varfiles.go 0.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2098      +/-   ##
==========================================
+ Coverage   76.19%   76.22%   +0.02%     
==========================================
  Files         829      830       +1     
  Lines       78551    78597      +46     
==========================================
+ Hits        59852    59907      +55     
+ Misses      14956    14947       -9     
  Partials     3743     3743              
Flag Coverage Δ
unittests 76.22% <88.46%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
internal/exec/describe_stacks.go 73.17% <100.00%> (ø)
internal/exec/utils.go 82.31% <100.00%> (+0.08%) ⬆️
internal/exec/terraform_generate_backends.go 22.16% <0.00%> (ø)
internal/exec/terraform_generate_varfiles.go 9.77% <0.00%> (ø)
pkg/yaml/delimiter.go 90.69% <90.69%> (ø)

... and 5 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@aknysh aknysh merged commit a5a525c into main Feb 22, 2026
57 checks passed
@aknysh aknysh deleted the aknysh/fix-terraform-state-7 branch February 22, 2026 06:35
@github-actions
Copy link
Copy Markdown

These changes were released in v1.208.0-rc.0.

@github-actions
Copy link
Copy Markdown

These changes were released in v1.208.0-test.15.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

patch A minor, backward compatible change size/xl Extra large size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

!terraform.state fails with templated arguments when custom delimiters include quotes

2 participants