Skip to content

fix: prevent YAML function concatenation in lists#1506

Merged
aknysh merged 12 commits intomainfrom
yaml-functions-in-lists
Sep 25, 2025
Merged

fix: prevent YAML function concatenation in lists#1506
aknysh merged 12 commits intomainfrom
yaml-functions-in-lists

Conversation

@osterman
Copy link
Member

@osterman osterman commented Sep 24, 2025

what

  • Fix an issue where YAML functions (like !terraform.state and !terraform.output) in lists could potentially cause concatenation errors
  • Clear custom YAML tags after processing to prevent double handling by the YAML decoder
  • Add comprehensive tests for YAML functions in lists with different return types

why

Users reported an error when using YAML functions in lists:

invalid number of arguments in the Atmos YAML function !terraform.state image1-ecr global repository_arn !terraform.state image2-ecr global repository_arn

The issue occurred because custom YAML tags were being incorporated into node values but the tags themselves weren't being cleared. This could lead to unexpected behavior during YAML decoding, especially when functions appeared in lists.

The fix ensures:

  • Each list item with a YAML function remains separate and independent
  • Functions can return any type (string, map, list, etc.) without type conflicts
  • No concatenation of multiple functions occurs
  • Mixed lists with functions and static values work correctly

references

  • Related to user-reported issue with ECR repository ARNs in lists
  • Fixes YAML function processing to be more robust

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Prevented re-processing of custom YAML tags to avoid unintended concatenation and ensure correct value types.
    • Ensured YAML function values in lists, maps, and nested structures preserve boundaries (no concatenation).
  • Tests

    • Added comprehensive tests covering YAML function return types, list behavior, and execution safety.
    • Added Terraform-based fixtures and stack scenarios to simulate outputs/states.
    • Improved test URL/path handling for cross-platform reliability.

When YAML functions (like !terraform.state or !terraform.output) are used in lists,
they were keeping their custom tags after being processed, which could cause issues
during YAML decoding. This fix clears the custom tag after incorporating it into
the node value, preventing potential double processing or concatenation issues.

The fix ensures that:
- Each list item with a YAML function remains separate
- Functions can return any type (string, map, list, etc.)
- No "invalid number of arguments" errors occur from concatenation

Added comprehensive tests to verify:
- YAML functions work correctly in lists
- Different return types are handled properly
- No concatenation of multiple functions occurs
- Mixed lists with functions and static values work

Fixes issue where users reported: "invalid number of arguments in the Atmos YAML
function !terraform.state image1-ecr global repository_arn !terraform.state
image2-ecr global repository_arn"

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@osterman osterman requested review from a team as code owners September 24, 2025 14:04
@github-actions github-actions bot added the size/l Large size PR label Sep 24, 2025
@mergify mergify bot added the triage Needs triage label Sep 24, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 24, 2025

Warning

Rate limit exceeded

@osterman has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 9 minutes and 6 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 3fc2add and c35185d.

📒 Files selected for processing (1)
  • internal/exec/template_utils_test.go (3 hunks)
📝 Walkthrough

Walkthrough

Adds tests validating Atmos YAML custom function handling (especially !terraform.state and !terraform.output) in lists, maps, and nested structures, prevents YAML decoder re-processing of custom tags by clearing node tags after value computation, adds Terraform test fixtures and a scenario, and improves URL handling in template tests.

Changes

Cohort / File(s) Summary
Exec tests: YAML functions in lists and return types
internal/exec/yaml_func_test.go, internal/exec/yaml_func_return_types_test.go
New tests covering YAML custom functions in lists, maps, and nested structures; assert correct return types, no unintended concatenation/concatenation errors, and safe execution when functions appear in lists.
YAML utils: tag handling
pkg/utils/yaml_utils.go
Clear YAML node Tag after computing the value for recognized Atmos custom tags to avoid the decoder re-processing the tag; preserve computed value for later post-processing.
Fixtures: Terraform component for YAML function testing
tests/fixtures/components/terraform/yaml-function-test/main.tf, .../outputs.tf, .../variables.tf
Add Terraform test component with variables and outputs to simulate scalar, list, map, and nested object return types used by YAML function tests.
Scenario config: atmos.yaml
tests/fixtures/scenarios/yaml-functions-in-lists/atmos.yaml
Update base_path and included_paths for the yaml-functions-in-lists scenario.
Stacks fixture: YAML functions usage
tests/fixtures/scenarios/yaml-functions-in-lists/stacks/test-yaml-functions.yaml
New stack fixture exercising YAML functions across lists, maps, nested objects, and edge cases to reproduce prior concatenation issues.
Template tests: URL handling improvement
internal/exec/template_utils_test.go
Replace naive file:// trimming with proper url.Parse handling and Windows path normalization when converting test file URLs to paths.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Test as Tests/CLI
    participant Decoder as YAML Decoder
    participant TagEval as getValueWithTag
    participant Post as processCustomTags

    Test->>Decoder: Load & unmarshal YAML (includes custom tags)
    Decoder->>TagEval: Encounter custom tag (!terraform.state/!terraform.output)
    TagEval-->>Decoder: Return computed value
    Note right of TagEval #f8f0d0: Clear node.Tag to avoid re-processing
    Decoder-->>Test: Return decoded structure (values preserved)
    Test->>Post: Post-process decoded structure to finalize functions
    Post-->>Test: Resolved values/types
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

patch

Suggested reviewers

  • aknysh
  • milldr
  • Benbentwo

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Linked Issues Check ⚠️ Warning The pull request implements YAML function handling and testing but does not address the linked issue #1500’s objectives, which focus on restoring vendor pull behavior and glob matching fixes for AtmosVendorConfig. Align the changes with the vendor pull requirements of issue #1500 or detach the YAML function enhancements into a separate pull request to avoid scope mismatch.
Out of Scope Changes Check ⚠️ Warning The addition of YAML function tests and tag-clearing logic is unrelated to the vendor pull functionality described in linked issue #1500, indicating these changes fall outside the issue’s intended scope. Extract the YAML function improvements into a dedicated pull request or refocus this PR exclusively on the vendor pull behavior fixes described in the linked issue.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title concisely and accurately describes the primary change, which is to prevent YAML function concatenation in lists, matching the main fix implemented in code and tests.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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.

coderabbitai[bot]
coderabbitai bot previously approved these changes Sep 24, 2025
@mergify mergify bot removed the triage Needs triage label Sep 24, 2025
@osterman osterman added the patch A minor, backward compatible change label Sep 24, 2025
@codecov
Copy link

codecov bot commented Sep 24, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 57.07%. Comparing base (83e8a1f) to head (c35185d).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1506      +/-   ##
==========================================
+ Coverage   57.02%   57.07%   +0.04%     
==========================================
  Files         286      286              
  Lines       30587    30592       +5     
==========================================
+ Hits        17443    17461      +18     
+ Misses      11305    11290      -15     
- Partials     1839     1841       +2     
Flag Coverage Δ
unittests 57.07% <100.00%> (+0.04%) ⬆️

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

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

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

Refactored the test fixtures to follow the established pattern of keeping
reusable components separate from scenarios:

- Created a generic yaml-function-test component in the shared fixtures
- Removed scenario-specific component implementation
- Updated test stacks to use the reusable component
- Maintained comprehensive test coverage for all data types

The new yaml-function-test component:
- Tests scalars (string, number, boolean)
- Tests collections (lists, maps)
- Tests complex nested structures
- Doesn't impose cloud provider conventions
- Provides comprehensive outputs for validation

This follows the established pattern and makes the test components
more maintainable and reusable across different test scenarios.
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)
tests/fixtures/components/terraform/yaml-function-test/variables.tf (1)

37-41: Type doesn’t match intent: allow truly mixed types.

The description says the list can contain different types, but list(string) restricts it. Prefer list(any) to align with test intent.

Apply:

 variable "mixed_type_list" {
   description = "List that can contain different types (as strings)"
-  type        = list(string)
+  type        = list(any)
   default     = []
 }
tests/fixtures/components/terraform/yaml-function-test/main.tf (1)

11-15: Locals are unused; consider exposing or removing.

Either add outputs for simulated_string, simulated_list, simulated_map or drop the locals to reduce noise.

For example, in outputs.tf:

+output "simulated_string" {
+  description = "Derived string via locals"
+  value       = local.simulated_string
+}
+
+output "simulated_list" {
+  description = "Derived list via locals"
+  value       = local.simulated_list
+}
+
+output "simulated_map" {
+  description = "Derived map via locals"
+  value       = local.simulated_map
+}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 34b108a and 0f15003.

📒 Files selected for processing (5)
  • tests/fixtures/components/terraform/yaml-function-test/main.tf (1 hunks)
  • tests/fixtures/components/terraform/yaml-function-test/outputs.tf (1 hunks)
  • tests/fixtures/components/terraform/yaml-function-test/variables.tf (1 hunks)
  • tests/fixtures/scenarios/yaml-functions-in-lists/atmos.yaml (1 hunks)
  • tests/fixtures/scenarios/yaml-functions-in-lists/stacks/test-yaml-functions.yaml (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • tests/fixtures/scenarios/yaml-functions-in-lists/stacks/test-yaml-functions.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/fixtures/scenarios/yaml-functions-in-lists/atmos.yaml
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-12-01T00:33:20.298Z
Learnt from: aknysh
PR: cloudposse/atmos#810
File: examples/tests/stacks/catalog/terraform/template-functions-test2/defaults.yaml:28-32
Timestamp: 2024-12-01T00:33:20.298Z
Learning: In `examples/tests/stacks/catalog/terraform/template-functions-test2/defaults.yaml`, `!exec atmos terraform output` is used in examples to demonstrate its usage, even though `!terraform.output` is the recommended approach according to the documentation.

Applied to files:

  • tests/fixtures/components/terraform/yaml-function-test/outputs.tf
  • tests/fixtures/components/terraform/yaml-function-test/main.tf
📚 Learning: 2024-12-03T04:01:16.446Z
Learnt from: aknysh
PR: cloudposse/atmos#810
File: website/docs/core-concepts/stacks/yaml-functions/terraform.output.mdx:0-0
Timestamp: 2024-12-03T04:01:16.446Z
Learning: In the `terraform.output.mdx` documentation file (`website/docs/core-concepts/stacks/yaml-functions/terraform.output.mdx`), the cache invalidation and cache scope behavior for the `!terraform.output` function are already described.

Applied to files:

  • tests/fixtures/components/terraform/yaml-function-test/outputs.tf
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Build (macos-latest, macos)
  • GitHub Check: Build (windows-latest, windows)
  • GitHub Check: Run pre-commit hooks
  • GitHub Check: Analyze (go)
  • GitHub Check: Lint (golangci)
  • GitHub Check: Summary
🔇 Additional comments (5)
tests/fixtures/components/terraform/yaml-function-test/variables.tf (2)

1-16: Solid, comprehensive variable coverage.

Definitions look consistent and useful for exercising many scenarios.

Also applies to: 18-36, 43-55, 56-74, 75-81


82-99: Widen YAML function variables to accept any type
Apply to tests/fixtures/components/terraform/yaml-function-test/variables.tf:

@@ variable "function_results_list" {
-  type        = list(string)
+  type        = list(any)
@@ variable "mixed_function_list" {
-  type        = list(string)
+  type        = list(any)
@@ variable "function_map_results" {
-  type        = map(string)
+  type        = map(any)
tests/fixtures/components/terraform/yaml-function-test/main.tf (1)

5-7: Terraform version constraint is fine.

>= 1.0 is broadly compatible for test fixtures.

tests/fixtures/components/terraform/yaml-function-test/outputs.tf (2)

1-13: Outputs look consistent and helpful for tests.

Covers scalar, list, map, nested, and helper summary outputs.

Also applies to: 14-18, 19-34, 35-45, 46-56, 57-72, 73-91, 92-100


97-100: Conditional guard is good; keep types flexible.

If function_results_list becomes list(any), this ternary still works. No change needed.

If you see type-unification warnings from Terraform here after widening types, we can add an explicit type = any to this output.

aknysh and others added 2 commits September 24, 2025 16:06
- Parse file URLs using url.Parse() instead of string manipulation
- Handle Windows-specific path formats (e.g., /D:/temp/file.json)
- Remove leading slash from Windows absolute paths after URL parsing
- Fixes test failures on Windows CI platform
@aknysh aknysh added no-release Do not create a new release (wait for additional code changes) and removed patch A minor, backward compatible change labels Sep 24, 2025
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: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0f15003 and 62528c3.

📒 Files selected for processing (1)
  • internal/exec/template_utils_test.go (3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*_test.go

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

**/*_test.go: Every new feature must include comprehensive unit tests
Test both happy paths and error conditions
Use table-driven tests for multiple scenarios

**/*_test.go: Test file naming symmetry: test files mirror implementation structure with _test.go suffix
Unit tests should focus on pure functions and use table-driven tests
ALWAYS use t.Skipf() instead of t.Skip() and provide a clear reason for skipped tests
NEVER use t.Skipf() without a reason

Files:

  • internal/exec/template_utils_test.go
**/*.go

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

**/*.go: All code must pass golangci-lint checks
Follow Go error handling idioms and use meaningful error messages
Wrap errors with context using fmt.Errorf("context: %w", err)
Consider custom error types for domain-specific errors
Follow standard Go coding style; run gofmt and goimports
Use snake_case for environment variables
Document complex logic with inline comments

**/*.go: All comments must end with periods - comments should be complete sentences (enforced by golangci-lint's godot linter)
All errors MUST be wrapped using static errors defined in errors/errors.go; never use dynamic errors directly
Always wrap errors with the static error first and add context-specific details after the static error; use %w verb to preserve error chain
ALWAYS use viper.BindEnv() for environment variable binding, and EVERY env var MUST have an ATMOS_ alternative
Distinguish between logging and UI output: only use logging for system/debug info, and use stderr/UI for prompts, progress, errors, and data for piping should go to stdout
Most text UI MUST go to stderr; only data/results go to stdout for proper piping; use utils.PrintfMessageToTUI() for UI messages
Atmos MUST work on Linux, macOS, and Windows—write portable implementations and prefer SDKs over calling binaries when available
ALWAYS compile after making changes; verify no compilation errors; run tests after successful compile; fix compilation errors immediately before proceeding
Write a test to reproduce the bug before fixing, run the test and confirm failure, fix the bug iteratively, and verify fix doesn't break existing tests

Files:

  • internal/exec/template_utils_test.go
internal/exec/**.go

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer extending existing utilities in internal/exec/ and pkg/ over creating new ones—search for existing methods before implementation

Files:

  • internal/exec/template_utils_test.go
🧠 Learnings (3)
📓 Common learnings
Learnt from: aknysh
PR: cloudposse/atmos#810
File: examples/tests/stacks/catalog/terraform/template-functions-test2/defaults.yaml:28-32
Timestamp: 2024-12-01T00:33:20.298Z
Learning: In `examples/tests/stacks/catalog/terraform/template-functions-test2/defaults.yaml`, `!exec atmos terraform output` is used in examples to demonstrate its usage, even though `!terraform.output` is the recommended approach according to the documentation.
📚 Learning: 2024-10-23T20:13:23.054Z
Learnt from: osterman
PR: cloudposse/atmos#731
File: pkg/utils/file_utils.go:198-202
Timestamp: 2024-10-23T20:13:23.054Z
Learning: In `pkg/utils/file_utils.go`, the current implementation of the `IsURL` function is considered sufficient; avoid suggesting more complex URL validation in future reviews.

Applied to files:

  • internal/exec/template_utils_test.go
📚 Learning: 2024-12-25T20:28:47.526Z
Learnt from: osterman
PR: cloudposse/atmos#887
File: internal/exec/stack_processor_utils.go:380-380
Timestamp: 2024-12-25T20:28:47.526Z
Learning: Windows path handling often requires `filepath.Join` to ensure correct separators and comparisons. Insufficient tests can break cross-platform compatibility, so migrating from `path.Join` to `filepath.Join` needs thorough testing on Windows before merging.

Applied to files:

  • internal/exec/template_utils_test.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Build (windows-latest, windows)
  • GitHub Check: Analyze (go)
  • GitHub Check: Lint (golangci)
  • GitHub Check: Run pre-commit hooks
  • GitHub Check: Summary

- Use filepath.FromSlash() to convert URL paths to OS-specific format
- Handle Windows drive letters correctly (remove leading slash from /C:/)
- Convert forward slashes to backslashes on Windows
- Fixes test failures on Windows CI where paths were incorrectly formatted
coderabbitai[bot]
coderabbitai bot previously approved these changes Sep 25, 2025
aknysh and others added 4 commits September 25, 2025 10:20
- Handle both file://C:/path and file:///C:/path URL formats on Windows
- Safely check for drive letters to avoid index out of bounds
- Combine Host and Path when needed for proper Windows file paths
- Fixes test failures on Windows where drive letters were missing
@aknysh aknysh merged commit e0d7cd7 into main Sep 25, 2025
52 checks passed
@aknysh aknysh deleted the yaml-functions-in-lists branch September 25, 2025 19:12
@github-actions
Copy link

These changes were released in v1.192.0.

@github-actions
Copy link

These changes were released in v1.193.0-test.1.

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

Labels

no-release Do not create a new release (wait for additional code changes) size/l Large size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants