Skip to content

refactor: Create pkg/function and pkg/yaml packages#1886

Merged
aknysh merged 13 commits intomainfrom
osterman/refactor-yaml-pkg
Dec 22, 2025
Merged

refactor: Create pkg/function and pkg/yaml packages#1886
aknysh merged 13 commits intomainfrom
osterman/refactor-yaml-pkg

Conversation

@osterman
Copy link
Member

@osterman osterman commented Dec 17, 2025

What

Create new packages for modularized function handling and YAML utilities:

  • pkg/function/: Format-agnostic function registry and handlers (env, exec, random, include, template, store, terraform, aws, repo-root)
  • pkg/yaml/: YAML-specific utilities (position tracking, error handling, types)
  • pkg/aws/identity/: Consolidated AWS identity caching (from internal/aws_utils)

Why

  • Separation of Concerns: Function handlers work across all formats (YAML, HCL, JSON); only parsing differs
  • Code Reuse: Registry pattern eliminates duplication across formats
  • Extensibility: New functions without modifying core logic
  • Testing: Interface-driven design with dependency injection
  • AWS Consolidation: Eliminate duplicate identity caching logic

References

Foundation for PR #1842 which refactors YAML processing.
Consolidates AWS identity handling per request.

Summary by CodeRabbit

  • New Features

    • New AWS functions: aws.account_id, aws.caller_identity_arn, aws.caller_identity_user_id, aws.region.
    • Format-agnostic function registry added with built-in handlers: env, exec, store, store.get, template, terraform.output, terraform.state, random, literal, include, repo-root.
    • Store interface now supports direct key retrieval (GetKey).
  • Refactor

    • Centralized AWS identity handling with per-context caching and Atmos auth support.
    • YAML utilities improved with position tracking and clearer error signals.

✏️ Tip: You can customize this high-level summary in your review settings.

…tion registry

## Summary

Create new packages for modularized function handling and YAML utilities:
- **pkg/function/**: Format-agnostic function registry (handlers work across YAML, HCL, JSON)
  - Function interfaces and base types
  - Thread-safe registry with Get/Register/GetByPhase operations
  - Function handlers: env, exec, random, include, template, store, terraform, aws, repo-root
  - Cycle detection context for dependency resolution
- **pkg/yaml/**: YAML-specific utilities (parsing, position tracking, error handling)
  - Type definitions (LongString, YAMLOptions)
  - YAML position and range tracking
  - Error types for YAML processing
- **pkg/aws/identity/**: Consolidated AWS identity caching
  - Moved from internal/aws_utils and internal/exec/aws_getter.go
  - Thread-safe CallerIdentity caching with auth context support
  - AWS config loading with role assumption

## Why

- **Separation of Concerns**: Function handlers are format-agnostic; only YAML/HCL/JSON parsing differs
- **Code Reuse**: Single registry used across all formats, reducing duplication
- **Extensibility**: Registry pattern enables new functions without modifying core logic
- **Testing**: Interface-driven design with dependency injection for testability
- **AWS Consolidation**: Eliminate duplicate AWS identity caching logic

## References

Preparation for PR #1842 which refactors YAML processing to use pkg/function registry.
Consolidates AWS identity handling per request to eliminate internal/aws_utils duplication.

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

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@osterman osterman requested a review from a team as a code owner December 17, 2025 21:31
@github-actions github-actions bot added the size/xl Extra large size PR label Dec 17, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 17, 2025

📝 Walkthrough

Walkthrough

This PR refactors AWS utilities and introduces a comprehensive format-agnostic function registry system. It removes internal/aws_utils, migrates AWS identity logic to pkg/aws/identity, creates a new pkg/function package with 15+ function implementations, adds YAML utilities to pkg/yaml, and updates callers throughout the codebase.

Changes

Cohort / File(s) Summary
AWS identity migration
internal/aws_utils/aws_utils.go, internal/aws_utils/aws_utils_test.go, pkg/aws/identity/doc.go, pkg/aws/identity/identity.go, pkg/aws/identity/identity_test.go, pkg/aws/identity/mock_identity.go
Removes internal/aws_utils and implements equivalent AWS config loading, STS GetCallerIdentity, per-auth-context caching, and test/mocking support in new pkg/aws/identity.
Function registry core & API
pkg/function/function.go, pkg/function/registry.go, pkg/function/errors.go, pkg/function/phase.go, pkg/function/tags.go, pkg/function/defaults.go, pkg/function/context.go, pkg/function/resolution/context.go, pkg/function/..._test.go
Adds a format-agnostic function registry (thread-safe), execution Phase enum, centralized tags, ExecutionContext, per-goroutine resolution context with cycle detection, and comprehensive tests.
Function implementations
pkg/function/aws.go, pkg/function/env.go, pkg/function/exec.go, pkg/function/git_root.go, pkg/function/include.go, pkg/function/literal.go, pkg/function/random.go, pkg/function/store.go, pkg/function/template.go, pkg/function/terraform.go, pkg/function/*_test.go
Implements many concrete functions (env, exec, random, template, include, literal, git-root, store, terraform., aws.) with constructors, Execute methods, argument parsing, and tests.
YAML utilities & types
pkg/yaml/doc.go, pkg/yaml/errors.go, pkg/yaml/position.go, pkg/yaml/types.go, pkg/yaml/*_test.go
Adds YAML package docs, error definitions, position extraction for provenance, LongString type with folded-style marshaling, and tests.
Call-site updates
internal/exec/aws_getter.go, internal/exec/yaml_func_aws_test.go, internal/terraform_backend/terraform_backend_s3.go, pkg/provisioner/backend/s3.go
Replaces references to internal/aws_utils with pkg/aws/identity (API rename: LoadAWSConfigWithAuth → LoadConfigWithAuth) and delegates identity-getter/caching logic to the new package.
Mocks & store changes
pkg/aws/identity/mock_identity.go, pkg/store/mock_store.go, pkg/store/store.go
Adds GoMock-generated mocks and extends Store interface with GetKey(key string) (any, error).
Tests: stability & cleanup
internal/exec/packer_test.go, internal/exec/terraform_test.go, internal/exec/terraform_utils_test.go, internal/exec/workflow_adapters_test.go
Improves stdout/stderr capture and restoration in tests and synchronization around pipe readers/writers.
Lint/config tweaks & misc
.golangci.yml, tools/lintroller/rule_perf_track.go, pkg/config/load.go
Adds depguard exclusion for pkg/aws/identity/**, adds perf-track receiver exclusions and nil guard, and replaces type switch with errors.As in config path handling.
Docs & website
website/blog/2025-12-18-function-registry-package.mdx
Adds blog post describing the function registry, YAML utilities, and AWS identity refactor.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas to focus during review:

  • pkg/aws/identity/identity.go — RWMutex usage, double-checked locking, cache key correctness, error caching semantics.
  • pkg/function/registry.go — case-insensitive name/alias handling, conflict detection, and locking strategies.
  • pkg/function/resolution/context.go — goroutine ID derivation safety, Scoped/GetOrCreate lifecycle, and cycle error formatting.
  • pkg/function/store.go — argument parsing (pipe/default/query), retrieval fallback logic, and error wrapping.
  • Call-site updates — ensure LoadConfigWithAuth usage and caller behavior match previous semantics where used (terraform backend, provisioner).

Possibly related PRs

Suggested reviewers

  • osterman

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.81% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the main change: creating two new packages (pkg/function and pkg/yaml), which aligns with the primary objective of introducing a format-agnostic function registry and YAML utilities.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch osterman/refactor-yaml-pkg

📜 Recent 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 14f3233 and 0f933ac.

📒 Files selected for processing (1)
  • pkg/store/store.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/store/store.go
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Acceptance Tests (macos)
  • GitHub Check: Acceptance Tests (windows)
  • GitHub Check: Summary

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.

@mergify
Copy link

mergify bot commented Dec 17, 2025

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

github-actions bot commented Dec 17, 2025

Dependency Review

✅ No vulnerabilities or license issues found.

Scanned Files

None

osterman and others added 2 commits December 17, 2025 15:34
Add LiteralFunction handler that preserves values exactly as written,
bypassing template processing. This enables passing template-like syntax
({{...}}, ${...}) to downstream tools like Terraform, Helm, and ArgoCD.

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

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

codecov bot commented Dec 17, 2025

Codecov Report

❌ Patch coverage is 87.60684% with 116 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.50%. Comparing base (5538ee0) to head (0f933ac).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
pkg/aws/identity/identity.go 38.39% 64 Missing and 5 partials ⚠️
pkg/function/env.go 77.27% 8 Missing and 2 partials ⚠️
pkg/function/store.go 92.06% 6 Missing and 4 partials ⚠️
pkg/function/random.go 86.00% 5 Missing and 2 partials ⚠️
pkg/function/literal.go 66.66% 4 Missing ⚠️
pkg/function/resolution/context.go 95.34% 2 Missing and 2 partials ⚠️
pkg/yaml/position.go 92.72% 2 Missing and 2 partials ⚠️
pkg/function/defaults.go 88.88% 2 Missing and 1 partial ⚠️
pkg/function/exec.go 90.90% 1 Missing and 1 partial ⚠️
pkg/function/git_root.go 88.88% 1 Missing and 1 partial ⚠️
... and 1 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1886      +/-   ##
==========================================
+ Coverage   73.23%   73.50%   +0.27%     
==========================================
  Files         614      633      +19     
  Lines       57827    58652     +825     
==========================================
+ Hits        42352    43115     +763     
- Misses      12523    12571      +48     
- Partials     2952     2966      +14     
Flag Coverage Δ
unittests 73.50% <87.60%> (+0.27%) ⬆️

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

Files with missing lines Coverage Δ
internal/exec/aws_getter.go 100.00% <100.00%> (+38.88%) ⬆️
internal/terraform_backend/terraform_backend_s3.go 88.29% <100.00%> (ø)
pkg/config/load.go 78.29% <100.00%> (-0.04%) ⬇️
pkg/function/aws.go 100.00% <100.00%> (ø)
pkg/function/context.go 100.00% <100.00%> (ø)
pkg/function/function.go 100.00% <100.00%> (ø)
pkg/function/include.go 100.00% <100.00%> (ø)
pkg/function/phase.go 100.00% <100.00%> (ø)
pkg/function/registry.go 100.00% <100.00%> (ø)
pkg/function/tags.go 100.00% <100.00%> (ø)
... and 15 more

... 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.

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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/exec/yaml_func_aws_test.go (1)

618-638: Unsafe assertions inside goroutines.

assert.NoError and assert.Equal inside goroutines won't properly fail the test—they may panic after the test completes or report to the wrong testing.T. Collect results or use a mutex-protected error slice.

 	const numGoroutines = 50
-	done := make(chan bool, numGoroutines)
+	results := make(chan error, numGoroutines)

 	for i := 0; i < numGoroutines; i++ {
 		go func() {
 			identity, err := getAWSCallerIdentityCached(ctx, atmosConfig, nil)
-			assert.NoError(t, err)
-			assert.Equal(t, "777777777777", identity.Account)
-			done <- true
+			if err != nil {
+				results <- err
+				return
+			}
+			if identity.Account != "777777777777" {
+				results <- errors.New("unexpected account")
+				return
+			}
+			results <- nil
 		}()
 	}

 	for i := 0; i < numGoroutines; i++ {
-		<-done
+		if err := <-results; err != nil {
+			t.Errorf("goroutine failed: %v", err)
+		}
 	}
🧹 Nitpick comments (15)
pkg/function/phase.go (1)

21-31: Remove perf.Track from this trivial accessor.

Phase.String() is a simple switch returning a constant string. The tracking overhead exceeds the method cost here. Based on learnings, trivial accessors and hot-path methods should avoid perf.Track().

 func (p Phase) String() string {
-	defer perf.Track(nil, "function.Phase.String")()
-
 	switch p {
 	case PreMerge:
 		return "pre-merge"

If perf.Track is removed entirely, the perf import can also be dropped.

pkg/function/context.go (1)

42-66: Consider removing perf.Track from these trivial fluent methods.

WithFile, WithBaseDir, and WithStackInfo are simple copy-and-set operations. Per the project's learnings, tracking overhead on such trivial methods provides no actionable performance data. These may be called frequently during YAML processing.

 func (ctx *ExecutionContext) WithFile(file string) *ExecutionContext {
-	defer perf.Track(nil, "function.ExecutionContext.WithFile")()
-
 	newCtx := *ctx
 	newCtx.File = file
 	return &newCtx
 }

 func (ctx *ExecutionContext) WithBaseDir(baseDir string) *ExecutionContext {
-	defer perf.Track(nil, "function.ExecutionContext.WithBaseDir")()
-
 	newCtx := *ctx
 	newCtx.BaseDir = baseDir
 	return &newCtx
 }

 func (ctx *ExecutionContext) WithStackInfo(stackInfo *schema.ConfigAndStacksInfo) *ExecutionContext {
-	defer perf.Track(nil, "function.ExecutionContext.WithStackInfo")()
-
 	newCtx := *ctx
 	newCtx.StackInfo = stackInfo
 	return &newCtx
 }
pkg/aws/identity/identity_test.go (1)

159-171: Consider removing this tautological test.

This test simply verifies that struct field assignments work, which is guaranteed by Go. It doesn't test any behavior or logic. As per coding guidelines, avoid tautological tests that don't add value.

pkg/function/exec.go (1)

49-49: Consider using ExecutionContext environment.

The function passes os.Environ() directly, which doesn't include the component's env section from stack manifests. The EnvFunction checks execCtx.StackInfo.GetComponentEnvSection() first to respect stack-specific environment variables. Consider merging or prioritizing the execution context environment if provided.

-	res, err := utils.ExecuteShellAndReturnOutput(args, YAMLTag(TagExec)+" "+args, ".", os.Environ(), false)
+	// Merge component env section with os.Environ() if available.
+	env := os.Environ()
+	if execCtx != nil && execCtx.StackInfo != nil {
+		if envSection := execCtx.StackInfo.GetComponentEnvSection(); envSection != nil {
+			for k, v := range envSection {
+				env = append(env, fmt.Sprintf("%s=%v", k, v))
+			}
+		}
+	}
+	res, err := utils.ExecuteShellAndReturnOutput(args, YAMLTag(TagExec)+" "+args, ".", env, false)
pkg/function/terraform.go (1)

3-11: Imports should have blank line separators between groups.

Per coding guidelines, imports should be organized in three groups separated by blank lines: stdlib, 3rd-party, Atmos packages.

 import (
 	"context"
 	"fmt"
 	"strings"
-
+
 	log "github.com/cloudposse/atmos/pkg/logger"
 	"github.com/cloudposse/atmos/pkg/perf"
 	"github.com/cloudposse/atmos/pkg/utils"
 )
pkg/yaml/position.go (2)

3-7: Import order should be: stdlib, 3rd-party, Atmos packages.

 import (
+	goyaml "gopkg.in/yaml.v3"
+
 	"github.com/cloudposse/atmos/pkg/perf"
 	"github.com/cloudposse/atmos/pkg/utils"
-	goyaml "gopkg.in/yaml.v3"
 )

62-67: Simplify path building using AppendJSONPathKey.

The utility function already handles empty base paths correctly.

-			var path string
-			if currentPath == "" {
-				path = key
-			} else {
-				path = utils.AppendJSONPathKey(currentPath, key)
-			}
+			path := utils.AppendJSONPathKey(currentPath, key)
pkg/yaml/errors.go (1)

5-23: Keep errors local if this follows the established package-specific pattern; otherwise centralize to errors/errors.go.

Per coding guidelines, errors should be centralized in errors/errors.go. However, if pkg/yaml follows a pattern similar to toolchain-specific packages (where canonical sentinels live in the package with re-exports), this is acceptable. If moving these to the central file, ensure no import cycles are introduced with pkg/schema or other dependencies. This can be addressed in a separate refactor PR if deferring changes.

pkg/function/random.go (1)

65-68: Unreachable case 0 branch.

The case 0 block is unreachable—args == "" is already handled at lines 54-56 before splitting. SplitStringByDelimiter filters empty strings, so len(parts) won't be 0 for non-empty input.

 	switch len(parts) {
-	case 0:
-		min = defaultRandomMin
-		max = defaultRandomMax
-
 	case 1:
pkg/function/store.go (1)

61-66: Inconsistent nil-value handling compared to StoreGetFunction.

StoreFunction.Execute doesn't check if value == nil before returning (only handles errors), but StoreGetFunction.Execute at lines 134-136 explicitly checks for nil value and applies the default. Consider aligning behavior.

 	value, err := store.Get(params.stack, params.component, params.key)
 	if err != nil {
 		if params.defaultValue != nil {
 			return *params.defaultValue, nil
 		}
 		return nil, fmt.Errorf("%w: failed to get key '%s': %w", ErrExecutionFailed, params.key, err)
 	}
+
+	// Check if the retrieved value is nil and use default if provided.
+	if value == nil && params.defaultValue != nil {
+		return *params.defaultValue, nil
+	}
pkg/function/tags.go (1)

63-105: AllTags() and TagsMap can drift out of sync.

Both list the same tags but are maintained separately. If a new tag is added to one but not the other, bugs ensue. Consider generating TagsMap from AllTags() or using a single source of truth.

One approach—compute TagsMap at init time:

var TagsMap map[string]bool

func init() {
	TagsMap = make(map[string]bool, len(allTagsList))
	for _, tag := range allTagsList {
		TagsMap[tag] = true
	}
}
pkg/function/resolution/context.go (1)

168-193: Error message includes sentinel twice.

Line 172 embeds errUtils.ErrCircularDependency as a string in the message, then line 192 wraps it again with %w. This duplicates the sentinel text in the output.

-	builder.WriteString(fmt.Sprintf("%s\n\n", errUtils.ErrCircularDependency))
+	builder.WriteString("Circular dependency detected\n\n")
 	builder.WriteString("Dependency chain:\n")

This keeps the message clean while still allowing errors.Is() checks via the wrap at line 192.

pkg/function/registry.go (1)

164-185: Unregister only works with primary name, not aliases.

If someone calls Unregister("some-alias"), it won't find the function since it looks in r.functions directly. Consider resolving aliases first.

 func (r *Registry) Unregister(name string) {
 	defer perf.Track(nil, "function.Registry.Unregister")()

 	r.mu.Lock()
 	defer r.mu.Unlock()

 	name = strings.ToLower(name)

+	// Resolve alias to primary name if needed.
+	if primaryName, exists := r.aliases[name]; exists {
+		name = primaryName
+	}
+
 	fn, exists := r.functions[name]
 	if !exists {
 		return
 	}
pkg/aws/identity/identity.go (1)

56-66: Redundant struct copy.

GetCallerIdentity already returns *CallerIdentity, so the field-by-field copy is unnecessary.

 	// Use the exported function to get caller identity.
 	result, err := GetCallerIdentity(ctx, "", "", 0, authContext)
 	if err != nil {
 		return nil, err
 	}

-	identity := &CallerIdentity{
-		Account: result.Account,
-		Arn:     result.Arn,
-		UserID:  result.UserID,
-		Region:  result.Region,
-	}
-
 	log.Debug("Retrieved AWS caller identity",
-		"account", identity.Account,
-		"arn", identity.Arn,
-		"user_id", identity.UserID,
-		"region", identity.Region,
+		"account", result.Account,
+		"arn", result.Arn,
+		"user_id", result.UserID,
+		"region", result.Region,
 	)

-	return identity, nil
+	return result, nil
pkg/function/aws.go (1)

144-179: LGTM, with an optional DRY observation.

All four Execute methods share nearly identical structure. Could consolidate with a field-selector approach if you want to reduce duplication in the future, but the current explicit implementation is perfectly readable.

If desired, you could consolidate the four handlers using a field extractor:

type awsIdentityFieldFunc func(*awsIdentity.CallerIdentity) string

func (f *BaseFunction) executeAWSField(ctx context.Context, execCtx *ExecutionContext, extract awsIdentityFieldFunc) (any, error) {
    identity, err := getAWSIdentity(ctx, execCtx)
    if err != nil {
        log.Error("Failed to get AWS caller identity", "error", err)
        return nil, err
    }
    return extract(identity), nil
}

Then each Execute becomes a one-liner. Low priority though - current approach is clear.

📜 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 3840735 and 43e7c13.

📒 Files selected for processing (35)
  • .golangci.yml (1 hunks)
  • internal/aws_utils/aws_utils.go (0 hunks)
  • internal/aws_utils/aws_utils_test.go (0 hunks)
  • internal/exec/aws_getter.go (2 hunks)
  • internal/exec/yaml_func_aws_test.go (3 hunks)
  • internal/terraform_backend/terraform_backend_s3.go (2 hunks)
  • pkg/aws/identity/doc.go (1 hunks)
  • pkg/aws/identity/identity.go (1 hunks)
  • pkg/aws/identity/identity_test.go (1 hunks)
  • pkg/function/aws.go (1 hunks)
  • pkg/function/context.go (1 hunks)
  • pkg/function/defaults.go (1 hunks)
  • pkg/function/doc.go (1 hunks)
  • pkg/function/env.go (1 hunks)
  • pkg/function/errors.go (1 hunks)
  • pkg/function/exec.go (1 hunks)
  • pkg/function/function.go (1 hunks)
  • pkg/function/git_root.go (1 hunks)
  • pkg/function/include.go (1 hunks)
  • pkg/function/literal.go (1 hunks)
  • pkg/function/phase.go (1 hunks)
  • pkg/function/random.go (1 hunks)
  • pkg/function/registry.go (1 hunks)
  • pkg/function/registry_test.go (1 hunks)
  • pkg/function/resolution/context.go (1 hunks)
  • pkg/function/resolution/errors.go (1 hunks)
  • pkg/function/store.go (1 hunks)
  • pkg/function/tags.go (1 hunks)
  • pkg/function/template.go (1 hunks)
  • pkg/function/terraform.go (1 hunks)
  • pkg/provisioner/backend/s3.go (2 hunks)
  • pkg/yaml/doc.go (1 hunks)
  • pkg/yaml/errors.go (1 hunks)
  • pkg/yaml/position.go (1 hunks)
  • pkg/yaml/types.go (1 hunks)
💤 Files with no reviewable changes (2)
  • internal/aws_utils/aws_utils_test.go
  • internal/aws_utils/aws_utils.go
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

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

**/*.go: Use Viper for managing configuration, environment variables, and flags in CLI commands
Use interfaces for external dependencies to facilitate mocking and consider using testify/mock for creating mock implementations
All code must pass golangci-lint checks
Follow Go's error handling idioms: use meaningful error messages, wrap errors with context using fmt.Errorf("context: %w", err), and consider using custom error types for domain-specific errors
Follow standard Go coding style: use gofmt and goimports to format code, prefer short descriptive variable names, use kebab-case for command-line flags, and snake_case for environment variables
Document all exported functions, types, and methods following Go's documentation conventions
Document complex logic with inline comments in Go code
Support configuration via files, environment variables, and flags following the precedence order: flags > environment variables > config file > defaults
Provide clear error messages to users, include troubleshooting hints when appropriate, and log detailed errors for debugging

**/*.go: NEVER use fmt.Fprintf(os.Stdout/Stderr) or fmt.Println(); use data.* or ui.* functions instead
All comments must end with periods (enforced by godot linter)
Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages; maintain aliases: cfg, log, u, errUtils
Add defer perf.Track(atmosConfig, "pkg.FuncName")() + blank line to all public functions for performance tracking; use nil if no atmosConfig param
All errors MUST be wrapped using static errors defined in errors/errors.go; use errors.Join for combining multiple errors; use fmt.Errorf with %w for adding string context; use error builder for complex errors; use errors.Is() for error checking; NEVER use dynamic errors directly
Use go.uber.org/mock/mockgen with //go:generate directives for mock generation; never create manual mocks
Keep files small...

Files:

  • pkg/function/context.go
  • internal/terraform_backend/terraform_backend_s3.go
  • pkg/function/resolution/errors.go
  • pkg/provisioner/backend/s3.go
  • pkg/function/defaults.go
  • pkg/function/include.go
  • pkg/function/errors.go
  • pkg/function/git_root.go
  • pkg/yaml/doc.go
  • pkg/function/env.go
  • pkg/aws/identity/identity_test.go
  • pkg/yaml/types.go
  • pkg/function/phase.go
  • pkg/yaml/position.go
  • pkg/function/terraform.go
  • pkg/function/registry_test.go
  • pkg/function/store.go
  • pkg/function/tags.go
  • internal/exec/aws_getter.go
  • pkg/function/doc.go
  • pkg/aws/identity/doc.go
  • pkg/function/exec.go
  • internal/exec/yaml_func_aws_test.go
  • pkg/function/random.go
  • pkg/function/template.go
  • pkg/yaml/errors.go
  • pkg/function/function.go
  • pkg/aws/identity/identity.go
  • pkg/function/registry.go
  • pkg/function/resolution/context.go
  • pkg/function/literal.go
  • pkg/function/aws.go
**/*_test.go

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

**/*_test.go: Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages
Use table-driven tests for testing multiple scenarios in Go
Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

**/*_test.go: Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage
Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Files:

  • pkg/aws/identity/identity_test.go
  • pkg/function/registry_test.go
  • internal/exec/yaml_func_aws_test.go
.golangci.yml

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

Configure golangci-lint in .golangci.yml with specific linters enabled: gofmt, goimports, govet, staticcheck, errcheck, ineffassign, misspell, unused, revive, gocritic

Files:

  • .golangci.yml
🧠 Learnings (67)
📓 Common learnings
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: errors/errors.go:184-203
Timestamp: 2025-12-13T06:10:13.688Z
Learning: cloudposse/atmos: For toolchain work, duplicate/unused error sentinels in errors/errors.go should be cleaned up in a separate refactor PR and not block feature PRs; canonical toolchain sentinels live under toolchain/registry with re-exports in toolchain/errors.go.
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1363
File: internal/exec/template_utils.go:18-18
Timestamp: 2025-07-05T20:59:02.914Z
Learning: In the Atmos project, gomplate v4 is imported with a blank import (`_ "github.com/hairyhenderson/gomplate/v4"`) alongside v3 imports to resolve AWS SDK version conflicts. V3 uses older AWS SDK versions that conflict with newer AWS modules used by Atmos. A full migration to v4 requires extensive refactoring due to API changes and should be handled in a separate PR.
Learnt from: Benbentwo
Repo: cloudposse/atmos PR: 1475
File: pkg/auth/manager.go:304-312
Timestamp: 2025-09-25T01:02:48.697Z
Learning: The auth manager in pkg/auth/manager.go should remain cloud-agnostic and not contain AWS-specific logic or references to specific cloud providers. Keep the manager generic and extensible.
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.614Z
Learning: Avoid utils package bloat; don't add new functions to pkg/utils/; create purpose-built packages instead (examples: pkg/store/, pkg/git/, pkg/pro/, pkg/filesystem/)
📚 Learning: 2025-12-16T18:20:55.614Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.614Z
Learning: Applies to **/*.go : Add `defer perf.Track(atmosConfig, "pkg.FuncName")()` + blank line to all public functions for performance tracking; use nil if no atmosConfig param

Applied to files:

  • pkg/function/context.go
  • pkg/provisioner/backend/s3.go
  • pkg/function/env.go
  • pkg/function/phase.go
  • pkg/yaml/position.go
  • pkg/function/terraform.go
  • pkg/function/store.go
  • pkg/function/tags.go
  • pkg/function/doc.go
  • pkg/yaml/errors.go
  • pkg/function/function.go
📚 Learning: 2025-10-13T18:13:54.020Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1622
File: pkg/perf/perf.go:140-184
Timestamp: 2025-10-13T18:13:54.020Z
Learning: In pkg/perf/perf.go, the `trackWithSimpleStack` function intentionally skips ownership checks at call stack depth > 1 to avoid expensive `getGoroutineID()` calls on every nested function. This is a performance optimization for the common single-goroutine execution case (most Atmos commands), accepting the rare edge case of potential metric corruption if multi-goroutine execution occurs at depth > 1. The ~19× performance improvement justifies this trade-off.

Applied to files:

  • pkg/function/context.go
📚 Learning: 2025-09-25T01:02:48.697Z
Learnt from: Benbentwo
Repo: cloudposse/atmos PR: 1475
File: pkg/auth/manager.go:304-312
Timestamp: 2025-09-25T01:02:48.697Z
Learning: The auth manager in pkg/auth/manager.go should remain cloud-agnostic and not contain AWS-specific logic or references to specific cloud providers. Keep the manager generic and extensible.

Applied to files:

  • internal/terraform_backend/terraform_backend_s3.go
  • pkg/provisioner/backend/s3.go
  • pkg/aws/identity/doc.go
  • pkg/aws/identity/identity.go
  • .golangci.yml
  • pkg/function/aws.go
📚 Learning: 2025-07-05T20:59:02.914Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1363
File: internal/exec/template_utils.go:18-18
Timestamp: 2025-07-05T20:59:02.914Z
Learning: In the Atmos project, gomplate v4 is imported with a blank import (`_ "github.com/hairyhenderson/gomplate/v4"`) alongside v3 imports to resolve AWS SDK version conflicts. V3 uses older AWS SDK versions that conflict with newer AWS modules used by Atmos. A full migration to v4 requires extensive refactoring due to API changes and should be handled in a separate PR.

Applied to files:

  • internal/terraform_backend/terraform_backend_s3.go
  • pkg/provisioner/backend/s3.go
  • pkg/function/tags.go
📚 Learning: 2025-09-13T18:06:07.674Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1466
File: toolchain/list.go:39-42
Timestamp: 2025-09-13T18:06:07.674Z
Learning: In the cloudposse/atmos repository, for UI messages in the toolchain package, use utils.PrintfMessageToTUI instead of log.Error or fmt.Fprintln(os.Stderr, ...). Import pkg/utils with alias "u" to follow the established pattern.

Applied to files:

  • internal/terraform_backend/terraform_backend_s3.go
  • pkg/provisioner/backend/s3.go
📚 Learning: 2025-12-16T18:20:55.614Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.614Z
Learning: Applies to **/*.go : Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages; maintain aliases: cfg, log, u, errUtils

Applied to files:

  • internal/terraform_backend/terraform_backend_s3.go
  • pkg/provisioner/backend/s3.go
  • pkg/function/tags.go
  • pkg/yaml/errors.go
📚 Learning: 2025-09-10T21:17:55.273Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1466
File: toolchain/http_client_test.go:3-10
Timestamp: 2025-09-10T21:17:55.273Z
Learning: In the cloudposse/atmos repository, imports should never be changed as per samtholiya's coding guidelines.

Applied to files:

  • internal/terraform_backend/terraform_backend_s3.go
  • pkg/provisioner/backend/s3.go
📚 Learning: 2025-11-09T19:06:58.470Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1752
File: pkg/profile/list/formatter_table.go:27-29
Timestamp: 2025-11-09T19:06:58.470Z
Learning: In the cloudposse/atmos repository, performance tracking with `defer perf.Track()` is enforced on all functions via linting, including high-frequency utility functions, formatters, and renderers. This is a repository-wide policy to maintain consistency and avoid making case-by-case judgment calls about which functions should have profiling.

Applied to files:

  • internal/terraform_backend/terraform_backend_s3.go
  • pkg/provisioner/backend/s3.go
  • pkg/function/phase.go
  • pkg/function/tags.go
📚 Learning: 2025-05-30T03:21:37.197Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1274
File: go.mod:63-63
Timestamp: 2025-05-30T03:21:37.197Z
Learning: The redis dependency (github.com/redis/go-redis/v9) in the atmos project is only used in tests, not in production code.

Applied to files:

  • internal/terraform_backend/terraform_backend_s3.go
  • pkg/provisioner/backend/s3.go
📚 Learning: 2025-11-08T19:56:18.660Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1697
File: internal/exec/oci_utils.go:0-0
Timestamp: 2025-11-08T19:56:18.660Z
Learning: In the Atmos codebase, when a function receives an `*schema.AtmosConfiguration` parameter, it should read configuration values from `atmosConfig.Settings` fields rather than using direct `os.Getenv()` or `viper.GetString()` calls. The Atmos pattern is: viper.BindEnv in cmd/root.go binds environment variables → Viper unmarshals into atmosConfig.Settings via mapstructure → business logic reads from the Settings struct. This provides centralized config management, respects precedence, and enables testability. Example: `atmosConfig.Settings.AtmosGithubToken` instead of `os.Getenv("ATMOS_GITHUB_TOKEN")` in functions like `getGHCRAuth` in internal/exec/oci_utils.go.

Applied to files:

  • internal/terraform_backend/terraform_backend_s3.go
📚 Learning: 2025-12-13T06:10:13.688Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: errors/errors.go:184-203
Timestamp: 2025-12-13T06:10:13.688Z
Learning: cloudposse/atmos: For toolchain work, duplicate/unused error sentinels in errors/errors.go should be cleaned up in a separate refactor PR and not block feature PRs; canonical toolchain sentinels live under toolchain/registry with re-exports in toolchain/errors.go.

Applied to files:

  • internal/terraform_backend/terraform_backend_s3.go
  • pkg/provisioner/backend/s3.go
  • pkg/yaml/errors.go
📚 Learning: 2025-09-07T18:07:00.549Z
Learnt from: Benbentwo
Repo: cloudposse/atmos PR: 1452
File: cmd/auth_login.go:43-44
Timestamp: 2025-09-07T18:07:00.549Z
Learning: In the atmos project, the identity flag is defined as a persistent flag on the auth root command (cmd/auth.go), making it available to all auth subcommands without needing to be redefined in each individual subcommand.

Applied to files:

  • internal/terraform_backend/terraform_backend_s3.go
📚 Learning: 2024-10-23T21:36:40.262Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 740
File: cmd/cmd_utils.go:340-359
Timestamp: 2024-10-23T21:36:40.262Z
Learning: In the Go codebase for Atmos, when reviewing functions like `checkAtmosConfig` in `cmd/cmd_utils.go`, avoid suggesting refactoring to return errors instead of calling `os.Exit` if such changes would significantly increase the scope due to the need to update multiple call sites.

Applied to files:

  • internal/terraform_backend/terraform_backend_s3.go
  • pkg/provisioner/backend/s3.go
  • pkg/yaml/errors.go
  • pkg/function/function.go
📚 Learning: 2025-10-02T19:17:51.630Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1504
File: pkg/profiler/profiler.go:20-31
Timestamp: 2025-10-02T19:17:51.630Z
Learning: In pkg/profiler/profiler.go, profiler-specific errors (ErrUnsupportedProfileType, ErrStartCPUProfile, ErrStartTraceProfile, ErrCreateProfileFile) must remain local and cannot be moved to errors/errors.go due to an import cycle: pkg/profiler → errors → pkg/schema → pkg/profiler. This is a valid exception to the centralized errors policy.

Applied to files:

  • pkg/function/resolution/errors.go
  • pkg/function/errors.go
📚 Learning: 2025-11-10T20:03:56.875Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1775
File: pkg/auth/providers/aws/sso_provisioning.go:40-79
Timestamp: 2025-11-10T20:03:56.875Z
Learning: In the Atmos AWS SSO provider (pkg/auth/providers/aws/sso_provisioning.go), the OAuth access token from the AWS SSO device flow is intentionally stored in the `AccessKeyID` field of `AWSCredentials` during authentication. This token is then extracted and used for ListAccounts and ListAccountRoles API calls during identity provisioning. This design reuses the existing `AWSCredentials` type for token transport rather than creating a separate credential type.

Applied to files:

  • pkg/provisioner/backend/s3.go
  • pkg/aws/identity/identity.go
📚 Learning: 2024-12-11T18:40:12.808Z
Learnt from: Listener430
Repo: cloudposse/atmos PR: 844
File: cmd/helmfile.go:37-37
Timestamp: 2024-12-11T18:40:12.808Z
Learning: In the atmos project, `cliConfig` is initialized within the `cmd` package in `root.go` and can be used in other command files.

Applied to files:

  • pkg/provisioner/backend/s3.go
📚 Learning: 2025-01-19T22:30:27.600Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 0
File: :0-0
Timestamp: 2025-01-19T22:30:27.600Z
Learning: The Atmos YAML function `!include` allows downloading local or remote files from different sources and assigning their contents to sections in stack manifests. It supports various protocols (file, http, git, s3, etc.) and can filter content using YQ expressions.

Applied to files:

  • pkg/function/include.go
  • pkg/yaml/errors.go
📚 Learning: 2024-11-19T23:00:45.899Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 795
File: internal/exec/stack_processor_utils.go:378-386
Timestamp: 2024-11-19T23:00:45.899Z
Learning: In the `ProcessYAMLConfigFile` function within `internal/exec/stack_processor_utils.go`, directory traversal in stack imports is acceptable and should not be restricted.

Applied to files:

  • pkg/function/include.go
  • pkg/yaml/doc.go
  • pkg/yaml/errors.go
📚 Learning: 2025-12-16T18:20:55.614Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.614Z
Learning: Applies to **/*.go : All errors MUST be wrapped using static errors defined in errors/errors.go; use errors.Join for combining multiple errors; use fmt.Errorf with %w for adding string context; use error builder for complex errors; use errors.Is() for error checking; NEVER use dynamic errors directly

Applied to files:

  • pkg/function/errors.go
  • pkg/yaml/errors.go
📚 Learning: 2025-01-25T04:01:58.095Z
Learnt from: Listener430
Repo: cloudposse/atmos PR: 934
File: internal/exec/docs_generate.go:98-101
Timestamp: 2025-01-25T04:01:58.095Z
Learning: In the `generateSingleReadme` function of the docs generation feature (internal/exec/docs_generate.go), errors from `fetchAndParseYAML` should be logged and skipped rather than causing early returns. This is by design to process all inputs and collect all errors, instead of failing fast on the first error.

Applied to files:

  • pkg/function/errors.go
  • pkg/yaml/doc.go
  • pkg/yaml/errors.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*.go : Document all exported functions, types, and methods following Go's documentation conventions

Applied to files:

  • pkg/yaml/doc.go
  • pkg/function/doc.go
📚 Learning: 2025-04-24T01:06:15.259Z
Learnt from: haitham911
Repo: cloudposse/atmos PR: 1202
File: pkg/config/process_yaml.go:0-0
Timestamp: 2025-04-24T01:06:15.259Z
Learning: When processing YAML structures with gopkg.in/yaml.v3, the Value field of a Node is only meant for scalar values. Complex data types like maps and arrays are represented through child nodes in the YAML tree structure.

Applied to files:

  • pkg/yaml/doc.go
  • pkg/yaml/position.go
📚 Learning: 2025-09-30T19:03:50.738Z
Learnt from: Cerebrovinny
Repo: cloudposse/atmos PR: 1560
File: pkg/utils/string_utils.go:43-64
Timestamp: 2025-09-30T19:03:50.738Z
Learning: In the Atmos codebase, YAML tags like !terraform.output rely on positional arguments, so the SplitStringByDelimiter function in pkg/utils/string_utils.go must preserve empty strings (even after trimming quotes) to maintain the correct number of positional arguments. Filtering out empty values after trimming would collapse the array and break these function calls.

Applied to files:

  • pkg/yaml/doc.go
  • pkg/yaml/position.go
  • pkg/function/terraform.go
  • pkg/function/tags.go
📚 Learning: 2024-11-25T17:17:15.703Z
Learnt from: RoseSecurity
Repo: cloudposse/atmos PR: 797
File: pkg/list/atmos.yaml:213-214
Timestamp: 2024-11-25T17:17:15.703Z
Learning: The file `pkg/list/atmos.yaml` is primarily intended for testing purposes.

Applied to files:

  • pkg/yaml/doc.go
  • pkg/yaml/errors.go
📚 Learning: 2025-04-24T01:40:13.576Z
Learnt from: haitham911
Repo: cloudposse/atmos PR: 1202
File: pkg/config/process_yaml.go:0-0
Timestamp: 2025-04-24T01:40:13.576Z
Learning: When processing YAML nodes with custom directives in Go using gopkg.in/yaml.v3, setting node.Tag = "" is sufficient to prevent re-processing of the node. It's not necessary to also clear node.Value after updating the configuration store (e.g., Viper), as the original value doesn't affect subsequent operations once the tag is removed.

Applied to files:

  • pkg/yaml/doc.go
📚 Learning: 2024-12-12T17:13:53.409Z
Learnt from: RoseSecurity
Repo: cloudposse/atmos PR: 848
File: pkg/utils/doc_utils.go:19-22
Timestamp: 2024-12-12T17:13:53.409Z
Learning: In `pkg/utils/doc_utils.go`, the `DisplayDocs` function uses the `PAGER` environment variable, which is intentionally user-configurable to allow users to specify custom pager commands that fit their workflow; adding validation to restrict it is not desired.

Applied to files:

  • pkg/yaml/doc.go
📚 Learning: 2025-12-13T06:10:25.156Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: internal/exec/workflow_utils.go:0-0
Timestamp: 2025-12-13T06:10:25.156Z
Learning: Atmos workflows: In internal/exec/workflow_utils.go ExecuteWorkflow, non-identity steps intentionally use baseWorkflowEnv, which is constructed from the parent environment with PATH modifications for the toolchain. Avoid appending os.Environ() again; prefer documenting this behavior and testing that standard environment variables are preserved.

Applied to files:

  • pkg/function/env.go
📚 Learning: 2025-12-16T18:20:55.614Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.614Z
Learning: Applies to **/*.go : Use ATMOS_ prefix for environment variables with viper.BindEnv("ATMOS_VAR", "ATMOS_VAR", "FALLBACK")

Applied to files:

  • pkg/function/env.go
📚 Learning: 2025-01-19T22:30:27.600Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 0
File: :0-0
Timestamp: 2025-01-19T22:30:27.600Z
Learning: The Atmos YAML function `!env` is used to retrieve environment variables and assign them to sections in stack manifests. It supports both simple types (string, number, boolean) and complex types (JSON-encoded lists, maps, objects).

Applied to files:

  • pkg/function/env.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*.go : Support configuration via files, environment variables, and flags following the precedence order: flags > environment variables > config file > defaults

Applied to files:

  • pkg/function/env.go
📚 Learning: 2025-04-10T20:48:22.687Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1147
File: pkg/config/load.go:0-0
Timestamp: 2025-04-10T20:48:22.687Z
Learning: In the `bindEnv` function in `pkg/config/load.go`, panic is used deliberately instead of returning errors because errors from `BindEnv` would only occur due to developer mistakes. Using panic helps with early detection of these developer errors during initialization.

Applied to files:

  • pkg/function/env.go
📚 Learning: 2025-04-23T15:02:50.246Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1202
File: pkg/utils/yaml_func_exec.go:104-104
Timestamp: 2025-04-23T15:02:50.246Z
Learning: In the Atmos codebase, direct calls to `os.Getenv` should be avoided. Instead, use `viper.BindEnv` for environment variable access. This provides a consistent approach to configuration management across the codebase.

Applied to files:

  • pkg/function/env.go
📚 Learning: 2025-12-16T18:20:55.614Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.614Z
Learning: Applies to **/*_test.go : Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage

Applied to files:

  • pkg/aws/identity/identity_test.go
  • pkg/function/registry_test.go
📚 Learning: 2025-12-16T18:20:55.614Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.614Z
Learning: Define interfaces for all major functionality and use dependency injection for testability; generate mocks with go.uber.org/mock/mockgen

Applied to files:

  • pkg/aws/identity/identity_test.go
  • pkg/function/registry_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*.go : Use interfaces for external dependencies to facilitate mocking and consider using testify/mock for creating mock implementations

Applied to files:

  • pkg/aws/identity/identity_test.go
📚 Learning: 2025-12-16T18:20:55.614Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.614Z
Learning: Applies to **/*_test.go : Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Applied to files:

  • pkg/aws/identity/identity_test.go
  • pkg/function/registry_test.go
  • internal/exec/yaml_func_aws_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages

Applied to files:

  • pkg/aws/identity/identity_test.go
  • pkg/function/registry_test.go
📚 Learning: 2025-11-30T04:16:24.155Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1821
File: pkg/merge/deferred.go:34-48
Timestamp: 2025-11-30T04:16:24.155Z
Learning: In the cloudposse/atmos repository, the `defer perf.Track()` guideline applies to functions that perform meaningful work (I/O, computation, external calls), but explicitly excludes trivial accessors/mutators (e.g., simple getters, setters with single integer increments, string joins, or map appends) where the tracking overhead would exceed the actual method cost and provide no actionable performance data. Hot-path methods called in tight loops should especially avoid perf.Track() if they perform only trivial operations.

Applied to files:

  • pkg/function/phase.go
  • pkg/function/tags.go
📚 Learning: 2025-10-16T05:26:31.892Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1639
File: pkg/merge/merge.go:392-430
Timestamp: 2025-10-16T05:26:31.892Z
Learning: In Atmos merge operations (pkg/merge/merge.go), context.Positions is a PositionMap (map[string]*schema.Position) keyed by JSONPath strings, not a slice indexed by input array position. Position lookups are path-based, making them independent of input array reorganization or filtering. This design allows filtering empty inputs without affecting provenance tracking correctness.

Applied to files:

  • pkg/yaml/position.go
📚 Learning: 2024-12-17T07:08:41.288Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 863
File: internal/exec/yaml_func_terraform_output.go:34-38
Timestamp: 2024-12-17T07:08:41.288Z
Learning: In the `processTagTerraformOutput` function within `internal/exec/yaml_func_terraform_output.go`, parameters are separated by spaces and do not contain spaces. Therefore, using `strings.Fields()` for parsing is acceptable, and there's no need to handle parameters with spaces.

Applied to files:

  • pkg/function/terraform.go
  • pkg/function/tags.go
📚 Learning: 2025-01-09T22:37:01.004Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 914
File: cmd/terraform_commands.go:260-265
Timestamp: 2025-01-09T22:37:01.004Z
Learning: In the terraform commands implementation (cmd/terraform_commands.go), the direct use of `os.Args[2:]` for argument handling is intentionally preserved to avoid extensive refactoring. While it could be improved to use cobra's argument parsing, such changes should be handled in a dedicated PR to maintain focus and minimize risk.

Applied to files:

  • pkg/function/terraform.go
📚 Learning: 2025-06-23T02:14:30.937Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1327
File: cmd/terraform.go:111-117
Timestamp: 2025-06-23T02:14:30.937Z
Learning: In cmd/terraform.go, flags for the DescribeAffected function are added dynamically at runtime when info.Affected is true. This is intentional to avoid exposing internal flags like "file", "format", "verbose", "include-spacelift-admin-stacks", "include-settings", and "upload" in the terraform command interface, while still providing them for the shared DescribeAffected function used by both `atmos describe affected` and `atmos terraform apply --affected`.

Applied to files:

  • pkg/function/terraform.go
  • pkg/function/tags.go
📚 Learning: 2024-12-03T05:18:49.169Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 810
File: internal/exec/terraform_utils.go:40-213
Timestamp: 2024-12-03T05:18:49.169Z
Learning: In the context of the Atmos project, it's acceptable for functions like `execTerraformOutput` to remain as single functions if they perform a single purpose, such as retrieving Terraform outputs for a component in a stack, even if the function is lengthy.

Applied to files:

  • pkg/function/terraform.go
📚 Learning: 2024-10-27T04:41:49.199Z
Learnt from: haitham911
Repo: cloudposse/atmos PR: 727
File: internal/exec/terraform_clean.go:215-223
Timestamp: 2024-10-27T04:41:49.199Z
Learning: In `internal/exec/terraform_clean.go`, the function `determineCleanPath` is necessary and should not be removed.

Applied to files:

  • pkg/function/terraform.go
  • internal/exec/yaml_func_aws_test.go
📚 Learning: 2024-11-12T03:16:02.910Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 775
File: internal/exec/template_funcs_component.go:157-159
Timestamp: 2024-11-12T03:16:02.910Z
Learning: In the Go code for `componentFunc` in `internal/exec/template_funcs_component.go`, the function `cleanTerraformWorkspace` does not return errors, and it's acceptable if the file does not exist. Therefore, error handling for `cleanTerraformWorkspace` is not needed.

Applied to files:

  • pkg/function/terraform.go
📚 Learning: 2025-10-10T23:51:36.597Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1599
File: internal/exec/terraform.go:394-402
Timestamp: 2025-10-10T23:51:36.597Z
Learning: In Atmos (internal/exec/terraform.go), when adding OpenTofu-specific flags like `--var-file` for `init`, do not gate them based on command name (e.g., checking if `info.Command == "tofu"` or `info.Command == "opentofu"`) because command names don't reliably indicate the actual binary being executed (symlinks, aliases). Instead, document the OpenTofu requirement in code comments and documentation, trusting users who enable the feature (e.g., `PassVars`) to ensure their terraform command points to an OpenTofu binary.

Applied to files:

  • pkg/function/terraform.go
  • pkg/function/tags.go
📚 Learning: 2024-11-30T22:07:08.610Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 810
File: internal/exec/yaml_func_terraform_output.go:35-40
Timestamp: 2024-11-30T22:07:08.610Z
Learning: In the Go function `processTagTerraformOutput` in `internal/exec/yaml_func_terraform_output.go`, parameters cannot contain spaces. The code splits the input by spaces, and if the parameters contain spaces, `len(parts) != 3` will fail and show an error to the user.

Applied to files:

  • pkg/function/terraform.go
  • pkg/function/tags.go
📚 Learning: 2024-11-02T15:35:09.958Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 759
File: internal/exec/terraform.go:366-368
Timestamp: 2024-11-02T15:35:09.958Z
Learning: In `internal/exec/terraform.go`, the workspace cleaning code under both the general execution path and within the `case "init":` block is intentionally duplicated because the code execution paths are different. The `.terraform/environment` file should be deleted before executing `terraform init` in both scenarios to ensure a clean state.

Applied to files:

  • pkg/function/terraform.go
📚 Learning: 2024-12-01T00:33:20.298Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 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:

  • pkg/function/terraform.go
📚 Learning: 2024-12-03T05:29:07.718Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 810
File: internal/exec/terraform_utils.go:145-146
Timestamp: 2024-12-03T05:29:07.718Z
Learning: In the Atmos project, a 5-minute timeout in the `execTerraformOutput` function is acceptable for retrieving `terraform output` for a component in a stack.

Applied to files:

  • pkg/function/terraform.go
📚 Learning: 2024-12-03T04:01:16.446Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 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:

  • pkg/function/terraform.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Use table-driven tests for testing multiple scenarios in Go

Applied to files:

  • pkg/function/registry_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

Applied to files:

  • pkg/function/registry_test.go
📚 Learning: 2025-11-10T23:23:39.771Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: toolchain/registry/aqua/aqua_test.go:417-442
Timestamp: 2025-11-10T23:23:39.771Z
Learning: In Atmos toolchain AquaRegistry, tests should not hit real GitHub. Use the options pattern via WithGitHubBaseURL to inject an httptest server URL and make GetLatestVersion/GetAvailableVersions deterministic.

Applied to files:

  • pkg/function/registry_test.go
📚 Learning: 2025-11-11T03:47:59.576Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: toolchain/which_test.go:166-223
Timestamp: 2025-11-11T03:47:59.576Z
Learning: In the cloudposse/atmos repo, tests that manipulate environment variables should use testing.T.Setenv for automatic setup/teardown instead of os.Setenv/Unsetenv.

Applied to files:

  • pkg/function/registry_test.go
📚 Learning: 2025-11-11T03:47:45.878Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: toolchain/add_test.go:67-77
Timestamp: 2025-11-11T03:47:45.878Z
Learning: In the cloudposse/atmos codebase, tests should prefer t.Setenv for environment variable setup/teardown instead of os.Setenv/Unsetenv to ensure test-scoped isolation.

Applied to files:

  • pkg/function/registry_test.go
📚 Learning: 2024-12-02T21:26:32.337Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 808
File: pkg/config/config.go:478-483
Timestamp: 2024-12-02T21:26:32.337Z
Learning: In the 'atmos' project, when reviewing Go code like `pkg/config/config.go`, avoid suggesting file size checks after downloading remote configs if such checks aren't implemented elsewhere in the codebase.

Applied to files:

  • pkg/function/tags.go
  • pkg/yaml/errors.go
📚 Learning: 2025-12-16T18:20:55.614Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.614Z
Learning: New configs support Go templating with FuncMap() from internal/exec/template_funcs.go

Applied to files:

  • pkg/function/tags.go
  • pkg/function/template.go
📚 Learning: 2025-01-25T03:49:03.951Z
Learnt from: Listener430
Repo: cloudposse/atmos PR: 934
File: internal/exec/template_utils.go:268-271
Timestamp: 2025-01-25T03:49:03.951Z
Learning: The `ProcessTmplWithDatasourcesGomplate` function in `internal/exec/template_utils.go` is used for documentation generation purposes, where simple environment variable handling is acceptable and thread-safety concerns are not critical.

Applied to files:

  • pkg/function/template.go
📚 Learning: 2025-10-22T14:55:44.014Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1695
File: pkg/auth/manager.go:169-171
Timestamp: 2025-10-22T14:55:44.014Z
Learning: Go 1.20+ supports multiple %w verbs in fmt.Errorf, which returns an error implementing Unwrap() []error. This is valid and does not panic. Atmos uses Go 1.24.8 and configures errorlint with errorf-multi: true to validate this pattern.

Applied to files:

  • pkg/yaml/errors.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*.go : Provide clear error messages to users, include troubleshooting hints when appropriate, and log detailed errors for debugging

Applied to files:

  • pkg/yaml/errors.go
📚 Learning: 2024-12-12T15:17:45.245Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 808
File: examples/demo-atmos.d/atmos.d/tools/helmfile.yml:10-10
Timestamp: 2024-12-12T15:17:45.245Z
Learning: In `examples/demo-atmos.d/atmos.d/tools/helmfile.yml`, when suggesting changes to `kubeconfig_path`, ensure that the values use valid Go template syntax.

Applied to files:

  • pkg/yaml/errors.go
📚 Learning: 2025-12-13T03:21:35.786Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1813
File: cmd/terraform/shell.go:28-73
Timestamp: 2025-12-13T03:21:35.786Z
Learning: In Atmos, when calling cfg.InitCliConfig, you must first populate the schema.ConfigAndStacksInfo struct with global flag values using flags.ParseGlobalFlags(cmd, v) rather than passing an empty struct. The LoadConfig function (pkg/config/load.go) reads config selection fields (AtmosConfigFilesFromArg, AtmosConfigDirsFromArg, BasePath, ProfilesFromArg) directly from the ConfigAndStacksInfo struct, NOT from Viper. Passing an empty struct causes config selection flags (--base-path, --config, --config-path, --profile) to be silently ignored. Correct pattern: parse flags → populate struct → call InitCliConfig. See cmd/terraform/plan_diff.go for reference implementation.

Applied to files:

  • pkg/yaml/errors.go
📚 Learning: 2024-10-20T13:12:46.499Z
Learnt from: haitham911
Repo: cloudposse/atmos PR: 736
File: pkg/config/const.go:6-6
Timestamp: 2024-10-20T13:12:46.499Z
Learning: In `cmd/cmd_utils.go`, it's acceptable to have hardcoded references to `atmos.yaml` in logs, and it's not necessary to update them to use the `CliConfigFileName` constant.

Applied to files:

  • pkg/yaml/errors.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to {go.mod,go.sum} : Manage dependencies with Go modules and keep dependencies up to date while minimizing external dependencies

Applied to files:

  • pkg/function/resolution/context.go
  • .golangci.yml
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*.go : All code must pass golangci-lint checks

Applied to files:

  • .golangci.yml
🧬 Code graph analysis (18)
pkg/function/context.go (2)
pkg/schema/schema.go (2)
  • AtmosConfiguration (54-99)
  • ConfigAndStacksInfo (667-763)
pkg/perf/perf.go (1)
  • Track (121-138)
internal/terraform_backend/terraform_backend_s3.go (1)
pkg/aws/identity/identity.go (1)
  • LoadConfigWithAuth (251-317)
pkg/provisioner/backend/s3.go (1)
pkg/aws/identity/identity.go (1)
  • LoadConfigWithAuth (251-317)
pkg/function/git_root.go (7)
pkg/function/function.go (1)
  • BaseFunction (27-31)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/function/tags.go (1)
  • TagRepoRoot (39-39)
pkg/function/phase.go (1)
  • PreMerge (13-13)
pkg/function/resolution/context.go (1)
  • Context (23-26)
pkg/function/context.go (1)
  • ExecutionContext (10-28)
pkg/logger/log.go (1)
  • Debug (24-26)
pkg/aws/identity/identity_test.go (3)
pkg/aws/identity/identity.go (5)
  • CallerIdentity (21-26)
  • GetCallerIdentity (178-216)
  • ClearIdentityCache (166-172)
  • SetGetter (84-92)
  • GetCallerIdentityCached (124-162)
pkg/schema/schema.go (2)
  • AtmosConfiguration (54-99)
  • AWSAuthContext (629-644)
pkg/schema/schema_auth.go (1)
  • Account (75-78)
pkg/yaml/types.go (1)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/function/phase.go (1)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/yaml/position.go (2)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/utils/jsonpath.go (2)
  • AppendJSONPathKey (50-58)
  • AppendJSONPathIndex (65-71)
internal/exec/aws_getter.go (1)
pkg/aws/identity/identity.go (5)
  • CallerIdentity (21-26)
  • Getter (32-40)
  • SetGetter (84-92)
  • GetCallerIdentityCached (124-162)
  • ClearIdentityCache (166-172)
pkg/function/exec.go (9)
pkg/function/function.go (1)
  • BaseFunction (27-31)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/function/tags.go (2)
  • TagExec (12-12)
  • YAMLTag (108-112)
pkg/function/phase.go (1)
  • PreMerge (13-13)
pkg/function/resolution/context.go (1)
  • Context (23-26)
pkg/function/context.go (1)
  • ExecutionContext (10-28)
pkg/logger/log.go (1)
  • Debug (24-26)
pkg/function/errors.go (1)
  • ErrInvalidArguments (14-14)
pkg/utils/shell_utils.go (1)
  • ExecuteShellAndReturnOutput (33-62)
pkg/function/random.go (9)
pkg/function/function.go (1)
  • BaseFunction (27-31)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/function/tags.go (1)
  • TagRandom (42-42)
pkg/function/phase.go (1)
  • PreMerge (13-13)
pkg/function/resolution/context.go (1)
  • Context (23-26)
pkg/function/context.go (1)
  • ExecutionContext (10-28)
pkg/logger/log.go (1)
  • Debug (24-26)
pkg/utils/string_utils.go (1)
  • SplitStringByDelimiter (33-66)
pkg/function/errors.go (1)
  • ErrInvalidArguments (14-14)
pkg/function/template.go (6)
pkg/function/function.go (1)
  • BaseFunction (27-31)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/function/tags.go (1)
  • TagTemplate (21-21)
pkg/function/phase.go (1)
  • PreMerge (13-13)
pkg/function/context.go (1)
  • ExecutionContext (10-28)
pkg/logger/log.go (1)
  • Debug (24-26)
pkg/function/function.go (3)
pkg/function/phase.go (1)
  • Phase (8-8)
pkg/function/context.go (1)
  • ExecutionContext (10-28)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/aws/identity/identity.go (3)
pkg/schema/schema.go (2)
  • AtmosConfiguration (54-99)
  • AWSAuthContext (629-644)
pkg/logger/log.go (1)
  • Debug (24-26)
errors/errors.go (1)
  • ErrAwsGetCallerIdentity (95-95)
pkg/function/registry.go (4)
pkg/function/function.go (1)
  • Function (11-23)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/function/errors.go (2)
  • ErrFunctionAlreadyRegistered (11-11)
  • ErrFunctionNotFound (7-7)
pkg/function/phase.go (1)
  • Phase (8-8)
pkg/function/resolution/context.go (2)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/function/resolution/errors.go (1)
  • ErrCircularDependency (6-6)
pkg/function/literal.go (5)
pkg/function/function.go (1)
  • BaseFunction (27-31)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/function/tags.go (1)
  • TagLiteral (45-45)
pkg/function/phase.go (1)
  • PreMerge (13-13)
pkg/logger/log.go (1)
  • Debug (24-26)
pkg/function/aws.go (7)
pkg/function/context.go (1)
  • ExecutionContext (10-28)
pkg/aws/identity/identity.go (2)
  • CallerIdentity (21-26)
  • GetCallerIdentityCached (124-162)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/schema/schema.go (3)
  • AWSAuthContext (629-644)
  • AuthContext (615-625)
  • AtmosConfiguration (54-99)
pkg/function/tags.go (4)
  • TagAwsAccountID (48-48)
  • TagAwsCallerIdentityArn (51-51)
  • TagAwsCallerIdentityUserID (54-54)
  • TagAwsRegion (57-57)
pkg/function/phase.go (1)
  • PostMerge (17-17)
pkg/logger/log.go (1)
  • Debug (24-26)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Acceptance Tests (windows)
  • GitHub Check: Summary

@osterman osterman added the minor New features that do not break anything label Dec 18, 2025
@github-actions
Copy link

Warning

Changelog Entry Required

This PR is labeled minor or major but doesn't include a changelog entry.

Action needed: Add a new blog post in website/blog/ to announce this change.

Example filename: website/blog/2025-12-18-feature-name.mdx

Alternatively: If this change doesn't require a changelog entry, remove the minor or major label.

- resolution/context.go: Fix "false sharing" terminology to "metric collisions"
  and generate unique fallback IDs (unknown-N) using atomic counter to avoid
  collisions when multiple goroutines fail goroutine ID parsing

- store.go: Use strings.SplitN instead of strings.Fields to handle query
  values containing spaces (e.g., query ".foo .bar")

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 18, 2025
osterman and others added 2 commits December 18, 2025 00:10
Add tests for pkg/function, pkg/yaml, and pkg/aws/identity packages:
- pkg/function/store_test.go: Tests for store and store.get functions
- pkg/function/aws_test.go: Tests for AWS identity functions
- pkg/function/terraform_test.go: Tests for terraform.output/state
- pkg/function/template_test.go: Tests for template processing
- pkg/function/tags_test.go: Tests for tag constants and helpers
- pkg/function/context_test.go: Tests for ExecutionContext
- pkg/function/resolution/context_test.go: Tests for circular dependency detection
- pkg/yaml/position_test.go: Tests for YAML position extraction
- pkg/aws/identity/identity_test.go: Additional cache and concurrency tests

Also fix lint issues:
- Extract duplicate string constant in aws.go
- Remove unused nolint directive in git_root.go

Coverage improvements:
- pkg/function: 82.4%
- pkg/function/resolution: 97.2%
- pkg/yaml: 88.6%

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Test isolation fix:
Tests that redirect os.Stdout/os.Stderr were not properly restoring
the original streams when t.Fatalf() was called on failure. This
caused subsequent tests to have stdout/stderr pointing to closed
pipes, resulting in "stdout not writable" errors in CI.

The fix adds defer statements to ensure stdout/stderr are always
restored even if tests fail early via t.Fatalf().

Files fixed for test isolation:
- internal/exec/terraform_test.go (6 instances)
- internal/exec/packer_test.go (3 instances)
- internal/exec/terraform_utils_test.go (2 instances)
- internal/exec/workflow_adapters_test.go (3 instances)

Lint fixes:
- pkg/config/load.go: Use errors.As() instead of type switch for error checking
- pkg/function/env.go: Reduce cyclomatic complexity by extracting parseEnvArgs and lookupEnvFromContext helpers
- pkg/function/store.go: Reduce cyclomatic complexity by extracting retrieveFromStore helper
- pkg/function/template.go: Reduce cyclomatic complexity by extracting processTemplateString helper

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@mergify
Copy link

mergify bot commented Dec 19, 2025

💥 This pull request now has conflicts. Could you fix it @osterman? 🙏

@mergify mergify bot added the conflict This PR has conflicts label Dec 19, 2025
Resolve merge conflicts by combining main's concurrent pipe reading
(to avoid deadlock when output exceeds pipe buffer) with defer-based
cleanup (to ensure stdout/stderr restoration even on test failure).

Also update error handling in pkg/config/load.go to use errors.As()
consistently per errorlint requirements.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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: 2

🧹 Nitpick comments (11)
pkg/yaml/position_test.go (1)

11-235: Consider refactoring into table-driven tests.

Many of these tests follow an identical pattern: parse YAML, extract positions, assert on specific paths. Consolidating into a table-driven test would reduce duplication and improve maintainability.

Example structure
func TestExtractPositions_Scenarios(t *testing.T) {
	tests := []struct {
		name      string
		yaml      string
		enabled   bool
		wantPaths []string
	}{
		{
			name:      "disabled returns empty",
			yaml:      `key: value`,
			enabled:   false,
			wantPaths: []string{},
		},
		{
			name:      "scalar value",
			yaml:      `key: value`,
			enabled:   true,
			wantPaths: []string{"key"},
		},
		{
			name: "nested mapping",
			yaml: `
parent:
  child1: value1
  child2: value2`,
			enabled:   true,
			wantPaths: []string{"parent", "parent.child1", "parent.child2"},
		},
		// ... additional cases
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var node goyaml.Node
			err := goyaml.Unmarshal([]byte(tt.yaml), &node)
			require.NoError(t, err)

			positions := ExtractPositions(&node, tt.enabled)

			for _, path := range tt.wantPaths {
				assert.True(t, HasPosition(positions, path), "missing path: %s", path)
			}
		})
	}
}

Based on coding guidelines, table-driven tests are preferred for testing multiple scenarios.

pkg/function/git_root.go (4)

3-10: Add blank line between import groups.

Per coding guidelines, separate stdlib and Atmos package imports with a blank line.

🔎 Proposed fix
 import (
 	"context"
 	"os/exec"
 	"strings"
+
 	log "github.com/cloudposse/atmos/pkg/logger"
 	"github.com/cloudposse/atmos/pkg/perf"
 )

17-28: Add blank line after perf.Track defer.

Per coding guidelines, public functions should have a blank line after the perf.Track defer statement.

🔎 Proposed fix
 func NewGitRootFunction() *GitRootFunction {
 	defer perf.Track(nil, "function.NewGitRootFunction")()
+
 	return &GitRootFunction{

36-37: Add blank line after perf.Track defer.

Per coding guidelines, public functions should have a blank line after the perf.Track defer statement.

🔎 Proposed fix
 func (f *GitRootFunction) Execute(ctx context.Context, args string, execCtx *ExecutionContext) (any, error) {
 	defer perf.Track(nil, "function.GitRootFunction.Execute")()
+
 	log.Debug("Executing repo-root function")

36-36: Consider documenting the args parameter behavior.

The args parameter is not used in this function. Since !repo-root doesn't accept arguments, consider adding a comment explaining this or validating that args is empty to catch misuse early.

internal/exec/terraform_test.go (1)

305-337: Consider aligning with concurrent pipe read pattern.

Unlike other tests in this file, TestExecuteTerraform_TerraformWorkspace reads from the pipe synchronously after ExecuteTerraform completes (line 327). If the workspace command produces output exceeding the pipe buffer (~64KB), this could deadlock.

Other tests use a goroutine to read concurrently (see lines 88-94 for the pattern). Consider applying the same approach here for consistency and robustness.

🔎 Proposed refactor to add concurrent read
 // Create a pipe to capture stdout to check if terraform is executed correctly.
 oldStdout := os.Stdout
 r, w, _ := os.Pipe()
 os.Stdout = w

 // Ensure stdout is restored even if test fails.
 defer func() {
   os.Stdout = oldStdout
 }()

+// Read from pipe concurrently to avoid deadlock when output exceeds pipe buffer.
+var buf bytes.Buffer
+done := make(chan struct{})
+go func() {
+  _, _ = buf.ReadFrom(r)
+  close(done)
+}()
+
 err := ExecuteTerraform(info)

 // Close writer and restore stdout before checking error.
 w.Close()
 os.Stdout = oldStdout

+// Wait for the reader goroutine to finish.
+<-done
+
 if err != nil {
   t.Fatalf("Failed to execute 'ExecuteTerraform': %v", err)
 }

-// Read the captured output.
-var buf bytes.Buffer
-_, err = buf.ReadFrom(r)
-if err != nil {
-  t.Fatalf("Failed to read from pipe: %v", err)
-}
 output := buf.String()
pkg/function/resolution/context.go (2)

173-198: Error message contains duplicate sentinel text.

Line 177 writes the sentinel error to the builder, then line 197 wraps it again with %w. When the error is printed, the sentinel message appears twice.

Consider removing line 177 or restructuring to avoid redundancy.

🔎 Proposed fix
 func (ctx *Context) buildCircularDependencyError(newNode DependencyNode) error {
 	var builder strings.Builder

-	builder.WriteString(fmt.Sprintf("%s\n\n", errUtils.ErrCircularDependency))
-	builder.WriteString("Dependency chain:\n")
+	builder.WriteString("Dependency chain:\n")

 	// Show the full call stack.
 	for i, node := range ctx.CallStack {

84-97: Consider using LoadOrStore for cleaner atomicity.

The current Load-then-Store pattern is technically safe since goroutine IDs are unique, but LoadOrStore is more idiomatic for this pattern.

🔎 Proposed alternative
 func GetOrCreate() *Context {
 	defer perf.Track(nil, "resolution.GetOrCreate")()

 	gid := getGoroutineID()

-	if ctx, ok := goroutineContexts.Load(gid); ok {
-		return ctx.(*Context)
-	}
-
-	ctx := NewContext()
-	goroutineContexts.Store(gid, ctx)
-	return ctx
+	ctx, _ := goroutineContexts.LoadOrStore(gid, NewContext())
+	return ctx.(*Context)
 }

One caveat: this always allocates a new context even when one exists. If allocation is a concern, the current approach is fine.

pkg/function/env.go (1)

3-12: Add blank line between import groups.

Per coding guidelines, import groups should be separated by blank lines. Add a blank line between the stdlib imports (context, fmt, os, strings) and the Atmos package imports.

Proposed fix
 import (
 	"context"
 	"fmt"
 	"os"
 	"strings"
+
 	log "github.com/cloudposse/atmos/pkg/logger"
 	"github.com/cloudposse/atmos/pkg/perf"
 	"github.com/cloudposse/atmos/pkg/utils"
 )

Based on coding guidelines.

pkg/function/template.go (1)

3-10: Add blank line between import groups.

Import groups should be separated by blank lines per coding guidelines.

Proposed fix
 import (
 	"context"
 	"encoding/json"
 	"strings"
+
 	log "github.com/cloudposse/atmos/pkg/logger"
 	"github.com/cloudposse/atmos/pkg/perf"
 )

Based on coding guidelines.

pkg/function/store.go (1)

3-12: Add blank line between import groups.

Import groups should be separated by blank lines per coding guidelines.

Proposed fix
 import (
 	"context"
 	"fmt"
 	"strings"
+
 	log "github.com/cloudposse/atmos/pkg/logger"
 	"github.com/cloudposse/atmos/pkg/perf"
 	"github.com/cloudposse/atmos/pkg/store"
 	"github.com/cloudposse/atmos/pkg/utils"
 )

Based on coding guidelines.

📜 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 43e7c13 and f4db88d.

📒 Files selected for processing (21)
  • internal/exec/packer_test.go
  • internal/exec/terraform_test.go
  • internal/exec/terraform_utils_test.go
  • internal/exec/workflow_adapters_test.go
  • pkg/aws/identity/identity_test.go
  • pkg/config/load.go
  • pkg/function/aws.go
  • pkg/function/aws_test.go
  • pkg/function/context_test.go
  • pkg/function/env.go
  • pkg/function/git_root.go
  • pkg/function/resolution/context.go
  • pkg/function/resolution/context_test.go
  • pkg/function/store.go
  • pkg/function/store_test.go
  • pkg/function/tags_test.go
  • pkg/function/template.go
  • pkg/function/template_test.go
  • pkg/function/terraform_test.go
  • pkg/yaml/position_test.go
  • website/blog/2025-12-18-function-registry-package.mdx
✅ Files skipped from review due to trivial changes (2)
  • pkg/function/tags_test.go
  • website/blog/2025-12-18-function-registry-package.mdx
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/function/aws.go
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

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

**/*.go: Use Viper for managing configuration, environment variables, and flags in CLI commands
Use interfaces for external dependencies to facilitate mocking and consider using testify/mock for creating mock implementations
All code must pass golangci-lint checks
Follow Go's error handling idioms: use meaningful error messages, wrap errors with context using fmt.Errorf("context: %w", err), and consider using custom error types for domain-specific errors
Follow standard Go coding style: use gofmt and goimports to format code, prefer short descriptive variable names, use kebab-case for command-line flags, and snake_case for environment variables
Document all exported functions, types, and methods following Go's documentation conventions
Document complex logic with inline comments in Go code
Support configuration via files, environment variables, and flags following the precedence order: flags > environment variables > config file > defaults
Provide clear error messages to users, include troubleshooting hints when appropriate, and log detailed errors for debugging

**/*.go: NEVER use fmt.Fprintf(os.Stdout/Stderr) or fmt.Println(); use data.* or ui.* functions instead
All comments must end with periods (enforced by godot linter)
Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages; maintain aliases: cfg, log, u, errUtils
Add defer perf.Track(atmosConfig, "pkg.FuncName")() + blank line to all public functions for performance tracking; use nil if no atmosConfig param
All errors MUST be wrapped using static errors defined in errors/errors.go; use errors.Join for combining multiple errors; use fmt.Errorf with %w for adding string context; use error builder for complex errors; use errors.Is() for error checking; NEVER use dynamic errors directly
Use go.uber.org/mock/mockgen with //go:generate directives for mock generation; never create manual mocks
Keep files small...

Files:

  • pkg/function/git_root.go
  • internal/exec/terraform_test.go
  • pkg/function/resolution/context_test.go
  • pkg/function/store_test.go
  • pkg/aws/identity/identity_test.go
  • pkg/function/context_test.go
  • pkg/function/terraform_test.go
  • pkg/function/env.go
  • pkg/function/store.go
  • pkg/function/resolution/context.go
  • pkg/yaml/position_test.go
  • internal/exec/packer_test.go
  • pkg/function/template_test.go
  • pkg/function/aws_test.go
  • pkg/function/template.go
  • internal/exec/workflow_adapters_test.go
  • internal/exec/terraform_utils_test.go
  • pkg/config/load.go
**/*_test.go

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

**/*_test.go: Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages
Use table-driven tests for testing multiple scenarios in Go
Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

**/*_test.go: Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage
Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Files:

  • internal/exec/terraform_test.go
  • pkg/function/resolution/context_test.go
  • pkg/function/store_test.go
  • pkg/aws/identity/identity_test.go
  • pkg/function/context_test.go
  • pkg/function/terraform_test.go
  • pkg/yaml/position_test.go
  • internal/exec/packer_test.go
  • pkg/function/template_test.go
  • pkg/function/aws_test.go
  • internal/exec/workflow_adapters_test.go
  • internal/exec/terraform_utils_test.go
🧠 Learnings (70)
📓 Common learnings
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: errors/errors.go:184-203
Timestamp: 2025-12-13T06:10:13.688Z
Learning: cloudposse/atmos: For toolchain work, duplicate/unused error sentinels in errors/errors.go should be cleaned up in a separate refactor PR and not block feature PRs; canonical toolchain sentinels live under toolchain/registry with re-exports in toolchain/errors.go.
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Avoid utils package bloat; don't add new functions to pkg/utils/; create purpose-built packages instead (examples: pkg/store/, pkg/git/, pkg/pro/, pkg/filesystem/)
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1363
File: internal/exec/template_utils.go:18-18
Timestamp: 2025-07-05T20:59:02.914Z
Learning: In the Atmos project, gomplate v4 is imported with a blank import (`_ "github.com/hairyhenderson/gomplate/v4"`) alongside v3 imports to resolve AWS SDK version conflicts. V3 uses older AWS SDK versions that conflict with newer AWS modules used by Atmos. A full migration to v4 requires extensive refactoring due to API changes and should be handled in a separate PR.
Learnt from: osterman
Repo: cloudposse/atmos PR: 1891
File: internal/exec/describe_affected.go:468-468
Timestamp: 2025-12-21T04:10:29.030Z
Learning: In Go, package-level constants, variables, types, and functions declared in any file within a package are automatically accessible to all other files in the same package without imports or redeclaration. When reviewing Go code in the cloudposse/atmos repository (or any Go codebase), verify whether a seemingly undefined identifier exists in other files within the same package before suggesting to add it.
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*.go : Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages; maintain aliases: cfg, log, u, errUtils
📚 Learning: 2025-12-21T04:10:29.030Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1891
File: internal/exec/describe_affected.go:468-468
Timestamp: 2025-12-21T04:10:29.030Z
Learning: In Go, package-level declarations (constants, variables, types, and functions) are visible to all files in the same package without imports. During reviews in cloudposse/atmos (and similar Go codebases), before suggesting to declare a new identifier, first check if it already exists in another file of the same package. If it exists, you can avoid adding a new declaration; if not, proceed with a proper package-level declaration. 

Applied to files:

  • pkg/function/git_root.go
  • internal/exec/terraform_test.go
  • pkg/function/resolution/context_test.go
  • pkg/function/store_test.go
  • pkg/aws/identity/identity_test.go
  • pkg/function/context_test.go
  • pkg/function/terraform_test.go
  • pkg/function/env.go
  • pkg/function/store.go
  • pkg/function/resolution/context.go
  • pkg/yaml/position_test.go
  • internal/exec/packer_test.go
  • pkg/function/template_test.go
  • pkg/function/aws_test.go
  • pkg/function/template.go
  • internal/exec/workflow_adapters_test.go
  • internal/exec/terraform_utils_test.go
  • pkg/config/load.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

Applied to files:

  • internal/exec/terraform_test.go
  • pkg/function/resolution/context_test.go
  • pkg/function/store_test.go
  • pkg/function/context_test.go
  • pkg/function/terraform_test.go
  • internal/exec/packer_test.go
  • pkg/function/template_test.go
  • internal/exec/workflow_adapters_test.go
📚 Learning: 2024-10-31T19:25:41.298Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 727
File: internal/exec/terraform_clean.go:233-235
Timestamp: 2024-10-31T19:25:41.298Z
Learning: When specifying color values in functions like `confirmDeleteTerraformLocal` in `internal/exec/terraform_clean.go`, avoid hardcoding color values. Instead, use predefined color constants or allow customization through configuration settings to improve accessibility and user experience across different terminals and themes.

Applied to files:

  • internal/exec/terraform_test.go
  • internal/exec/terraform_utils_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Applied to files:

  • internal/exec/terraform_test.go
  • pkg/function/resolution/context_test.go
  • pkg/function/store_test.go
  • pkg/aws/identity/identity_test.go
  • pkg/function/terraform_test.go
  • internal/exec/packer_test.go
  • pkg/function/template_test.go
  • pkg/function/aws_test.go
  • internal/exec/workflow_adapters_test.go
  • internal/exec/terraform_utils_test.go
📚 Learning: 2024-11-02T15:35:09.958Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 759
File: internal/exec/terraform.go:366-368
Timestamp: 2024-11-02T15:35:09.958Z
Learning: In `internal/exec/terraform.go`, the workspace cleaning code under both the general execution path and within the `case "init":` block is intentionally duplicated because the code execution paths are different. The `.terraform/environment` file should be deleted before executing `terraform init` in both scenarios to ensure a clean state.

Applied to files:

  • internal/exec/terraform_test.go
  • internal/exec/terraform_utils_test.go
📚 Learning: 2025-12-10T18:32:51.237Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1808
File: cmd/terraform/backend/backend_delete_test.go:9-23
Timestamp: 2025-12-10T18:32:51.237Z
Learning: In cmd subpackages (e.g., cmd/terraform/backend/), tests cannot use cmd.NewTestKit(t) due to Go's test visibility rules (NewTestKit is in a parent package test file). These tests only need TestKit if they execute commands through RootCmd or modify RootCmd state. Structural tests that only verify command structure/flags without touching RootCmd don't require TestKit cleanup.

Applied to files:

  • internal/exec/terraform_test.go
  • pkg/function/context_test.go
  • pkg/function/terraform_test.go
  • internal/exec/packer_test.go
  • internal/exec/terraform_utils_test.go
📚 Learning: 2025-10-11T19:11:58.965Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1599
File: internal/exec/terraform.go:0-0
Timestamp: 2025-10-11T19:11:58.965Z
Learning: For terraform apply interactivity checks in Atmos (internal/exec/terraform.go), use stdin TTY detection (e.g., `IsTTYSupportForStdin()` or checking `os.Stdin`) to determine if user prompts are possible. This is distinct from stdout/stderr TTY checks used for output display (like TUI rendering). User input requires stdin to be a TTY; output display requires stdout/stderr to be a TTY.

Applied to files:

  • internal/exec/terraform_test.go
  • internal/exec/terraform_utils_test.go
📚 Learning: 2025-10-10T23:51:36.597Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1599
File: internal/exec/terraform.go:394-402
Timestamp: 2025-10-10T23:51:36.597Z
Learning: In Atmos (internal/exec/terraform.go), when adding OpenTofu-specific flags like `--var-file` for `init`, do not gate them based on command name (e.g., checking if `info.Command == "tofu"` or `info.Command == "opentofu"`) because command names don't reliably indicate the actual binary being executed (symlinks, aliases). Instead, document the OpenTofu requirement in code comments and documentation, trusting users who enable the feature (e.g., `PassVars`) to ensure their terraform command points to an OpenTofu binary.

Applied to files:

  • internal/exec/terraform_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Use table-driven tests for testing multiple scenarios in Go

Applied to files:

  • internal/exec/terraform_test.go
  • pkg/function/resolution/context_test.go
  • pkg/function/store_test.go
  • pkg/function/context_test.go
  • pkg/function/terraform_test.go
  • pkg/yaml/position_test.go
  • pkg/function/template_test.go
  • internal/exec/workflow_adapters_test.go
📚 Learning: 2025-12-13T06:10:25.156Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: internal/exec/workflow_utils.go:0-0
Timestamp: 2025-12-13T06:10:25.156Z
Learning: Atmos workflows: In internal/exec/workflow_utils.go ExecuteWorkflow, non-identity steps intentionally use baseWorkflowEnv, which is constructed from the parent environment with PATH modifications for the toolchain. Avoid appending os.Environ() again; prefer documenting this behavior and testing that standard environment variables are preserved.

Applied to files:

  • internal/exec/terraform_test.go
  • pkg/function/context_test.go
  • pkg/function/env.go
  • internal/exec/workflow_adapters_test.go
📚 Learning: 2025-01-09T22:37:01.004Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 914
File: cmd/terraform_commands.go:260-265
Timestamp: 2025-01-09T22:37:01.004Z
Learning: In the terraform commands implementation (cmd/terraform_commands.go), the direct use of `os.Args[2:]` for argument handling is intentionally preserved to avoid extensive refactoring. While it could be improved to use cobra's argument parsing, such changes should be handled in a dedicated PR to maintain focus and minimize risk.

Applied to files:

  • internal/exec/terraform_test.go
  • pkg/function/store.go
📚 Learning: 2024-11-12T03:16:02.910Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 775
File: internal/exec/template_funcs_component.go:157-159
Timestamp: 2024-11-12T03:16:02.910Z
Learning: In the Go code for `componentFunc` in `internal/exec/template_funcs_component.go`, the function `cleanTerraformWorkspace` does not return errors, and it's acceptable if the file does not exist. Therefore, error handling for `cleanTerraformWorkspace` is not needed.

Applied to files:

  • internal/exec/terraform_test.go
  • pkg/function/terraform_test.go
  • internal/exec/terraform_utils_test.go
📚 Learning: 2024-11-24T19:13:10.287Z
Learnt from: haitham911
Repo: cloudposse/atmos PR: 727
File: internal/exec/terraform_clean.go:407-416
Timestamp: 2024-11-24T19:13:10.287Z
Learning: In `internal/exec/terraform_clean.go`, when `getStackTerraformStateFolder` returns an error in the `handleCleanSubCommand` function, the error is logged, and the process continues without returning the error.

Applied to files:

  • internal/exec/terraform_test.go
  • internal/exec/terraform_utils_test.go
📚 Learning: 2024-10-23T21:36:40.262Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 740
File: cmd/cmd_utils.go:340-359
Timestamp: 2024-10-23T21:36:40.262Z
Learning: In the Go codebase for Atmos, when reviewing functions like `checkAtmosConfig` in `cmd/cmd_utils.go`, avoid suggesting refactoring to return errors instead of calling `os.Exit` if such changes would significantly increase the scope due to the need to update multiple call sites.

Applied to files:

  • internal/exec/terraform_test.go
  • pkg/config/load.go
📚 Learning: 2025-10-03T18:02:08.535Z
Learnt from: Benbentwo
Repo: cloudposse/atmos PR: 1475
File: internal/exec/terraform.go:269-272
Timestamp: 2025-10-03T18:02:08.535Z
Learning: In internal/exec/terraform.go, when auth.TerraformPreHook fails, the error is logged but execution continues. This is a deliberate design choice to allow Terraform commands to proceed even if authentication setup fails, rather than failing fast.

Applied to files:

  • internal/exec/terraform_test.go
  • internal/exec/terraform_utils_test.go
📚 Learning: 2025-12-13T04:37:25.223Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: cmd/root.go:0-0
Timestamp: 2025-12-13T04:37:25.223Z
Learning: In Atmos cmd/root.go Execute(), after cfg.InitCliConfig, we must call both toolchainCmd.SetAtmosConfig(&atmosConfig) and toolchain.SetAtmosConfig(&atmosConfig) so the CLI wrapper and the toolchain package receive configuration; missing either can cause nil-pointer panics in toolchain path resolution.

Applied to files:

  • internal/exec/terraform_test.go
  • pkg/config/load.go
📚 Learning: 2024-10-31T19:23:45.538Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 727
File: internal/exec/terraform.go:65-66
Timestamp: 2024-10-31T19:23:45.538Z
Learning: The variable `shouldCheckStack` in `ExecuteTerraform` controls whether validation is performed.

Applied to files:

  • internal/exec/terraform_test.go
  • internal/exec/terraform_utils_test.go
📚 Learning: 2024-11-30T22:07:08.610Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 810
File: internal/exec/yaml_func_terraform_output.go:35-40
Timestamp: 2024-11-30T22:07:08.610Z
Learning: In the Go function `processTagTerraformOutput` in `internal/exec/yaml_func_terraform_output.go`, parameters cannot contain spaces. The code splits the input by spaces, and if the parameters contain spaces, `len(parts) != 3` will fail and show an error to the user.

Applied to files:

  • internal/exec/terraform_test.go
  • pkg/function/terraform_test.go
  • pkg/function/store.go
  • pkg/function/template_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*.go : UI (prompts, status) outputs go to stderr; data outputs go to stdout; logging is for system events only; never use logging for UI

Applied to files:

  • internal/exec/terraform_test.go
  • internal/exec/workflow_adapters_test.go
📚 Learning: 2024-11-10T19:28:17.365Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 768
File: internal/exec/vendor_utils.go:0-0
Timestamp: 2024-11-10T19:28:17.365Z
Learning: When TTY is not supported, log the downgrade message at the Warn level using `u.LogWarning(cliConfig, ...)` instead of `fmt.Println`.

Applied to files:

  • internal/exec/terraform_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages

Applied to files:

  • pkg/function/resolution/context_test.go
  • pkg/function/store_test.go
  • pkg/aws/identity/identity_test.go
  • pkg/function/terraform_test.go
  • pkg/yaml/position_test.go
  • pkg/function/template_test.go
📚 Learning: 2025-10-13T18:13:54.020Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1622
File: pkg/perf/perf.go:140-184
Timestamp: 2025-10-13T18:13:54.020Z
Learning: In pkg/perf/perf.go, the `trackWithSimpleStack` function intentionally skips ownership checks at call stack depth > 1 to avoid expensive `getGoroutineID()` calls on every nested function. This is a performance optimization for the common single-goroutine execution case (most Atmos commands), accepting the rare edge case of potential metric corruption if multi-goroutine execution occurs at depth > 1. The ~19× performance improvement justifies this trade-off.

Applied to files:

  • pkg/function/resolution/context_test.go
  • pkg/function/resolution/context.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Use context.Context ONLY for cancellation signals, deadlines/timeouts, and request-scoped values (trace IDs, request IDs); context MUST be the first parameter in functions that accept it. DO NOT use context for passing configuration, dependencies, or avoiding proper function parameters

Applied to files:

  • pkg/function/resolution/context_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage

Applied to files:

  • pkg/function/store_test.go
  • pkg/aws/identity/identity_test.go
  • pkg/function/aws_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Define interfaces for all major functionality and use dependency injection for testability; generate mocks with go.uber.org/mock/mockgen

Applied to files:

  • pkg/function/store_test.go
  • pkg/aws/identity/identity_test.go
  • pkg/function/aws_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*.go : Use interfaces for external dependencies to facilitate mocking and consider using testify/mock for creating mock implementations

Applied to files:

  • pkg/function/store_test.go
  • pkg/aws/identity/identity_test.go
  • pkg/function/aws_test.go
📚 Learning: 2025-05-23T19:51:47.091Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1255
File: cmd/describe_affected_test.go:15-15
Timestamp: 2025-05-23T19:51:47.091Z
Learning: The atmos codebase has a custom extension to *testing.T that provides a Chdir method, allowing test functions to call t.Chdir() to change working directories during tests. This is used consistently across test files in the codebase.

Applied to files:

  • pkg/function/context_test.go
📚 Learning: 2025-11-11T03:47:59.576Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: toolchain/which_test.go:166-223
Timestamp: 2025-11-11T03:47:59.576Z
Learning: In the cloudposse/atmos repo, tests that manipulate environment variables should use testing.T.Setenv for automatic setup/teardown instead of os.Setenv/Unsetenv.

Applied to files:

  • pkg/function/context_test.go
  • pkg/function/terraform_test.go
📚 Learning: 2025-11-11T03:47:45.878Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: toolchain/add_test.go:67-77
Timestamp: 2025-11-11T03:47:45.878Z
Learning: In the cloudposse/atmos codebase, tests should prefer t.Setenv for environment variable setup/teardown instead of os.Setenv/Unsetenv to ensure test-scoped isolation.

Applied to files:

  • pkg/function/context_test.go
  • pkg/function/aws_test.go
  • internal/exec/terraform_utils_test.go
📚 Learning: 2025-11-08T19:56:18.660Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1697
File: internal/exec/oci_utils.go:0-0
Timestamp: 2025-11-08T19:56:18.660Z
Learning: In the Atmos codebase, when a function receives an `*schema.AtmosConfiguration` parameter, it should read configuration values from `atmosConfig.Settings` fields rather than using direct `os.Getenv()` or `viper.GetString()` calls. The Atmos pattern is: viper.BindEnv in cmd/root.go binds environment variables → Viper unmarshals into atmosConfig.Settings via mapstructure → business logic reads from the Settings struct. This provides centralized config management, respects precedence, and enables testability. Example: `atmosConfig.Settings.AtmosGithubToken` instead of `os.Getenv("ATMOS_GITHUB_TOKEN")` in functions like `getGHCRAuth` in internal/exec/oci_utils.go.

Applied to files:

  • pkg/function/context_test.go
  • pkg/config/load.go
📚 Learning: 2025-08-15T14:43:41.030Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1352
File: pkg/store/artifactory_store_test.go:108-113
Timestamp: 2025-08-15T14:43:41.030Z
Learning: In test files for the atmos project, it's acceptable to ignore errors from os.Setenv/Unsetenv operations during test environment setup and teardown, as these are controlled test scenarios.

Applied to files:

  • pkg/function/context_test.go
  • internal/exec/terraform_utils_test.go
📚 Learning: 2025-05-23T19:51:47.091Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1255
File: cmd/describe_affected_test.go:15-15
Timestamp: 2025-05-23T19:51:47.091Z
Learning: In the atmos codebase, t.Chdir() is a valid method call on *testing.T objects and works correctly for changing directories in tests. This is implemented through custom testing framework extensions and is used consistently throughout the test suite.

Applied to files:

  • pkg/function/context_test.go
📚 Learning: 2025-04-11T22:06:46.999Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1147
File: internal/exec/validate_schema.go:42-57
Timestamp: 2025-04-11T22:06:46.999Z
Learning: The "ExecuteAtmosValidateSchemaCmd" function in internal/exec/validate_schema.go has been reviewed and confirmed to have acceptable cognitive complexity despite static analysis warnings. The function uses a clean structure with only three if statements for error handling and delegates complex operations to helper methods.

Applied to files:

  • pkg/function/context_test.go
📚 Learning: 2025-05-23T19:51:47.091Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1255
File: cmd/describe_affected_test.go:15-15
Timestamp: 2025-05-23T19:51:47.091Z
Learning: In the atmos codebase, t.Chdir() is a valid method that can be called on *testing.T objects. This functionality is implemented through custom testing framework extensions and is used consistently throughout the test suite for changing working directories during tests.

Applied to files:

  • pkg/function/context_test.go
📚 Learning: 2024-12-03T05:18:49.169Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 810
File: internal/exec/terraform_utils.go:40-213
Timestamp: 2024-12-03T05:18:49.169Z
Learning: In the context of the Atmos project, it's acceptable for functions like `execTerraformOutput` to remain as single functions if they perform a single purpose, such as retrieving Terraform outputs for a component in a stack, even if the function is lengthy.

Applied to files:

  • pkg/function/terraform_test.go
📚 Learning: 2024-12-01T00:33:20.298Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 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:

  • pkg/function/terraform_test.go
📚 Learning: 2024-12-17T07:08:41.288Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 863
File: internal/exec/yaml_func_terraform_output.go:34-38
Timestamp: 2024-12-17T07:08:41.288Z
Learning: In the `processTagTerraformOutput` function within `internal/exec/yaml_func_terraform_output.go`, parameters are separated by spaces and do not contain spaces. Therefore, using `strings.Fields()` for parsing is acceptable, and there's no need to handle parameters with spaces.

Applied to files:

  • pkg/function/terraform_test.go
  • pkg/function/store.go
  • pkg/function/template_test.go
📚 Learning: 2025-12-13T03:21:35.786Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1813
File: cmd/terraform/shell.go:28-73
Timestamp: 2025-12-13T03:21:35.786Z
Learning: In Atmos, when calling cfg.InitCliConfig, you must first populate the schema.ConfigAndStacksInfo struct with global flag values using flags.ParseGlobalFlags(cmd, v) rather than passing an empty struct. The LoadConfig function (pkg/config/load.go) reads config selection fields (AtmosConfigFilesFromArg, AtmosConfigDirsFromArg, BasePath, ProfilesFromArg) directly from the ConfigAndStacksInfo struct, NOT from Viper. Passing an empty struct causes config selection flags (--base-path, --config, --config-path, --profile) to be silently ignored. Correct pattern: parse flags → populate struct → call InitCliConfig. See cmd/terraform/plan_diff.go for reference implementation.

Applied to files:

  • pkg/function/terraform_test.go
  • pkg/config/load.go
📚 Learning: 2025-09-30T19:03:50.738Z
Learnt from: Cerebrovinny
Repo: cloudposse/atmos PR: 1560
File: pkg/utils/string_utils.go:43-64
Timestamp: 2025-09-30T19:03:50.738Z
Learning: In the Atmos codebase, YAML tags like !terraform.output rely on positional arguments, so the SplitStringByDelimiter function in pkg/utils/string_utils.go must preserve empty strings (even after trimming quotes) to maintain the correct number of positional arguments. Filtering out empty values after trimming would collapse the array and break these function calls.

Applied to files:

  • pkg/function/terraform_test.go
  • pkg/function/store.go
  • pkg/function/template_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*.go : Add `defer perf.Track(atmosConfig, "pkg.FuncName")()` + blank line to all public functions for performance tracking; use nil if no atmosConfig param

Applied to files:

  • pkg/function/env.go
  • pkg/function/store.go
📚 Learning: 2025-01-19T22:30:27.600Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 0
File: :0-0
Timestamp: 2025-01-19T22:30:27.600Z
Learning: The Atmos YAML function `!env` is used to retrieve environment variables and assign them to sections in stack manifests. It supports both simple types (string, number, boolean) and complex types (JSON-encoded lists, maps, objects).

Applied to files:

  • pkg/function/env.go
📚 Learning: 2025-02-04T22:45:15.845Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 0
File: :0-0
Timestamp: 2025-02-04T22:45:15.845Z
Learning: When validating URLs in Go, use the standard `url.Parse` function instead of character-based validation to properly handle URL-safe characters and query parameters.

Applied to files:

  • pkg/function/store.go
📚 Learning: 2025-04-04T02:03:23.676Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1185
File: internal/exec/yaml_func_store.go:26-26
Timestamp: 2025-04-04T02:03:23.676Z
Learning: The Atmos codebase currently uses `log.Fatal` for error handling in multiple places. The maintainers are aware this isn't an ideal pattern (should only be used in main() or init() functions) and plan to address it comprehensively in a separate PR. CodeRabbit should not flag these issues or push for immediate changes until that refactoring is complete.

Applied to files:

  • pkg/function/resolution/context.go
📚 Learning: 2024-11-13T21:37:07.852Z
Learnt from: Cerebrovinny
Repo: cloudposse/atmos PR: 764
File: internal/exec/describe_stacks.go:289-295
Timestamp: 2024-11-13T21:37:07.852Z
Learning: In the `internal/exec/describe_stacks.go` file of the `atmos` project written in Go, avoid extracting the stack name handling logic into a helper function within the `ExecuteDescribeStacks` method, even if the logic appears duplicated.

Applied to files:

  • pkg/function/resolution/context.go
📚 Learning: 2025-10-22T14:55:44.014Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1695
File: pkg/auth/manager.go:169-171
Timestamp: 2025-10-22T14:55:44.014Z
Learning: Go 1.20+ supports multiple %w verbs in fmt.Errorf, which returns an error implementing Unwrap() []error. This is valid and does not panic. Atmos uses Go 1.24.8 and configures errorlint with errorf-multi: true to validate this pattern.

Applied to files:

  • pkg/function/resolution/context.go
📚 Learning: 2025-10-16T05:26:31.892Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1639
File: pkg/merge/merge.go:392-430
Timestamp: 2025-10-16T05:26:31.892Z
Learning: In Atmos merge operations (pkg/merge/merge.go), context.Positions is a PositionMap (map[string]*schema.Position) keyed by JSONPath strings, not a slice indexed by input array position. Position lookups are path-based, making them independent of input array reorganization or filtering. This design allows filtering empty inputs without affecting provenance tracking correctness.

Applied to files:

  • pkg/yaml/position_test.go
📚 Learning: 2025-04-24T01:06:15.259Z
Learnt from: haitham911
Repo: cloudposse/atmos PR: 1202
File: pkg/config/process_yaml.go:0-0
Timestamp: 2025-04-24T01:06:15.259Z
Learning: When processing YAML structures with gopkg.in/yaml.v3, the Value field of a Node is only meant for scalar values. Complex data types like maps and arrays are represented through child nodes in the YAML tree structure.

Applied to files:

  • pkg/yaml/position_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: New configs support Go templating with FuncMap() from internal/exec/template_funcs.go

Applied to files:

  • pkg/function/template_test.go
  • pkg/function/template.go
📚 Learning: 2025-01-25T03:49:03.951Z
Learnt from: Listener430
Repo: cloudposse/atmos PR: 934
File: internal/exec/template_utils.go:268-271
Timestamp: 2025-01-25T03:49:03.951Z
Learning: The `ProcessTmplWithDatasourcesGomplate` function in `internal/exec/template_utils.go` is used for documentation generation purposes, where simple environment variable handling is acceptable and thread-safety concerns are not critical.

Applied to files:

  • pkg/function/template_test.go
  • pkg/function/template.go
📚 Learning: 2025-04-24T01:40:13.576Z
Learnt from: haitham911
Repo: cloudposse/atmos PR: 1202
File: pkg/config/process_yaml.go:0-0
Timestamp: 2025-04-24T01:40:13.576Z
Learning: When processing YAML nodes with custom directives in Go using gopkg.in/yaml.v3, setting node.Tag = "" is sufficient to prevent re-processing of the node. It's not necessary to also clear node.Value after updating the configuration store (e.g., Viper), as the original value doesn't affect subsequent operations once the tag is removed.

Applied to files:

  • pkg/function/template_test.go
📚 Learning: 2025-06-02T14:12:02.710Z
Learnt from: milldr
Repo: cloudposse/atmos PR: 1229
File: internal/exec/workflow_test.go:0-0
Timestamp: 2025-06-02T14:12:02.710Z
Learning: In the atmos codebase, workflow error handling was refactored to use `PrintErrorMarkdown` followed by returning specific error variables (like `ErrWorkflowNoSteps`, `ErrInvalidFromStep`, `ErrInvalidWorkflowStepType`, `ErrWorkflowStepFailed`) instead of `PrintErrorMarkdownAndExit`. This pattern allows proper error testing without the function terminating the process with `os.Exit`, enabling unit tests to assert on error conditions while maintaining excellent user-facing error formatting.

Applied to files:

  • internal/exec/workflow_adapters_test.go
📚 Learning: 2025-06-02T14:12:02.710Z
Learnt from: milldr
Repo: cloudposse/atmos PR: 1229
File: internal/exec/workflow_test.go:0-0
Timestamp: 2025-06-02T14:12:02.710Z
Learning: In the atmos codebase, workflow error handling was refactored to use `PrintErrorMarkdown` followed by returning the error instead of `PrintErrorMarkdownAndExit`. This pattern allows proper error testing without the function terminating the process with `os.Exit`, enabling unit tests to assert on error conditions.

Applied to files:

  • internal/exec/workflow_adapters_test.go
  • internal/exec/terraform_utils_test.go
📚 Learning: 2025-04-26T15:54:10.506Z
Learnt from: haitham911
Repo: cloudposse/atmos PR: 1195
File: internal/exec/terraform_clean.go:99-99
Timestamp: 2025-04-26T15:54:10.506Z
Learning: The error variable `ErrRelPath` is defined in `internal/exec/terraform_clean_util.go` and is used across files in the `exec` package, including in `terraform_clean.go`. This is part of an approach to standardize error handling in the codebase.

Applied to files:

  • internal/exec/terraform_utils_test.go
📚 Learning: 2024-10-20T00:41:57.135Z
Learnt from: haitham911
Repo: cloudposse/atmos PR: 731
File: internal/exec/validate_stacks.go:93-98
Timestamp: 2024-10-20T00:41:57.135Z
Learning: When downloading schema files in `internal/exec/validate_stacks.go`, use a consistent temporary file name to overwrite the file each time and avoid creating multiple temporary files.

Applied to files:

  • internal/exec/terraform_utils_test.go
📚 Learning: 2024-12-07T16:19:01.683Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 825
File: internal/exec/terraform.go:30-30
Timestamp: 2024-12-07T16:19:01.683Z
Learning: In `internal/exec/terraform.go`, skipping stack validation when help flags are present is not necessary.

Applied to files:

  • internal/exec/terraform_utils_test.go
📚 Learning: 2025-06-23T02:14:30.937Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1327
File: cmd/terraform.go:111-117
Timestamp: 2025-06-23T02:14:30.937Z
Learning: In cmd/terraform.go, flags for the DescribeAffected function are added dynamically at runtime when info.Affected is true. This is intentional to avoid exposing internal flags like "file", "format", "verbose", "include-spacelift-admin-stacks", "include-settings", and "upload" in the terraform command interface, while still providing them for the shared DescribeAffected function used by both `atmos describe affected` and `atmos terraform apply --affected`.

Applied to files:

  • internal/exec/terraform_utils_test.go
📚 Learning: 2025-04-10T20:48:22.687Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1147
File: pkg/config/load.go:0-0
Timestamp: 2025-04-10T20:48:22.687Z
Learning: In the `bindEnv` function in `pkg/config/load.go`, panic is used deliberately instead of returning errors because errors from `BindEnv` would only occur due to developer mistakes. Using panic helps with early detection of these developer errors during initialization.

Applied to files:

  • pkg/config/load.go
📚 Learning: 2024-10-20T13:06:20.839Z
Learnt from: haitham911
Repo: cloudposse/atmos PR: 736
File: pkg/utils/file_utils.go:177-194
Timestamp: 2024-10-20T13:06:20.839Z
Learning: In `pkg/utils/file_utils.go`, the `SearchConfigFile` function should focus on returning the file path and an `exists` boolean without returning an error.

Applied to files:

  • pkg/config/load.go
📚 Learning: 2024-12-02T21:26:32.337Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 808
File: pkg/config/config.go:478-483
Timestamp: 2024-12-02T21:26:32.337Z
Learning: In the 'atmos' project, when reviewing Go code like `pkg/config/config.go`, avoid suggesting file size checks after downloading remote configs if such checks aren't implemented elsewhere in the codebase.

Applied to files:

  • pkg/config/load.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*.go : Provide clear error messages to users, include troubleshooting hints when appropriate, and log detailed errors for debugging

Applied to files:

  • pkg/config/load.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*.go : Support configuration via files, environment variables, and flags following the precedence order: flags > environment variables > config file > defaults

Applied to files:

  • pkg/config/load.go
📚 Learning: 2025-08-29T20:57:35.423Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1433
File: cmd/theme_list.go:33-36
Timestamp: 2025-08-29T20:57:35.423Z
Learning: In the Atmos codebase, avoid using viper.SetEnvPrefix("ATMOS") with viper.AutomaticEnv() because canonical environment variable names are not exclusive to Atmos and could cause conflicts. Instead, use selective environment variable binding through the setEnv function in pkg/config/load.go with bindEnv(v, "config.key", "ENV_VAR_NAME") for specific environment variables.

Applied to files:

  • pkg/config/load.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*.go : Follow Go's error handling idioms: use meaningful error messages, wrap errors with context using `fmt.Errorf("context: %w", err)`, and consider using custom error types for domain-specific errors

Applied to files:

  • pkg/config/load.go
📚 Learning: 2024-10-28T01:51:30.811Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 727
File: internal/exec/terraform_clean.go:329-332
Timestamp: 2024-10-28T01:51:30.811Z
Learning: In the Atmos Go code, when deleting directories or handling file paths (e.g., in `terraform_clean.go`), always resolve the absolute path using `filepath.Abs` and use the logger `u.LogWarning` for logging messages instead of using `fmt.Printf`.

Applied to files:

  • pkg/config/load.go
📚 Learning: 2024-12-11T18:40:12.808Z
Learnt from: Listener430
Repo: cloudposse/atmos PR: 844
File: cmd/helmfile.go:37-37
Timestamp: 2024-12-11T18:40:12.808Z
Learning: In the atmos project, `cliConfig` is initialized within the `cmd` package in `root.go` and can be used in other command files.

Applied to files:

  • pkg/config/load.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*.go : Use ATMOS_ prefix for environment variables with viper.BindEnv("ATMOS_VAR", "ATMOS_VAR", "FALLBACK")

Applied to files:

  • pkg/config/load.go
📚 Learning: 2025-04-23T15:02:50.246Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1202
File: pkg/utils/yaml_func_exec.go:104-104
Timestamp: 2025-04-23T15:02:50.246Z
Learning: In the Atmos codebase, direct calls to `os.Getenv` should be avoided. Instead, use `viper.BindEnv` for environment variable access. This provides a consistent approach to configuration management across the codebase.

Applied to files:

  • pkg/config/load.go
📚 Learning: 2024-10-20T13:12:46.499Z
Learnt from: haitham911
Repo: cloudposse/atmos PR: 736
File: pkg/config/const.go:6-6
Timestamp: 2024-10-20T13:12:46.499Z
Learning: In `cmd/cmd_utils.go`, it's acceptable to have hardcoded references to `atmos.yaml` in logs, and it's not necessary to update them to use the `CliConfigFileName` constant.

Applied to files:

  • pkg/config/load.go
📚 Learning: 2025-09-23T04:43:31.857Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1430
File: pkg/config/config.go:98-107
Timestamp: 2025-09-23T04:43:31.857Z
Learning: In the Atmos codebase, NO_PAGER environment variable handling is intentionally kept as direct os.Getenv() access in pkg/config/config.go rather than using Viper binding, because adding no_pager to the config file would be confusing for users. This is an acknowledged exception to the normal Viper binding pattern for environment variables.

Applied to files:

  • pkg/config/load.go
🧬 Code graph analysis (8)
internal/exec/terraform_test.go (1)
internal/exec/terraform.go (1)
  • ExecuteTerraform (45-675)
pkg/function/resolution/context_test.go (2)
pkg/perf/perf.go (1)
  • CallStack (53-56)
pkg/function/resolution/context.go (3)
  • Clear (100-105)
  • GetOrCreate (85-97)
  • Scoped (113-136)
pkg/yaml/position_test.go (1)
pkg/yaml/position.go (3)
  • ExtractPositions (21-31)
  • HasPosition (125-134)
  • GetPosition (109-122)
pkg/function/template_test.go (3)
pkg/function/template.go (1)
  • NewTemplateFunction (18-28)
pkg/function/tags.go (2)
  • TagTemplate (21-21)
  • YAMLTag (108-112)
pkg/function/phase.go (2)
  • PreMerge (13-13)
  • Phase (8-8)
pkg/function/aws_test.go (3)
pkg/aws/identity/identity.go (4)
  • CallerIdentity (21-26)
  • GetCallerIdentity (178-216)
  • ClearIdentityCache (166-172)
  • SetGetter (84-92)
pkg/schema/schema.go (3)
  • AtmosConfiguration (54-99)
  • ConfigAndStacksInfo (667-763)
  • AuthContext (615-625)
pkg/function/aws.go (4)
  • NewAwsAccountIDFunction (42-52)
  • NewAwsCallerIdentityArnFunction (79-89)
  • NewAwsCallerIdentityUserIDFunction (116-126)
  • NewAwsRegionFunction (153-163)
internal/exec/workflow_adapters_test.go (1)
pkg/terminal/terminal.go (1)
  • Stderr (85-85)
internal/exec/terraform_utils_test.go (1)
tests/preconditions.go (1)
  • RequireExecutable (322-334)
pkg/config/load.go (1)
pkg/logger/log.go (1)
  • Debug (24-26)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Acceptance Tests (windows)
  • GitHub Check: Summary

@aknysh
Copy link
Member

aknysh commented Dec 22, 2025

@coderabbitai full review please

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 22, 2025

@aknysh: I'll perform a full review of the PR changes now.


🧠 Learnings used
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: errors/errors.go:184-203
Timestamp: 2025-12-13T06:10:13.688Z
Learning: cloudposse/atmos: For toolchain work, duplicate/unused error sentinels in errors/errors.go should be cleaned up in a separate refactor PR and not block feature PRs; canonical toolchain sentinels live under toolchain/registry with re-exports in toolchain/errors.go.
✅ Actions performed

Full review triggered.

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: 5

🧹 Nitpick comments (13)
pkg/function/include_test.go (1)

11-31: Consider using table-driven tests to reduce duplication.

The Execute tests for Include and IncludeRaw follow nearly identical patterns. Per coding guidelines, table-driven tests are preferred for testing multiple scenarios.

🔎 Proposed table-driven test refactor
-func TestIncludeFunction_Execute(t *testing.T) {
-	fn := NewIncludeFunction()
-
-	// Include function always returns ErrSpecialYAMLHandling.
-	result, err := fn.Execute(context.Background(), "path/to/file.yaml", nil)
-	require.Error(t, err)
-	assert.Nil(t, result)
-	assert.ErrorIs(t, err, ErrSpecialYAMLHandling)
-	assert.Contains(t, err.Error(), "include")
-}
-
-func TestIncludeRawFunction_Execute(t *testing.T) {
-	fn := NewIncludeRawFunction()
-
-	// Include.raw function always returns ErrSpecialYAMLHandling.
-	result, err := fn.Execute(context.Background(), "path/to/file.txt", nil)
-	require.Error(t, err)
-	assert.Nil(t, result)
-	assert.ErrorIs(t, err, ErrSpecialYAMLHandling)
-	assert.Contains(t, err.Error(), "include.raw")
-}
+func TestIncludeFunction_Execute(t *testing.T) {
+	tests := []struct {
+		name           string
+		fn             Function
+		input          string
+		expectedSubstr string
+	}{
+		{
+			name:           "include",
+			fn:             NewIncludeFunction(),
+			input:          "path/to/file.yaml",
+			expectedSubstr: "include",
+		},
+		{
+			name:           "include.raw",
+			fn:             NewIncludeRawFunction(),
+			input:          "path/to/file.txt",
+			expectedSubstr: "include.raw",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			result, err := tt.fn.Execute(context.Background(), tt.input, nil)
+			require.Error(t, err)
+			assert.Nil(t, result)
+			assert.ErrorIs(t, err, ErrSpecialYAMLHandling)
+			assert.Contains(t, err.Error(), tt.expectedSubstr)
+		})
+	}
+}
pkg/function/phase_test.go (1)

40-44: Consider whether testing exact constant values adds value.

This test verifies the specific numeric values of iota constants. Unless these values are part of a stable API contract (e.g., for serialization or protocol compatibility), testing that PreMerge == Phase(0) is tautological—it just confirms what the code defines.

If ordering matters more than exact values, you could test PreMerge < PostMerge instead. Otherwise, this test could be removed.

pkg/yaml/types_test.go (2)

70-73: Remove tautological constant test.

Testing that a constant equals its declared value doesn't add meaningful coverage. Constants can't change at runtime, so this test only verifies Go's const mechanism.

Based on coding guidelines that state "avoid tautological tests."


75-85: Remove tautological struct field test.

Testing basic struct field assignment (setting Indent: 4 and verifying it equals 4) doesn't validate behavior. This would only be valuable if Options had validation logic, computed properties, or methods to test.

Based on coding guidelines that state "avoid tautological tests."

pkg/function/git_root_test.go (2)

13-33: Consider adding conditional skip for non-git environments.

This integration test assumes execution within a git repository, making it fragile and environment-dependent. If the test runs outside a git context (e.g., in certain CI environments or when testing the package in isolation), it will fail.

Consider adding t.Skip() when not in a git repo, or refactor to use dependency injection with a mockable git interface for true unit testing.

🔎 Example with conditional skip
 func TestGitRootFunction_Execute_InGitRepo(t *testing.T) {
+	// Skip if not in a git repository
+	if _, err := os.Stat(".git"); os.IsNotExist(err) {
+		t.Skip("Not in a git repository, skipping integration test")
+	}
+
 	fn := NewGitRootFunction()
 
 	// This test assumes we're running in a git repository.

As per coding guidelines: prefer unit tests with mocks over integration tests.


35-48: Consider consolidating metadata tests or using table-driven approach.

TestGitRootFunction_Aliases duplicates alias validation already covered in TestNewGitRootFunction (line 40). You could consolidate these into a single metadata test or use a table-driven structure for checking Name/Phase/Aliases properties.

🔎 Example consolidation
 func TestNewGitRootFunction(t *testing.T) {
 	fn := NewGitRootFunction()
 	require.NotNil(t, fn)
 	assert.Equal(t, TagRepoRoot, fn.Name())
 	assert.Equal(t, PreMerge, fn.Phase())
-	assert.Contains(t, fn.Aliases(), "git-root")
-}
-
-func TestGitRootFunction_Aliases(t *testing.T) {
-	fn := NewGitRootFunction()
-	aliases := fn.Aliases()
+	
+	// Validate aliases
+	aliases := fn.Aliases()
 	assert.Len(t, aliases, 1)
 	assert.Equal(t, "git-root", aliases[0])
 }
pkg/function/env_test.go (4)

73-80: Consider using sentinel errors instead of string matching.

Line 79 checks error message content with assert.Contains, which doesn't follow the guideline to use errors.Is() for error checking. While validation errors often use string matching, wrapping argument validation errors in a defined sentinel (e.g., ErrInvalidArgCount) would be more robust and testable.

As per coding guidelines: all errors should be wrapped using static errors from errors/errors.go.


82-127: Consider whether internal helper warrants direct testing.

parseEnvArgs is unexported, and testing internal helpers directly can couple tests to implementation details. If the parsing logic is complex enough to justify independent testing, this is fine. Otherwise, consider testing this behavior exclusively through the public Execute method.

The test structure itself is solid—just verify that direct testing of this helper aligns with your testing strategy.


129-140: Minimal coverage for internal helper.

Similar to parseEnvArgs, this tests an unexported helper directly. Additionally, this only covers nil/empty cases without positive test cases. Consider whether this helper's behavior is sufficiently tested through the public API or if positive cases are missing here.


142-148: Constructor test may be tautological.

This test verifies the constructor returns specific constant values (TagEnv, PreMerge, nil aliases). If these properties are part of the Function interface contract and consumers depend on them, this is valid. Otherwise, it's testing that the constructor does exactly what it says it does, which can be considered a tautology.

Consider whether these properties are sufficiently validated through integration tests or if this constructor-level check adds value.

pkg/function/store_test.go (1)

590-622: Consider removing tautological struct tests.

These tests verify basic struct field assignment, which is Go language behavior rather than business logic. If struct assignment were broken, the functional tests above would already fail. Removing these tests reduces maintenance overhead without losing meaningful coverage.

Based on coding guidelines: "Test behavior, not implementation; avoid tautological tests".

pkg/function/exec_test.go (2)

20-36: Refactor to table-driven test.

These two test functions validate the same error condition with different inputs. Consolidate them into a single table-driven test per Go best practices.

🔎 Proposed refactor using table-driven test
-func TestExecFunction_Execute_EmptyArgs(t *testing.T) {
-	fn := NewExecFunction()
-
-	// Empty args should return error.
-	_, err := fn.Execute(context.Background(), "", nil)
-	require.Error(t, err)
-	assert.ErrorIs(t, err, ErrInvalidArguments)
-}
-
-func TestExecFunction_Execute_WhitespaceOnly(t *testing.T) {
-	fn := NewExecFunction()
-
-	// Whitespace-only args should return error.
-	_, err := fn.Execute(context.Background(), "   ", nil)
-	require.Error(t, err)
-	assert.ErrorIs(t, err, ErrInvalidArguments)
-}
+func TestExecFunction_Execute_InvalidArgs(t *testing.T) {
+	tests := []struct {
+		name string
+		args string
+	}{
+		{"empty args", ""},
+		{"whitespace only", "   "},
+	}
+
+	fn := NewExecFunction()
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			_, err := fn.Execute(context.Background(), tt.args, nil)
+			require.Error(t, err)
+			assert.ErrorIs(t, err, ErrInvalidArguments)
+		})
+	}
+}

Based on coding guidelines.


38-71: Consider table-driven test for output parsing scenarios.

The three output parsing tests share similar structure and could be consolidated into a table-driven test to reduce duplication and improve maintainability.

🔎 Proposed refactor using table-driven test
-func TestExecFunction_Execute_JSONOutput(t *testing.T) {
-	fn := NewExecFunction()
-
-	// Test command that outputs JSON.
-	result, err := fn.Execute(context.Background(), `echo '{"key": "value"}'`, nil)
-	require.NoError(t, err)
-
-	// Result should be parsed as map.
-	m, ok := result.(map[string]any)
-	require.True(t, ok)
-	assert.Equal(t, "value", m["key"])
-}
-
-func TestExecFunction_Execute_JSONArray(t *testing.T) {
-	fn := NewExecFunction()
-
-	// Test command that outputs JSON array.
-	result, err := fn.Execute(context.Background(), `echo '[1, 2, 3]'`, nil)
-	require.NoError(t, err)
-
-	// Result should be parsed as slice.
-	arr, ok := result.([]any)
-	require.True(t, ok)
-	assert.Len(t, arr, 3)
-}
-
-func TestExecFunction_Execute_NonJSONOutput(t *testing.T) {
-	fn := NewExecFunction()
-
-	// Test command that outputs non-JSON (echo adds a newline).
-	result, err := fn.Execute(context.Background(), "echo 'not json'", nil)
-	require.NoError(t, err)
-	assert.Equal(t, "not json\n", result)
-}
+func TestExecFunction_Execute_OutputParsing(t *testing.T) {
+	tests := []struct {
+		name      string
+		cmd       string
+		validator func(t *testing.T, result any)
+	}{
+		{
+			name: "JSON object",
+			cmd:  `echo '{"key": "value"}'`,
+			validator: func(t *testing.T, result any) {
+				m, ok := result.(map[string]any)
+				require.True(t, ok)
+				assert.Equal(t, "value", m["key"])
+			},
+		},
+		{
+			name: "JSON array",
+			cmd:  `echo '[1, 2, 3]'`,
+			validator: func(t *testing.T, result any) {
+				arr, ok := result.([]any)
+				require.True(t, ok)
+				assert.Len(t, arr, 3)
+			},
+		},
+		{
+			name: "non-JSON output",
+			cmd:  "echo 'not json'",
+			validator: func(t *testing.T, result any) {
+				assert.Equal(t, "not json\n", result)
+			},
+		},
+	}
+
+	fn := NewExecFunction()
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			result, err := fn.Execute(context.Background(), tt.cmd, nil)
+			require.NoError(t, err)
+			tt.validator(t, result)
+		})
+	}
+}

Based on coding guidelines.

📜 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 f4db88d and d9e4772.

📒 Files selected for processing (12)
  • pkg/function/env_test.go
  • pkg/function/exec_test.go
  • pkg/function/git_root.go
  • pkg/function/git_root_test.go
  • pkg/function/include_test.go
  • pkg/function/phase_test.go
  • pkg/function/random_test.go
  • pkg/function/registry_test.go
  • pkg/function/resolution/context.go
  • pkg/function/resolution/context_test.go
  • pkg/function/store_test.go
  • pkg/yaml/types_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/function/git_root.go
  • pkg/function/resolution/context_test.go
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

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

**/*.go: Use Viper for managing configuration, environment variables, and flags in CLI commands
Use interfaces for external dependencies to facilitate mocking and consider using testify/mock for creating mock implementations
All code must pass golangci-lint checks
Follow Go's error handling idioms: use meaningful error messages, wrap errors with context using fmt.Errorf("context: %w", err), and consider using custom error types for domain-specific errors
Follow standard Go coding style: use gofmt and goimports to format code, prefer short descriptive variable names, use kebab-case for command-line flags, and snake_case for environment variables
Document all exported functions, types, and methods following Go's documentation conventions
Document complex logic with inline comments in Go code
Support configuration via files, environment variables, and flags following the precedence order: flags > environment variables > config file > defaults
Provide clear error messages to users, include troubleshooting hints when appropriate, and log detailed errors for debugging

**/*.go: NEVER use fmt.Fprintf(os.Stdout/Stderr) or fmt.Println(); use data.* or ui.* functions instead
All comments must end with periods (enforced by godot linter)
Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages; maintain aliases: cfg, log, u, errUtils
Add defer perf.Track(atmosConfig, "pkg.FuncName")() + blank line to all public functions for performance tracking; use nil if no atmosConfig param
All errors MUST be wrapped using static errors defined in errors/errors.go; use errors.Join for combining multiple errors; use fmt.Errorf with %w for adding string context; use error builder for complex errors; use errors.Is() for error checking; NEVER use dynamic errors directly
Use go.uber.org/mock/mockgen with //go:generate directives for mock generation; never create manual mocks
Keep files small...

Files:

  • pkg/function/phase_test.go
  • pkg/function/git_root_test.go
  • pkg/function/registry_test.go
  • pkg/function/random_test.go
  • pkg/function/exec_test.go
  • pkg/function/env_test.go
  • pkg/function/include_test.go
  • pkg/function/resolution/context.go
  • pkg/yaml/types_test.go
  • pkg/function/store_test.go
**/*_test.go

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

**/*_test.go: Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages
Use table-driven tests for testing multiple scenarios in Go
Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

**/*_test.go: Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage
Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Files:

  • pkg/function/phase_test.go
  • pkg/function/git_root_test.go
  • pkg/function/registry_test.go
  • pkg/function/random_test.go
  • pkg/function/exec_test.go
  • pkg/function/env_test.go
  • pkg/function/include_test.go
  • pkg/yaml/types_test.go
  • pkg/function/store_test.go
🧠 Learnings (27)
📓 Common learnings
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: errors/errors.go:184-203
Timestamp: 2025-12-13T06:10:13.688Z
Learning: cloudposse/atmos: For toolchain work, duplicate/unused error sentinels in errors/errors.go should be cleaned up in a separate refactor PR and not block feature PRs; canonical toolchain sentinels live under toolchain/registry with re-exports in toolchain/errors.go.
Learnt from: Listener430
Repo: cloudposse/atmos PR: 934
File: tests/fixtures/scenarios/docs-generate/README.md.gotmpl:99-118
Timestamp: 2025-01-25T03:51:57.689Z
Learning: For the cloudposse/atmos repository, changes to template contents should be handled in dedicated PRs and are typically considered out of scope for PRs focused on other objectives.
Learnt from: aknysh
Repo: cloudposse/atmos PR: 944
File: go.mod:206-206
Timestamp: 2025-01-17T00:18:57.769Z
Learning: For indirect dependencies with license compliance issues in the cloudposse/atmos repository, the team prefers to handle them in follow-up PRs rather than blocking the current changes, as these issues often require deeper investigation of the dependency tree.
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Avoid utils package bloat; don't add new functions to pkg/utils/; create purpose-built packages instead (examples: pkg/store/, pkg/git/, pkg/pro/, pkg/filesystem/)
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1363
File: internal/exec/template_utils.go:18-18
Timestamp: 2025-07-05T20:59:02.914Z
Learning: In the Atmos project, gomplate v4 is imported with a blank import (`_ "github.com/hairyhenderson/gomplate/v4"`) alongside v3 imports to resolve AWS SDK version conflicts. V3 uses older AWS SDK versions that conflict with newer AWS modules used by Atmos. A full migration to v4 requires extensive refactoring due to API changes and should be handled in a separate PR.
Learnt from: Benbentwo
Repo: cloudposse/atmos PR: 1475
File: pkg/auth/manager.go:304-312
Timestamp: 2025-09-25T01:02:48.697Z
Learning: The auth manager in pkg/auth/manager.go should remain cloud-agnostic and not contain AWS-specific logic or references to specific cloud providers. Keep the manager generic and extensible.
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages

Applied to files:

  • pkg/function/phase_test.go
  • pkg/function/git_root_test.go
  • pkg/function/registry_test.go
  • pkg/function/random_test.go
  • pkg/function/exec_test.go
  • pkg/function/env_test.go
  • pkg/function/include_test.go
  • pkg/yaml/types_test.go
  • pkg/function/store_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

Applied to files:

  • pkg/function/phase_test.go
  • pkg/function/git_root_test.go
  • pkg/function/registry_test.go
  • pkg/function/random_test.go
  • pkg/function/exec_test.go
  • pkg/function/env_test.go
  • pkg/function/include_test.go
  • pkg/yaml/types_test.go
  • pkg/function/store_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Applied to files:

  • pkg/function/phase_test.go
  • pkg/function/git_root_test.go
  • pkg/function/registry_test.go
  • pkg/function/random_test.go
  • pkg/function/exec_test.go
  • pkg/function/env_test.go
  • pkg/function/include_test.go
  • pkg/yaml/types_test.go
  • pkg/function/store_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Use table-driven tests for testing multiple scenarios in Go

Applied to files:

  • pkg/function/phase_test.go
  • pkg/function/git_root_test.go
  • pkg/function/registry_test.go
  • pkg/function/random_test.go
  • pkg/function/exec_test.go
  • pkg/function/env_test.go
  • pkg/function/include_test.go
  • pkg/yaml/types_test.go
  • pkg/function/store_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage

Applied to files:

  • pkg/function/phase_test.go
  • pkg/function/git_root_test.go
  • pkg/function/registry_test.go
  • pkg/function/random_test.go
  • pkg/function/include_test.go
  • pkg/yaml/types_test.go
  • pkg/function/store_test.go
📚 Learning: 2025-05-23T19:51:47.091Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1255
File: cmd/describe_affected_test.go:15-15
Timestamp: 2025-05-23T19:51:47.091Z
Learning: The atmos codebase has a custom extension to *testing.T that provides a Chdir method, allowing test functions to call t.Chdir() to change working directories during tests. This is used consistently across test files in the codebase.

Applied to files:

  • pkg/function/phase_test.go
  • pkg/function/git_root_test.go
  • pkg/function/env_test.go
📚 Learning: 2025-12-21T04:10:29.030Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1891
File: internal/exec/describe_affected.go:468-468
Timestamp: 2025-12-21T04:10:29.030Z
Learning: In Go, package-level declarations (constants, variables, types, and functions) are visible to all files in the same package without imports. During reviews in cloudposse/atmos (and similar Go codebases), before suggesting to declare a new identifier, first check if it already exists in another file of the same package. If it exists, you can avoid adding a new declaration; if not, proceed with a proper package-level declaration. 

Applied to files:

  • pkg/function/phase_test.go
  • pkg/function/git_root_test.go
  • pkg/function/registry_test.go
  • pkg/function/random_test.go
  • pkg/function/exec_test.go
  • pkg/function/env_test.go
  • pkg/function/include_test.go
  • pkg/function/resolution/context.go
  • pkg/yaml/types_test.go
  • pkg/function/store_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to cmd/**/*_test.go : ALWAYS use cmd.NewTestKit(t) for cmd tests to auto-clean RootCmd state (flags, args)

Applied to files:

  • pkg/function/git_root_test.go
📚 Learning: 2025-12-10T18:32:51.237Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1808
File: cmd/terraform/backend/backend_delete_test.go:9-23
Timestamp: 2025-12-10T18:32:51.237Z
Learning: In cmd subpackages (e.g., cmd/terraform/backend/), tests cannot use cmd.NewTestKit(t) due to Go's test visibility rules (NewTestKit is in a parent package test file). These tests only need TestKit if they execute commands through RootCmd or modify RootCmd state. Structural tests that only verify command structure/flags without touching RootCmd don't require TestKit cleanup.

Applied to files:

  • pkg/function/git_root_test.go
📚 Learning: 2025-05-23T19:51:47.091Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1255
File: cmd/describe_affected_test.go:15-15
Timestamp: 2025-05-23T19:51:47.091Z
Learning: In the atmos codebase, t.Chdir() is a valid method call on *testing.T objects and works correctly for changing directories in tests. This is implemented through custom testing framework extensions and is used consistently throughout the test suite.

Applied to files:

  • pkg/function/git_root_test.go
📚 Learning: 2025-11-10T23:23:39.771Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: toolchain/registry/aqua/aqua_test.go:417-442
Timestamp: 2025-11-10T23:23:39.771Z
Learning: In Atmos toolchain AquaRegistry, tests should not hit real GitHub. Use the options pattern via WithGitHubBaseURL to inject an httptest server URL and make GetLatestVersion/GetAvailableVersions deterministic.

Applied to files:

  • pkg/function/registry_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Define interfaces for all major functionality and use dependency injection for testability; generate mocks with go.uber.org/mock/mockgen

Applied to files:

  • pkg/function/registry_test.go
  • pkg/function/store_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: New configs support Go templating with FuncMap() from internal/exec/template_funcs.go

Applied to files:

  • pkg/function/exec_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*.go : Keep files small and focused (<600 lines); one cmd/impl per file; co-locate tests; never use //revive:disable:file-length-limit

Applied to files:

  • pkg/function/exec_test.go
  • pkg/yaml/types_test.go
📚 Learning: 2025-11-11T03:47:59.576Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: toolchain/which_test.go:166-223
Timestamp: 2025-11-11T03:47:59.576Z
Learning: In the cloudposse/atmos repo, tests that manipulate environment variables should use testing.T.Setenv for automatic setup/teardown instead of os.Setenv/Unsetenv.

Applied to files:

  • pkg/function/env_test.go
📚 Learning: 2025-11-11T03:47:45.878Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: toolchain/add_test.go:67-77
Timestamp: 2025-11-11T03:47:45.878Z
Learning: In the cloudposse/atmos codebase, tests should prefer t.Setenv for environment variable setup/teardown instead of os.Setenv/Unsetenv to ensure test-scoped isolation.

Applied to files:

  • pkg/function/env_test.go
📚 Learning: 2025-12-13T06:10:25.156Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: internal/exec/workflow_utils.go:0-0
Timestamp: 2025-12-13T06:10:25.156Z
Learning: Atmos workflows: In internal/exec/workflow_utils.go ExecuteWorkflow, non-identity steps intentionally use baseWorkflowEnv, which is constructed from the parent environment with PATH modifications for the toolchain. Avoid appending os.Environ() again; prefer documenting this behavior and testing that standard environment variables are preserved.

Applied to files:

  • pkg/function/env_test.go
📚 Learning: 2025-08-15T14:43:41.030Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1352
File: pkg/store/artifactory_store_test.go:108-113
Timestamp: 2025-08-15T14:43:41.030Z
Learning: In test files for the atmos project, it's acceptable to ignore errors from os.Setenv/Unsetenv operations during test environment setup and teardown, as these are controlled test scenarios.

Applied to files:

  • pkg/function/env_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*.go : Add `defer perf.Track(atmosConfig, "pkg.FuncName")()` + blank line to all public functions for performance tracking; use nil if no atmosConfig param

Applied to files:

  • pkg/function/resolution/context.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Use context.Context ONLY for cancellation signals, deadlines/timeouts, and request-scoped values (trace IDs, request IDs); context MUST be the first parameter in functions that accept it. DO NOT use context for passing configuration, dependencies, or avoiding proper function parameters

Applied to files:

  • pkg/function/resolution/context.go
📚 Learning: 2025-10-13T18:13:54.020Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1622
File: pkg/perf/perf.go:140-184
Timestamp: 2025-10-13T18:13:54.020Z
Learning: In pkg/perf/perf.go, the `trackWithSimpleStack` function intentionally skips ownership checks at call stack depth > 1 to avoid expensive `getGoroutineID()` calls on every nested function. This is a performance optimization for the common single-goroutine execution case (most Atmos commands), accepting the rare edge case of potential metric corruption if multi-goroutine execution occurs at depth > 1. The ~19× performance improvement justifies this trade-off.

Applied to files:

  • pkg/function/resolution/context.go
📚 Learning: 2025-04-04T02:03:23.676Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1185
File: internal/exec/yaml_func_store.go:26-26
Timestamp: 2025-04-04T02:03:23.676Z
Learning: The Atmos codebase currently uses `log.Fatal` for error handling in multiple places. The maintainers are aware this isn't an ideal pattern (should only be used in main() or init() functions) and plan to address it comprehensively in a separate PR. CodeRabbit should not flag these issues or push for immediate changes until that refactoring is complete.

Applied to files:

  • pkg/function/resolution/context.go
📚 Learning: 2024-11-13T21:37:07.852Z
Learnt from: Cerebrovinny
Repo: cloudposse/atmos PR: 764
File: internal/exec/describe_stacks.go:289-295
Timestamp: 2024-11-13T21:37:07.852Z
Learning: In the `internal/exec/describe_stacks.go` file of the `atmos` project written in Go, avoid extracting the stack name handling logic into a helper function within the `ExecuteDescribeStacks` method, even if the logic appears duplicated.

Applied to files:

  • pkg/function/resolution/context.go
📚 Learning: 2025-10-22T14:55:44.014Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1695
File: pkg/auth/manager.go:169-171
Timestamp: 2025-10-22T14:55:44.014Z
Learning: Go 1.20+ supports multiple %w verbs in fmt.Errorf, which returns an error implementing Unwrap() []error. This is valid and does not panic. Atmos uses Go 1.24.8 and configures errorlint with errorf-multi: true to validate this pattern.

Applied to files:

  • pkg/function/resolution/context.go
📚 Learning: 2025-04-24T01:06:15.259Z
Learnt from: haitham911
Repo: cloudposse/atmos PR: 1202
File: pkg/config/process_yaml.go:0-0
Timestamp: 2025-04-24T01:06:15.259Z
Learning: When processing YAML structures with gopkg.in/yaml.v3, the Value field of a Node is only meant for scalar values. Complex data types like maps and arrays are represented through child nodes in the YAML tree structure.

Applied to files:

  • pkg/yaml/types_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*.go : Use interfaces for external dependencies to facilitate mocking and consider using testify/mock for creating mock implementations

Applied to files:

  • pkg/function/store_test.go
🧬 Code graph analysis (7)
pkg/function/phase_test.go (1)
pkg/function/phase.go (3)
  • Phase (8-8)
  • PreMerge (13-13)
  • PostMerge (17-17)
pkg/function/git_root_test.go (4)
pkg/function/git_root.go (1)
  • NewGitRootFunction (20-30)
pkg/function/tags.go (1)
  • TagRepoRoot (39-39)
pkg/function/phase.go (2)
  • PreMerge (13-13)
  • Phase (8-8)
internal/tui/templates/base_template.go (1)
  • Aliases (8-8)
pkg/function/random_test.go (3)
pkg/function/random.go (1)
  • NewRandomFunction (28-38)
pkg/function/tags.go (1)
  • TagRandom (42-42)
pkg/function/phase.go (2)
  • PreMerge (13-13)
  • Phase (8-8)
pkg/function/env_test.go (3)
pkg/function/env.go (1)
  • NewEnvFunction (20-30)
pkg/function/tags.go (1)
  • TagEnv (30-30)
pkg/function/phase.go (2)
  • PreMerge (13-13)
  • Phase (8-8)
pkg/function/resolution/context.go (4)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/schema/schema.go (1)
  • AtmosConfiguration (54-99)
pkg/function/errors.go (1)
  • ErrCircularDependency (20-20)
pkg/function/resolution/errors.go (1)
  • ErrCircularDependency (6-6)
pkg/yaml/types_test.go (1)
pkg/yaml/types.go (1)
  • DefaultIndent (9-9)
pkg/function/store_test.go (5)
pkg/function/store.go (2)
  • NewStoreFunction (20-30)
  • NewStoreGetFunction (86-96)
pkg/function/tags.go (2)
  • TagStore (15-15)
  • TagStoreGet (18-18)
pkg/function/phase.go (2)
  • PostMerge (17-17)
  • Phase (8-8)
pkg/store/store.go (1)
  • Store (6-10)
pkg/function/errors.go (1)
  • ErrExecutionFailed (17-17)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Acceptance Tests (windows)
  • GitHub Check: Summary
🔇 Additional comments (16)
pkg/function/include_test.go (1)

33-47: Constructor tests look good.

Tests appropriately validate the Function interface contract (Name, Phase, Aliases), which is behavioral in the context of the registry pattern. The metadata validated here is used for function lookup and orchestration.

pkg/function/phase_test.go (1)

9-38: Solid table-driven test for Phase.String().

Good coverage of all Phase.String() cases including the edge case for unknown phases. Clean and straightforward.

pkg/yaml/types_test.go (1)

11-51: Solid table-driven test coverage.

The test properly validates marshaling behavior across multiple scenarios with appropriate assertions for node kind, style, and value.

pkg/function/git_root_test.go (1)

3-11: Add blank line between import groups.

Per coding guidelines, imports should be organized in groups separated by blank lines: stdlib, 3rd-party, atmos packages.

🔎 Proposed fix
 import (
 	"context"
 	"os"
 	"path/filepath"
 	"testing"
+
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
⛔ Skipped due to learnings
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1255
File: cmd/describe_affected_test.go:15-15
Timestamp: 2025-05-23T19:51:47.091Z
Learning: The atmos codebase has a custom extension to *testing.T that provides a Chdir method, allowing test functions to call t.Chdir() to change working directories during tests. This is used consistently across test files in the codebase.
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Include integration tests for command flows and test CLI end-to-end when possible with test fixtures
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to cmd/**/*_test.go : ALWAYS use cmd.NewTestKit(t) for cmd tests to auto-clean RootCmd state (flags, args)
Learnt from: osterman
Repo: cloudposse/atmos PR: 1808
File: cmd/terraform/backend/backend_delete_test.go:9-23
Timestamp: 2025-12-10T18:32:51.237Z
Learning: In cmd subpackages (e.g., cmd/terraform/backend/), tests cannot use cmd.NewTestKit(t) due to Go's test visibility rules (NewTestKit is in a parent package test file). These tests only need TestKit if they execute commands through RootCmd or modify RootCmd state. Structural tests that only verify command structure/flags without touching RootCmd don't require TestKit cleanup.
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Use table-driven tests for testing multiple scenarios in Go
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*.go : Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages; maintain aliases: cfg, log, u, errUtils
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*.go : Use interfaces for external dependencies to facilitate mocking and consider using testify/mock for creating mock implementations
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*.go : Keep files small and focused (<600 lines); one cmd/impl per file; co-locate tests; never use //revive:disable:file-length-limit
Learnt from: osterman
Repo: cloudposse/atmos PR: 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.
pkg/function/env_test.go (1)

11-71: Comprehensive edge case coverage with proper test hygiene.

The table-driven structure and use of t.Setenv align with best practices. The test cases cover meaningful scenarios including empty/whitespace args, existing/missing vars, and default value handling.

One note: Line 62 passes nil as the ExecutionContext parameter. Verify this is the intended behavior for testing edge cases rather than testing with a minimal valid context.

pkg/function/store_test.go (1)

43-588: Excellent test coverage and structure.

The test suite is comprehensive and well-organized:

  • Table-driven tests throughout
  • Covers happy paths, error conditions, and edge cases
  • Proper use of errors.Is() for error checking
  • Tests behavior rather than implementation details

This aligns perfectly with the project's testing standards.

pkg/function/registry_test.go (1)

1-431: Excellent test coverage and structure.

This test file provides comprehensive coverage of the function registry with well-structured tests covering registration, retrieval, phase-based filtering, alias handling, case-insensitivity, conflict detection, and function execution integration. The tests follow best practices with proper use of errors.Is(), testify assertions, and behavior-focused validation. Edge cases like duplicate registration, non-existent lookups, and empty registry operations are all covered.

pkg/function/resolution/context.go (5)

1-40: Clean type definitions and proper import organization.

The package structure follows guidelines with properly organized imports (stdlib, then atmos packages) and well-documented types. The DependencyNode and Context structures are clearly defined for tracking dependency resolution chains.


42-82: Goroutine ID parsing is robust with proper fallback handling.

The implementation correctly addresses the previous review feedback by using an atomic counter to generate unique fallback IDs (unknown-N) when parsing fails, preventing metric collisions. The dynamic buffer growth with a safety limit is a solid defensive approach.


84-136: Context lifecycle management is well-designed.

The GetOrCreate, Clear, and Scoped functions provide proper per-goroutine context management with appropriate performance tracking. The Scoped pattern with a restore function is excellent for preventing memory leaks and cross-call contamination.


138-171: Cycle detection logic is sound and efficient.

The Push and Pop methods correctly maintain the call stack and visited set for circular dependency detection. The implementation properly checks for cycles before adding nodes and maintains consistency when popping.


173-218: Error reporting and cloning are well-implemented.

The buildCircularDependencyError function creates detailed, actionable error messages showing the complete dependency chain and helpful resolution steps. The Clone method properly performs a deep copy of both the call stack and visited map, with appropriate nil handling and capacity pre-allocation for efficiency.

pkg/function/random_test.go (3)

71-83: Good boundary testing.

The boundary value test with min=0, max=1 effectively validates range constraints across multiple iterations. Well structured.


85-95: LGTM!

Negative value handling is tested appropriately with clear range validation.


97-103: LGTM!

Constructor test validates the public interface contract correctly.

pkg/function/exec_test.go (1)

11-71: Cross-platform compatibility is already handled—no action needed.

The implementation uses mvdan.cc/sh/v3, a pure Go POSIX shell parser and interpreter that provides consistent shell semantics across all platforms. Single quotes in test commands like echo '{"key": "value"}' are interpreted identically on Windows, Linux, and macOS. Tests will work correctly on all platforms without modification.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 22, 2025

@aknysh, I'll perform a comprehensive review of all the changes in this PR now.


🧠 Learnings used
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: errors/errors.go:184-203
Timestamp: 2025-12-13T06:10:13.688Z
Learning: cloudposse/atmos: For toolchain work, duplicate/unused error sentinels in errors/errors.go should be cleaned up in a separate refactor PR and not block feature PRs; canonical toolchain sentinels live under toolchain/registry with re-exports in toolchain/errors.go.

Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: docs/prd/tool-dependencies-integration.md:58-64
Timestamp: 2025-12-13T06:07:37.766Z
Learning: cloudposse/atmos: For PRD docs (docs/prd/*.md), markdownlint issues like MD040/MD010/MD034 can be handled in a separate documentation cleanup commit and should not block the current PR.

Learnt from: aknysh
Repo: cloudposse/atmos PR: 1185
File: internal/exec/yaml_func_store.go:26-26
Timestamp: 2025-04-04T02:03:23.676Z
Learning: The Atmos codebase currently uses `log.Fatal` for error handling in multiple places. The maintainers are aware this isn't an ideal pattern (should only be used in main() or init() functions) and plan to address it comprehensively in a separate PR. CodeRabbit should not flag these issues or push for immediate changes until that refactoring is complete.

Learnt from: osterman
Repo: cloudposse/atmos PR: 808
File: pkg/config/config.go:478-483
Timestamp: 2024-12-02T21:26:32.337Z
Learning: In the 'atmos' project, when reviewing Go code like `pkg/config/config.go`, avoid suggesting file size checks after downloading remote configs if such checks aren't implemented elsewhere in the codebase.

Learnt from: Listener430
Repo: cloudposse/atmos PR: 934
File: tests/fixtures/scenarios/docs-generate/README.md.gotmpl:99-118
Timestamp: 2025-01-25T03:51:57.689Z
Learning: For the cloudposse/atmos repository, changes to template contents should be handled in dedicated PRs and are typically considered out of scope for PRs focused on other objectives.

Learnt from: aknysh
Repo: cloudposse/atmos PR: 1327
File: internal/exec/describe_affected_utils_2.go:248-248
Timestamp: 2025-06-23T18:50:33.614Z
Learning: When aknysh mentions that a suggestion is not related to the current PR and needs separate tests to be reviewed in a separate PR, this indicates their preference for maintaining focused PR scope and proper test coverage for each change.

Learnt from: aknysh
Repo: cloudposse/atmos PR: 944
File: go.mod:206-206
Timestamp: 2025-01-17T00:18:57.769Z
Learning: For indirect dependencies with license compliance issues in the cloudposse/atmos repository, the team prefers to handle them in follow-up PRs rather than blocking the current changes, as these issues often require deeper investigation of the dependency tree.

Learnt from: osterman
Repo: cloudposse/atmos PR: 740
File: cmd/cmd_utils.go:340-359
Timestamp: 2024-10-23T21:36:40.262Z
Learning: In the Go codebase for Atmos, when reviewing functions like `checkAtmosConfig` in `cmd/cmd_utils.go`, avoid suggesting refactoring to return errors instead of calling `os.Exit` if such changes would significantly increase the scope due to the need to update multiple call sites.
✅ Actions performed

Full review triggered.

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: 3

♻️ Duplicate comments (1)
pkg/function/resolution/context_test.go (1)

359-395: Concurrent access test fix verified.

Previous review correctly identified the issue with require.NotNil inside goroutines. Now using assert.NotNil (line 374) and properly capturing the Push error with assert.NoError (line 385). Good fix.

🧹 Nitpick comments (15)
internal/exec/terraform_utils_test.go (2)

113-116: Consider consolidating cleanup in defer.

The defer at line 114-116 and explicit restoration at line 144-145 both restore os.Stderr, creating redundancy. While this defensive approach ensures restoration in all cases, a more idiomatic pattern would consolidate both w.Close() and os.Stderr restoration in the defer:

-	// Ensure stderr is restored even if test fails.
-	defer func() {
+	defer func() {
+		w.Close()
 		os.Stderr = oldStd
 	}()
 
 	stack := "prod"
 	...
 	err = ExecuteTerraformAffected(&a, &info)
-
-	// Restore stderr before checking error.
-	w.Close()
-	os.Stderr = oldStd
 
 	if err != nil {

This eliminates the double restoration while maintaining cleanup safety.

Also applies to: 141-145


167-170: Consider consolidating cleanup in defer.

Same pattern as TestExecuteTerraformAffectedWithDependents: the defer and explicit restoration create redundancy. Consolidating cleanup operations in the defer would be cleaner:

-	// Ensure stderr is restored even if test fails.
-	defer func() {
+	defer func() {
+		w.Close()
 		os.Stderr = oldStd
 	}()
 
 	stack := "prod"
 	...
 	err := ExecuteTerraformQuery(&info)
-
-	// Restore stderr before checking error.
-	w.Close()
-	os.Stderr = oldStd
 
 	if err != nil {

Also applies to: 183-186

internal/exec/packer_test.go (1)

13-33: Consider adding edge case test for non-git directory.

The test assumes execution in a git repository. Consider adding a test case for Execute behavior when called from a non-git directory to ensure graceful error handling.

Optional test case
func TestGitRootFunction_Execute_NotInGitRepo(t *testing.T) {
	fn := NewGitRootFunction()
	
	// Create a temp directory outside any git repo
	tmpDir := t.TempDir()
	originalWd, _ := os.Getwd()
	defer os.Chdir(originalWd)
	os.Chdir(tmpDir)
	
	_, err := fn.Execute(context.Background(), "", nil)
	assert.Error(t, err)
}
pkg/function/phase.go (1)

20-32: Consider removing perf tracking from this trivial accessor.

The String() method is a simple switch statement. Based on learnings, trivial accessors like this don't need perf.Track() since the tracking overhead would exceed the actual method cost. Consider removing the defer perf.Track(nil, "function.Phase.String")() line.

🔎 Proposed change
 // String returns a human-readable representation of the phase.
 func (p Phase) String() string {
-	defer perf.Track(nil, "function.Phase.String")()
-
 	switch p {
 	case PreMerge:
 		return "pre-merge"
 	case PostMerge:
 		return "post-merge"
 	default:
 		return "unknown"
 	}
 }

Based on learnings, hot-path methods with only trivial operations should avoid perf.Track().

pkg/aws/identity/identity_test.go (2)

14-28: Consider generating mock with mockgen.

Manual mock works here, but coding guidelines prefer go.uber.org/mock/mockgen for consistency. A //go:generate directive in identity.go would enable this.


160-172: TestCallerIdentity is somewhat tautological.

This test only verifies that struct fields hold assigned values. Consider removing or replacing with a test that validates actual behavior (e.g., field extraction from API response).

pkg/function/exec.go (2)

49-52: Wrap shell execution error with context.

Per error handling guidelines, wrap the error with meaningful context to aid debugging.

🔎 Proposed fix
 	res, err := utils.ExecuteShellAndReturnOutput(args, YAMLTag(TagExec)+" "+args, ".", os.Environ(), false)
 	if err != nil {
-		return nil, err
+		return nil, fmt.Errorf("exec function failed for command %q: %w", args, err)
 	}

You'll need to add "fmt" to imports.


49-49: Consider using BaseDir from ExecutionContext.

The working directory is hardcoded to ".". If execCtx is non-nil and execCtx.BaseDir is set, it might be more appropriate to execute from that directory for relative path resolution consistency.

pkg/function/template_test.go (1)

44-65: Consider consolidating JSON type tests into table-driven format.

Tests for JSONNumber, JSONBoolean could be combined into a single table-driven test. Not critical, just a minor cleanup opportunity.

pkg/function/aws_test.go (1)

15-29: Consider using generated mock for consistency.

Same suggestion as identity_test.go - a generated mock via mockgen would align with coding guidelines.

pkg/function/terraform_test.go (1)

234-244: Consider removing tautological struct test.

This test only verifies that Go struct assignment works correctly—it doesn't test any behavior of your code. Tests should validate behavior, not language mechanics. As per coding guidelines, avoid tautological tests.

internal/exec/terraform_test.go (1)

718-724: No-op logger reset.

Line 721 log.SetDefault(log.Default()) sets the default logger to itself—this has no effect. Looks like leftover from refactoring. Consider removing this line.

🔎 Proposed fix
 			// Restore stdout, stderr, and logger.
 			os.Stdout = oldStdout
 			os.Stderr = oldStderr
-			log.SetDefault(log.Default())
pkg/function/tags.go (2)

64-85: Consider using a package-level constant slice instead of a function.

AllTags() allocates a new slice on every call. Since the tag list is fixed at compile time, consider replacing it with a package-level variable:

var allTags = []string{
    TagExec,
    TagStore,
    // ... rest
}

This eliminates repeated allocations while maintaining the same API.

🔎 Proposed refactor
-// AllTags returns all registered tag names.
-func AllTags() []string {
-	defer perf.Track(nil, "function.AllTags")()
-
-	return []string{
-		TagExec,
-		TagStore,
-		TagStoreGet,
-		TagTemplate,
-		TagTerraformOutput,
-		TagTerraformState,
-		TagEnv,
-		TagInclude,
-		TagIncludeRaw,
-		TagRepoRoot,
-		TagRandom,
-		TagLiteral,
-		TagAwsAccountID,
-		TagAwsCallerIdentityArn,
-		TagAwsCallerIdentityUserID,
-		TagAwsRegion,
-	}
-}
+// allTags holds all registered tag names.
+var allTags = []string{
+	TagExec,
+	TagStore,
+	TagStoreGet,
+	TagTemplate,
+	TagTerraformOutput,
+	TagTerraformState,
+	TagEnv,
+	TagInclude,
+	TagIncludeRaw,
+	TagRepoRoot,
+	TagRandom,
+	TagLiteral,
+	TagAwsAccountID,
+	TagAwsCallerIdentityArn,
+	TagAwsCallerIdentityUserID,
+	TagAwsRegion,
+}
+
+// AllTags returns all registered tag names.
+func AllTags() []string {
+	defer perf.Track(nil, "function.AllTags")()
+	return allTags
+}

108-112: Performance tracking overhead may exceed function cost.

YAMLTag and FromYAMLTag perform trivial string operations (concatenation and slice), yet include perf tracking. The tracking overhead likely exceeds the actual work. Per learnings, perf.Track should be skipped for hot-path trivial operations.

🔎 Proposed refactor
 // YAMLTag returns the YAML tag format for a function name (e.g., "env" -> "!env").
 func YAMLTag(name string) string {
-	defer perf.Track(nil, "function.YAMLTag")()
-
 	return YAMLTagPrefix + name
 }
 
 // FromYAMLTag extracts the function name from a YAML tag (e.g., "!env" -> "env").
 func FromYAMLTag(tag string) string {
-	defer perf.Track(nil, "function.FromYAMLTag")()
-
 	if len(tag) > 0 && tag[0] == '!' {
 		return tag[1:]
 	}
 	return tag
 }

Based on learnings, hot-path methods with trivial operations should avoid perf.Track() when tracking overhead exceeds actual work.

Also applies to: 115-122

pkg/function/git_root_test.go (1)

43-48: Minor duplication with constructor test.

This test overlaps with line 40 of TestNewGitRootFunction which already checks aliases. The duplication provides focused coverage for aliases but could be consolidated into table-driven tests if you expand 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: 1

♻️ Duplicate comments (5)
pkg/function/exec_test.go (1)

11-18: Security concern already raised in prior review.

The command injection risk identified in the previous review comment remains valid. The ExecFunction passes commands directly to shell execution without validation. If this is exposed to user-provided configuration values, input validation or command allowlisting should be enforced.

pkg/function/random_test.go (2)

11-69: Good table-driven test structure for error cases.

The test organization follows guidelines well with clear test case names and expectations.

However, the error assertions at line 63 should use errors.Is(err, ErrInvalidArguments) instead of substring matching per coding guidelines.


105-118: Use errors.Is() for sentinel error checking.

Line 117 checks error message with assert.Contains. Since generateRandom wraps ErrInvalidArguments, use errors.Is(err, ErrInvalidArguments) for proper sentinel verification.

pkg/yaml/types_test.go (1)

53-68: Strengthen the integration test assertion.

The comment claims to verify folded style (>), but line 67 only checks for "description:". Assert "description: >" to actually validate the folded scalar format.

pkg/function/store_test.go (1)

15-41: Manual mock noted - follow-up per prior feedback.

This was already flagged in a previous review. The current implementation works; transitioning to mockgen can happen in a follow-up.

🧹 Nitpick comments (12)
pkg/function/phase.go (1)

20-32: Remove perf.Track from trivial String() method.

String() performs only a 3-case switch returning literals. The perf tracking overhead exceeds the method's actual cost, especially since String() is likely called frequently for logging/debugging.

Based on learnings, defer perf.Track() should be excluded from "trivial accessors/mutators where the tracking overhead would exceed the actual method cost."

🔎 Proposed fix
 func (p Phase) String() string {
-	defer perf.Track(nil, "function.Phase.String")()
-
 	switch p {
 	case PreMerge:
 		return "pre-merge"
pkg/function/context.go (1)

41-66: Remove perf.Track from trivial mutator methods.

WithFile, WithBaseDir, and WithStackInfo are trivial mutators that only copy a struct and update a single field. The perf tracking overhead exceeds the actual work performed (struct copy + field assignment).

Based on learnings, defer perf.Track() should be excluded from "trivial accessors/mutators where the tracking overhead would exceed the actual method cost."

🔎 Proposed fix
 func (ctx *ExecutionContext) WithFile(file string) *ExecutionContext {
-	defer perf.Track(nil, "function.ExecutionContext.WithFile")()
-
 	newCtx := *ctx
 	newCtx.File = file
 	return &newCtx
 }
 
 func (ctx *ExecutionContext) WithBaseDir(baseDir string) *ExecutionContext {
-	defer perf.Track(nil, "function.ExecutionContext.WithBaseDir")()
-
 	newCtx := *ctx
 	newCtx.BaseDir = baseDir
 	return &newCtx
 }
 
 func (ctx *ExecutionContext) WithStackInfo(stackInfo *schema.ConfigAndStacksInfo) *ExecutionContext {
-	defer perf.Track(nil, "function.ExecutionContext.WithStackInfo")()
-
 	newCtx := *ctx
 	newCtx.StackInfo = stackInfo
 	return &newCtx
 }
pkg/function/function.go (1)

25-52: Consider perf.Track overhead on trivial getters.

Name(), Aliases(), and Phase() are simple field accessors. The perf.Track calls add overhead for minimal benefit here. These methods may be called frequently during registry lookups.

That said, per guidelines, public functions should have tracking—so this is compliant. Just flagging for awareness.

pkg/function/exec.go (1)

39-62: Consider wrapping the shell execution error with context.

Line 51 returns err directly from ExecuteShellAndReturnOutput. Per coding guidelines, errors should be wrapped with context using fmt.Errorf("context: %w", err).

🔎 Proposed fix
 	res, err := utils.ExecuteShellAndReturnOutput(args, YAMLTag(TagExec)+" "+args, ".", os.Environ(), false)
 	if err != nil {
-		return nil, err
+		return nil, fmt.Errorf("exec function failed: %w", err)
 	}

You'll also need to add "fmt" to the imports.

internal/exec/terraform_test.go (1)

305-337: Minor inconsistency: synchronous pipe read.

Unlike other tests in this file, TestExecuteTerraform_TerraformWorkspace reads from the pipe synchronously (lines 326-331) rather than using a concurrent reader goroutine. This works for small outputs but could theoretically block if output grows. Consider aligning with the concurrent pattern used elsewhere for consistency.

pkg/function/env.go (1)

39-42: Error wrapping loses context.

When SplitStringByDelimiter fails, the original error is discarded. Consider preserving it for debugging.

🔎 Proposed fix
 	parts, err := utils.SplitStringByDelimiter(args, ' ')
 	if err != nil {
-		return "", "", fmt.Errorf("%w: %s", ErrInvalidArguments, args)
+		return "", "", fmt.Errorf("%w: failed to parse args %q: %w", ErrInvalidArguments, args, err)
 	}
pkg/function/terraform.go (1)

102-153: Consider extracting shared validation logic.

TerraformStateFunction.Execute is nearly identical to TerraformOutputFunction.Execute. Once the actual implementations land, you might benefit from a shared helper for the common pre-validation and argument parsing. Not blocking - just something to keep in mind for the migration.

pkg/function/tags.go (1)

63-105: AllTags and TagsMap provide flexibility for tag lookups.

AllTags() allocates on each call which is fine for infrequent use. Note that TagsMap is technically mutable since it's an exported var—external code could modify it. If immutability is important, consider a getter function instead.

pkg/function/store.go (1)

60-77: Consider handling nil values consistently with store.get.

The store function returns defaults only on retrieval errors (line 63), but store.get (via retrieveFromStore at lines 108-111) also returns defaults when the retrieved value is nil. Should both functions handle nil values the same way?

Proposed alignment with store.get behavior
 	// Retrieve the value from the store.
 	value, err := store.Get(params.stack, params.component, params.key)
 	if err != nil {
 		if params.defaultValue != nil {
 			return *params.defaultValue, nil
 		}
 		return nil, fmt.Errorf("%w: failed to get key '%s': %w", ErrExecutionFailed, params.key, err)
 	}
+
+	// Check if the retrieved value is nil and use default if provided.
+	if value == nil && params.defaultValue != nil {
+		return *params.defaultValue, nil
+	}
 
 	// Execute the YQ expression if provided.
pkg/aws/identity/identity.go (1)

301-314: Consider wrapping the error on config reload.

Line 313 returns the result of config.LoadDefaultConfig for assumed role credentials but doesn't wrap the error with context. For consistency with line 297, should this error also be wrapped with errUtils.ErrLoadAWSConfig?

Proposed fix
 		// Reload full config with assumed role credentials.
-		return config.LoadDefaultConfig(ctx, cfgOpts...)
+		assumedCfg, err := config.LoadDefaultConfig(ctx, cfgOpts...)
+		if err != nil {
+			return aws.Config{}, fmt.Errorf("%w: %w", errUtils.ErrLoadAWSConfig, err)
+		}
+		return assumedCfg, nil
 	}
 
 	return baseCfg, nil
pkg/function/resolution/context.go (2)

29-30: Consider adding a godoc comment for package-level variables.

While goroutineContexts is unexported, adding a brief comment would improve code readability and explain its purpose for future maintainers.


49-50: Consider adding a godoc comment for the counter variable.

A brief comment explaining that unknownIDCounter generates unique IDs when goroutine ID parsing fails would improve code documentation.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 22, 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

♻️ Duplicate comments (1)
pkg/function/exec.go (1)

40-63: Execute implementation is clean with understood security trade-offs.

The function directly executes shell commands from stack manifests (line 50). The past review identified this as a command injection vector, which is correct. However, this appears to be an intentional design decision where stack manifests are treated as trusted infrastructure code. The use of mvdan.cc/sh/v3 provides some isolation compared to direct shell execution.

🧹 Nitpick comments (4)
pkg/function/git_root_test.go (1)

14-42: Add error case tests to improve coverage.

The test only covers the happy path. Consider adding tests for error scenarios to reach >80% coverage:

  • Execute behavior when git commands fail (mock or simulate)
  • Execute behavior with different input arguments
  • Edge cases like nested git repositories or worktrees

Additionally, this is more of an integration test than a unit test since it calls actual git commands. Per coding guidelines, consider refactoring the implementation to accept a git command executor interface that can be mocked, enabling true unit tests with dependency injection.

pkg/store/store.go (1)

6-12: Consider adding documentation for GetKey method.

While the go:generate directive is correctly placed, the newly added GetKey method lacks a comment explaining its purpose. Adding a brief comment would improve API clarity.

Suggested documentation
 //go:generate go run go.uber.org/mock/mockgen@v0.6.0 -source=$GOFILE -destination=mock_store.go -package=store
 type Store interface {
 	Set(stack string, component string, key string, value any) error
 	Get(stack string, component string, key string) (any, error)
+	// GetKey retrieves a value directly by key without stack or component context.
 	GetKey(key string) (any, error)
 }
pkg/function/phase.go (1)

17-26: Consider adding performance tracking for consistency.

The repository policy enforces defer perf.Track(nil, "function.Phase.String")() on all public functions, including simple getters. While the overhead might exceed this trivial operation's work, consistency with linting rules is valued.

🔎 Optional enhancement
 func (p Phase) String() string {
+	defer perf.Track(nil, "function.Phase.String")()
+
 	switch p {
 	case PreMerge:
 		return "pre-merge"
 	case PostMerge:
 		return "post-merge"
 	default:
 		return "unknown"
 	}
 }
pkg/function/context.go (1)

41-60: Immutable builder pattern is well-implemented.

The With* methods correctly return copies, preventing mutation bugs and ensuring thread-safety.

Minor consistency note: per repository policy, public methods should include defer perf.Track(...). While these are trivial operations (struct copies), consistency with linting rules is valued across the codebase.

🔎 Optional enhancement for consistency
 func (ctx *ExecutionContext) WithFile(file string) *ExecutionContext {
+	defer perf.Track(nil, "function.ExecutionContext.WithFile")()
+
 	newCtx := *ctx
 	newCtx.File = file
 	return &newCtx
 }

 func (ctx *ExecutionContext) WithBaseDir(baseDir string) *ExecutionContext {
+	defer perf.Track(nil, "function.ExecutionContext.WithBaseDir")()
+
 	newCtx := *ctx
 	newCtx.BaseDir = baseDir
 	return &newCtx
 }

 func (ctx *ExecutionContext) WithStackInfo(stackInfo *schema.ConfigAndStacksInfo) *ExecutionContext {
+	defer perf.Track(nil, "function.ExecutionContext.WithStackInfo")()
+
 	newCtx := *ctx
 	newCtx.StackInfo = stackInfo
 	return &newCtx
 }
📜 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 d9e4772 and 14f3233.

📒 Files selected for processing (19)
  • pkg/aws/identity/identity.go
  • pkg/aws/identity/mock_identity.go
  • pkg/function/context.go
  • pkg/function/env.go
  • pkg/function/env_test.go
  • pkg/function/exec.go
  • pkg/function/exec_test.go
  • pkg/function/git_root_test.go
  • pkg/function/include_test.go
  • pkg/function/phase.go
  • pkg/function/phase_test.go
  • pkg/function/random_test.go
  • pkg/function/store_test.go
  • pkg/function/tags.go
  • pkg/function/tags_test.go
  • pkg/store/mock_store.go
  • pkg/store/store.go
  • pkg/yaml/types_test.go
  • tools/lintroller/rule_perf_track.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • pkg/function/phase_test.go
  • pkg/function/tags_test.go
  • pkg/yaml/types_test.go
  • pkg/function/env_test.go
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

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

**/*.go: Use Viper for managing configuration, environment variables, and flags in CLI commands
Use interfaces for external dependencies to facilitate mocking and consider using testify/mock for creating mock implementations
All code must pass golangci-lint checks
Follow Go's error handling idioms: use meaningful error messages, wrap errors with context using fmt.Errorf("context: %w", err), and consider using custom error types for domain-specific errors
Follow standard Go coding style: use gofmt and goimports to format code, prefer short descriptive variable names, use kebab-case for command-line flags, and snake_case for environment variables
Document all exported functions, types, and methods following Go's documentation conventions
Document complex logic with inline comments in Go code
Support configuration via files, environment variables, and flags following the precedence order: flags > environment variables > config file > defaults
Provide clear error messages to users, include troubleshooting hints when appropriate, and log detailed errors for debugging

**/*.go: NEVER use fmt.Fprintf(os.Stdout/Stderr) or fmt.Println(); use data.* or ui.* functions instead
All comments must end with periods (enforced by godot linter)
Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages; maintain aliases: cfg, log, u, errUtils
Add defer perf.Track(atmosConfig, "pkg.FuncName")() + blank line to all public functions for performance tracking; use nil if no atmosConfig param
All errors MUST be wrapped using static errors defined in errors/errors.go; use errors.Join for combining multiple errors; use fmt.Errorf with %w for adding string context; use error builder for complex errors; use errors.Is() for error checking; NEVER use dynamic errors directly
Use go.uber.org/mock/mockgen with //go:generate directives for mock generation; never create manual mocks
Keep files small...

Files:

  • pkg/function/random_test.go
  • tools/lintroller/rule_perf_track.go
  • pkg/function/git_root_test.go
  • pkg/aws/identity/mock_identity.go
  • pkg/function/phase.go
  • pkg/store/mock_store.go
  • pkg/store/store.go
  • pkg/function/tags.go
  • pkg/function/include_test.go
  • pkg/function/exec_test.go
  • pkg/function/exec.go
  • pkg/function/context.go
  • pkg/function/env.go
  • pkg/function/store_test.go
  • pkg/aws/identity/identity.go
**/*_test.go

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

**/*_test.go: Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages
Use table-driven tests for testing multiple scenarios in Go
Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

**/*_test.go: Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage
Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Files:

  • pkg/function/random_test.go
  • pkg/function/git_root_test.go
  • pkg/function/include_test.go
  • pkg/function/exec_test.go
  • pkg/function/store_test.go
🧠 Learnings (56)
📓 Common learnings
Learnt from: aknysh
Repo: cloudposse/atmos PR: 944
File: go.mod:206-206
Timestamp: 2025-01-17T00:18:57.769Z
Learning: For indirect dependencies with license compliance issues in the cloudposse/atmos repository, the team prefers to handle them in follow-up PRs rather than blocking the current changes, as these issues often require deeper investigation of the dependency tree.
Learnt from: Listener430
Repo: cloudposse/atmos PR: 934
File: tests/fixtures/scenarios/docs-generate/README.md.gotmpl:99-118
Timestamp: 2025-01-25T03:51:57.689Z
Learning: For the cloudposse/atmos repository, changes to template contents should be handled in dedicated PRs and are typically considered out of scope for PRs focused on other objectives.
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Avoid utils package bloat; don't add new functions to pkg/utils/; create purpose-built packages instead (examples: pkg/store/, pkg/git/, pkg/pro/, pkg/filesystem/)
Learnt from: Benbentwo
Repo: cloudposse/atmos PR: 1475
File: pkg/auth/manager.go:304-312
Timestamp: 2025-09-25T01:02:48.697Z
Learning: The auth manager in pkg/auth/manager.go should remain cloud-agnostic and not contain AWS-specific logic or references to specific cloud providers. Keep the manager generic and extensible.
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages

Applied to files:

  • pkg/function/random_test.go
  • pkg/function/git_root_test.go
  • pkg/function/include_test.go
  • pkg/function/exec_test.go
  • pkg/function/store_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Test behavior, not implementation; never test stub functions; avoid tautological tests; make code testable via DI; no coverage theater; remove always-skipped tests; use errors.Is() for error checking

Applied to files:

  • pkg/function/random_test.go
  • tools/lintroller/rule_perf_track.go
  • pkg/function/git_root_test.go
  • pkg/function/tags.go
  • pkg/function/include_test.go
  • pkg/function/exec_test.go
  • pkg/function/store_test.go
  • pkg/aws/identity/identity.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Use table-driven tests for testing multiple scenarios in Go

Applied to files:

  • pkg/function/random_test.go
  • pkg/function/git_root_test.go
  • pkg/function/include_test.go
  • pkg/function/exec_test.go
  • pkg/function/store_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*_test.go : Prefer unit tests with mocks over integration tests; use interfaces + dependency injection for testability; generate mocks with go.uber.org/mock/mockgen; use table-driven tests; target >80% coverage

Applied to files:

  • pkg/function/random_test.go
  • pkg/function/git_root_test.go
  • pkg/aws/identity/mock_identity.go
  • pkg/store/mock_store.go
  • pkg/function/include_test.go
  • pkg/function/store_test.go
  • pkg/aws/identity/identity.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*_test.go : Include integration tests for command flows and test CLI end-to-end when possible with test fixtures

Applied to files:

  • pkg/function/random_test.go
  • pkg/function/git_root_test.go
  • pkg/function/include_test.go
  • pkg/function/exec_test.go
  • pkg/function/store_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*.go : Use interfaces for external dependencies to facilitate mocking and consider using testify/mock for creating mock implementations

Applied to files:

  • pkg/function/random_test.go
  • pkg/aws/identity/mock_identity.go
  • pkg/store/mock_store.go
  • pkg/store/store.go
  • pkg/function/store_test.go
  • pkg/aws/identity/identity.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Define interfaces for all major functionality and use dependency injection for testability; generate mocks with go.uber.org/mock/mockgen

Applied to files:

  • pkg/function/random_test.go
  • pkg/aws/identity/mock_identity.go
  • pkg/store/mock_store.go
  • pkg/store/store.go
  • pkg/function/store_test.go
  • pkg/aws/identity/identity.go
📚 Learning: 2024-10-23T21:36:40.262Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 740
File: cmd/cmd_utils.go:340-359
Timestamp: 2024-10-23T21:36:40.262Z
Learning: In the Go codebase for Atmos, when reviewing functions like `checkAtmosConfig` in `cmd/cmd_utils.go`, avoid suggesting refactoring to return errors instead of calling `os.Exit` if such changes would significantly increase the scope due to the need to update multiple call sites.

Applied to files:

  • pkg/function/random_test.go
  • tools/lintroller/rule_perf_track.go
  • pkg/function/exec_test.go
📚 Learning: 2025-04-04T02:03:23.676Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1185
File: internal/exec/yaml_func_store.go:26-26
Timestamp: 2025-04-04T02:03:23.676Z
Learning: The Atmos codebase currently uses `log.Fatal` for error handling in multiple places. The maintainers are aware this isn't an ideal pattern (should only be used in main() or init() functions) and plan to address it comprehensively in a separate PR. CodeRabbit should not flag these issues or push for immediate changes until that refactoring is complete.

Applied to files:

  • pkg/function/random_test.go
📚 Learning: 2025-12-13T06:10:13.688Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: errors/errors.go:184-203
Timestamp: 2025-12-13T06:10:13.688Z
Learning: cloudposse/atmos: For toolchain work, duplicate/unused error sentinels in errors/errors.go should be cleaned up in a separate refactor PR and not block feature PRs; canonical toolchain sentinels live under toolchain/registry with re-exports in toolchain/errors.go.

Applied to files:

  • pkg/function/random_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*.go : Follow Go's error handling idioms: use meaningful error messages, wrap errors with context using `fmt.Errorf("context: %w", err)`, and consider using custom error types for domain-specific errors

Applied to files:

  • pkg/function/random_test.go
📚 Learning: 2024-12-02T21:26:32.337Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 808
File: pkg/config/config.go:478-483
Timestamp: 2024-12-02T21:26:32.337Z
Learning: In the 'atmos' project, when reviewing Go code like `pkg/config/config.go`, avoid suggesting file size checks after downloading remote configs if such checks aren't implemented elsewhere in the codebase.

Applied to files:

  • pkg/function/random_test.go
  • pkg/function/tags.go
📚 Learning: 2025-04-11T22:06:46.999Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1147
File: internal/exec/validate_schema.go:42-57
Timestamp: 2025-04-11T22:06:46.999Z
Learning: The "ExecuteAtmosValidateSchemaCmd" function in internal/exec/validate_schema.go has been reviewed and confirmed to have acceptable cognitive complexity despite static analysis warnings. The function uses a clean structure with only three if statements for error handling and delegates complex operations to helper methods.

Applied to files:

  • pkg/function/random_test.go
  • pkg/function/exec_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*.go : All errors MUST be wrapped using static errors defined in errors/errors.go; use errors.Join for combining multiple errors; use fmt.Errorf with %w for adding string context; use error builder for complex errors; use errors.Is() for error checking; NEVER use dynamic errors directly

Applied to files:

  • pkg/function/random_test.go
📚 Learning: 2025-12-21T04:10:29.030Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1891
File: internal/exec/describe_affected.go:468-468
Timestamp: 2025-12-21T04:10:29.030Z
Learning: In Go, package-level declarations (constants, variables, types, and functions) are visible to all files in the same package without imports. During reviews in cloudposse/atmos (and similar Go codebases), before suggesting to declare a new identifier, first check if it already exists in another file of the same package. If it exists, you can avoid adding a new declaration; if not, proceed with a proper package-level declaration. 

Applied to files:

  • pkg/function/random_test.go
  • tools/lintroller/rule_perf_track.go
  • pkg/function/git_root_test.go
  • pkg/aws/identity/mock_identity.go
  • pkg/function/phase.go
  • pkg/store/mock_store.go
  • pkg/store/store.go
  • pkg/function/tags.go
  • pkg/function/include_test.go
  • pkg/function/exec_test.go
  • pkg/function/exec.go
  • pkg/function/context.go
  • pkg/function/env.go
  • pkg/function/store_test.go
  • pkg/aws/identity/identity.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*.go : Add `defer perf.Track(atmosConfig, "pkg.FuncName")()` + blank line to all public functions for performance tracking; use nil if no atmosConfig param

Applied to files:

  • tools/lintroller/rule_perf_track.go
  • pkg/function/tags.go
  • pkg/function/context.go
📚 Learning: 2025-11-30T04:16:24.155Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1821
File: pkg/merge/deferred.go:34-48
Timestamp: 2025-11-30T04:16:24.155Z
Learning: In the cloudposse/atmos repository, the `defer perf.Track()` guideline applies to functions that perform meaningful work (I/O, computation, external calls), but explicitly excludes trivial accessors/mutators (e.g., simple getters, setters with single integer increments, string joins, or map appends) where the tracking overhead would exceed the actual method cost and provide no actionable performance data. Hot-path methods called in tight loops should especially avoid perf.Track() if they perform only trivial operations.

Applied to files:

  • tools/lintroller/rule_perf_track.go
📚 Learning: 2025-10-13T18:13:54.020Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1622
File: pkg/perf/perf.go:140-184
Timestamp: 2025-10-13T18:13:54.020Z
Learning: In pkg/perf/perf.go, the `trackWithSimpleStack` function intentionally skips ownership checks at call stack depth > 1 to avoid expensive `getGoroutineID()` calls on every nested function. This is a performance optimization for the common single-goroutine execution case (most Atmos commands), accepting the rare edge case of potential metric corruption if multi-goroutine execution occurs at depth > 1. The ~19× performance improvement justifies this trade-off.

Applied to files:

  • tools/lintroller/rule_perf_track.go
  • pkg/function/context.go
📚 Learning: 2025-11-30T04:16:01.899Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1821
File: pkg/merge/deferred.go:50-59
Timestamp: 2025-11-30T04:16:01.899Z
Learning: In the cloudposse/atmos repository, performance tracking with `defer perf.Track()` should NOT be added to trivial O(1) getter methods that only return field references or check map lengths (e.g., `GetDeferredValues()`, `HasDeferredValues()`). The guideline to add perf tracking to "all public functions" applies to functions that do meaningful work (I/O, computation, external calls), not to trivial accessors where the tracking overhead would exceed the operation time and pollute performance reports.

Applied to files:

  • tools/lintroller/rule_perf_track.go
📚 Learning: 2025-11-09T19:06:58.470Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1752
File: pkg/profile/list/formatter_table.go:27-29
Timestamp: 2025-11-09T19:06:58.470Z
Learning: In the cloudposse/atmos repository, performance tracking with `defer perf.Track()` is enforced on all functions via linting, including high-frequency utility functions, formatters, and renderers. This is a repository-wide policy to maintain consistency and avoid making case-by-case judgment calls about which functions should have profiling.

Applied to files:

  • tools/lintroller/rule_perf_track.go
  • pkg/function/tags.go
📚 Learning: 2025-10-02T19:17:51.630Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1504
File: pkg/profiler/profiler.go:20-31
Timestamp: 2025-10-02T19:17:51.630Z
Learning: In pkg/profiler/profiler.go, profiler-specific errors (ErrUnsupportedProfileType, ErrStartCPUProfile, ErrStartTraceProfile, ErrCreateProfileFile) must remain local and cannot be moved to errors/errors.go due to an import cycle: pkg/profiler → errors → pkg/schema → pkg/profiler. This is a valid exception to the centralized errors policy.

Applied to files:

  • tools/lintroller/rule_perf_track.go
📚 Learning: 2025-05-23T19:51:47.091Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1255
File: cmd/describe_affected_test.go:15-15
Timestamp: 2025-05-23T19:51:47.091Z
Learning: The atmos codebase has a custom extension to *testing.T that provides a Chdir method, allowing test functions to call t.Chdir() to change working directories during tests. This is used consistently across test files in the codebase.

Applied to files:

  • pkg/function/git_root_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to cmd/**/*_test.go : ALWAYS use cmd.NewTestKit(t) for cmd tests to auto-clean RootCmd state (flags, args)

Applied to files:

  • pkg/function/git_root_test.go
📚 Learning: 2025-12-10T18:32:51.237Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1808
File: cmd/terraform/backend/backend_delete_test.go:9-23
Timestamp: 2025-12-10T18:32:51.237Z
Learning: In cmd subpackages (e.g., cmd/terraform/backend/), tests cannot use cmd.NewTestKit(t) due to Go's test visibility rules (NewTestKit is in a parent package test file). These tests only need TestKit if they execute commands through RootCmd or modify RootCmd state. Structural tests that only verify command structure/flags without touching RootCmd don't require TestKit cleanup.

Applied to files:

  • pkg/function/git_root_test.go
📚 Learning: 2025-03-25T12:23:42.649Z
Learnt from: Listener430
Repo: cloudposse/atmos PR: 1149
File: internal/exec/go_getter_utils.go:104-104
Timestamp: 2025-03-25T12:23:42.649Z
Learning: Listener430 plans to add a test for verifying that token injection is skipped for unsupported hosts in a future review or refactoring iteration. This relates to the CustomGitDetector.Detect method in internal/exec/go_getter_utils.go.

Applied to files:

  • pkg/function/git_root_test.go
📚 Learning: 2025-02-05T11:10:51.031Z
Learnt from: mss
Repo: cloudposse/atmos PR: 1024
File: internal/exec/go_getter_utils.go:31-33
Timestamp: 2025-02-05T11:10:51.031Z
Learning: The path traversal check in `ValidateURI` function in `internal/exec/go_getter_utils.go` is intentionally kept despite potentially blocking valid Git URLs, as this validation is planned to be addressed in a separate ticket.

Applied to files:

  • pkg/function/git_root_test.go
📚 Learning: 2025-05-23T19:51:47.091Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1255
File: cmd/describe_affected_test.go:15-15
Timestamp: 2025-05-23T19:51:47.091Z
Learning: In the atmos codebase, t.Chdir() is a valid method call on *testing.T objects and works correctly for changing directories in tests. This is implemented through custom testing framework extensions and is used consistently throughout the test suite.

Applied to files:

  • pkg/function/git_root_test.go
📚 Learning: 2025-05-23T19:51:47.091Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1255
File: cmd/describe_affected_test.go:15-15
Timestamp: 2025-05-23T19:51:47.091Z
Learning: In the atmos codebase, t.Chdir() is a valid method call on *testing.T objects and works correctly for changing directories in tests.

Applied to files:

  • pkg/function/git_root_test.go
📚 Learning: 2024-10-22T23:00:20.627Z
Learnt from: Cerebrovinny
Repo: cloudposse/atmos PR: 737
File: internal/exec/vendor_utils.go:131-141
Timestamp: 2024-10-22T23:00:20.627Z
Learning: In the `ReadAndProcessVendorConfigFile` function in `internal/exec/vendor_utils.go`, the existence of the vendor config file is already checked, so additional file existence checks may be unnecessary.

Applied to files:

  • pkg/function/git_root_test.go
  • pkg/function/exec_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*.go : Use go.uber.org/mock/mockgen with //go:generate directives for mock generation; never create manual mocks

Applied to files:

  • pkg/aws/identity/mock_identity.go
  • pkg/store/mock_store.go
  • pkg/store/store.go
  • pkg/function/store_test.go
  • pkg/aws/identity/identity.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Follow multi-provider registry pattern: define interface, implement per provider, register implementations, generate mocks (example: pkg/store/)

Applied to files:

  • pkg/store/mock_store.go
  • pkg/store/store.go
  • pkg/function/store_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*.go : Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages; maintain aliases: cfg, log, u, errUtils

Applied to files:

  • pkg/function/tags.go
📚 Learning: 2025-09-30T19:03:50.738Z
Learnt from: Cerebrovinny
Repo: cloudposse/atmos PR: 1560
File: pkg/utils/string_utils.go:43-64
Timestamp: 2025-09-30T19:03:50.738Z
Learning: In the Atmos codebase, YAML tags like !terraform.output rely on positional arguments, so the SplitStringByDelimiter function in pkg/utils/string_utils.go must preserve empty strings (even after trimming quotes) to maintain the correct number of positional arguments. Filtering out empty values after trimming would collapse the array and break these function calls.

Applied to files:

  • pkg/function/tags.go
📚 Learning: 2025-06-23T02:14:30.937Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1327
File: cmd/terraform.go:111-117
Timestamp: 2025-06-23T02:14:30.937Z
Learning: In cmd/terraform.go, flags for the DescribeAffected function are added dynamically at runtime when info.Affected is true. This is intentional to avoid exposing internal flags like "file", "format", "verbose", "include-spacelift-admin-stacks", "include-settings", and "upload" in the terraform command interface, while still providing them for the shared DescribeAffected function used by both `atmos describe affected` and `atmos terraform apply --affected`.

Applied to files:

  • pkg/function/tags.go
📚 Learning: 2024-12-17T07:08:41.288Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 863
File: internal/exec/yaml_func_terraform_output.go:34-38
Timestamp: 2024-12-17T07:08:41.288Z
Learning: In the `processTagTerraformOutput` function within `internal/exec/yaml_func_terraform_output.go`, parameters are separated by spaces and do not contain spaces. Therefore, using `strings.Fields()` for parsing is acceptable, and there's no need to handle parameters with spaces.

Applied to files:

  • pkg/function/tags.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*.go : Document all exported functions, types, and methods following Go's documentation conventions

Applied to files:

  • pkg/function/tags.go
📚 Learning: 2024-11-30T22:07:08.610Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 810
File: internal/exec/yaml_func_terraform_output.go:35-40
Timestamp: 2024-11-30T22:07:08.610Z
Learning: In the Go function `processTagTerraformOutput` in `internal/exec/yaml_func_terraform_output.go`, parameters cannot contain spaces. The code splits the input by spaces, and if the parameters contain spaces, `len(parts) != 3` will fail and show an error to the user.

Applied to files:

  • pkg/function/tags.go
📚 Learning: 2025-10-10T23:51:36.597Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1599
File: internal/exec/terraform.go:394-402
Timestamp: 2025-10-10T23:51:36.597Z
Learning: In Atmos (internal/exec/terraform.go), when adding OpenTofu-specific flags like `--var-file` for `init`, do not gate them based on command name (e.g., checking if `info.Command == "tofu"` or `info.Command == "opentofu"`) because command names don't reliably indicate the actual binary being executed (symlinks, aliases). Instead, document the OpenTofu requirement in code comments and documentation, trusting users who enable the feature (e.g., `PassVars`) to ensure their terraform command points to an OpenTofu binary.

Applied to files:

  • pkg/function/tags.go
  • pkg/function/exec_test.go
📚 Learning: 2025-06-07T19:27:40.807Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1266
File: pkg/utils/highlight_utils.go:0-0
Timestamp: 2025-06-07T19:27:40.807Z
Learning: In pkg/utils/highlight_utils.go, the global variable `isTermPresent` that caches terminal detection at package initialization is an intentional design choice and should not be changed to per-call detection.

Applied to files:

  • pkg/function/tags.go
📚 Learning: 2025-04-24T01:40:13.576Z
Learnt from: haitham911
Repo: cloudposse/atmos PR: 1202
File: pkg/config/process_yaml.go:0-0
Timestamp: 2025-04-24T01:40:13.576Z
Learning: When processing YAML nodes with custom directives in Go using gopkg.in/yaml.v3, setting node.Tag = "" is sufficient to prevent re-processing of the node. It's not necessary to also clear node.Value after updating the configuration store (e.g., Viper), as the original value doesn't affect subsequent operations once the tag is removed.

Applied to files:

  • pkg/function/tags.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: New configs support Go templating with FuncMap() from internal/exec/template_funcs.go

Applied to files:

  • pkg/function/tags.go
  • pkg/function/exec_test.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to .github/workflows/*.{yml,yaml} : Configure CI to run unit tests, integration tests, golangci-lint, and coverage reporting on all pull requests

Applied to files:

  • pkg/function/include_test.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*.go : Keep files small and focused (<600 lines); one cmd/impl per file; co-locate tests; never use //revive:disable:file-length-limit

Applied to files:

  • pkg/function/exec_test.go
📚 Learning: 2024-11-16T17:30:52.893Z
Learnt from: pkbhowmick
Repo: cloudposse/atmos PR: 786
File: internal/exec/shell_utils.go:159-162
Timestamp: 2024-11-16T17:30:52.893Z
Learning: For the `atmos terraform shell` command in `internal/exec/shell_utils.go`, input validation for the custom shell prompt is not required, as users will use this as a CLI tool and any issues will impact themselves.

Applied to files:

  • pkg/function/exec_test.go
📚 Learning: 2024-11-19T23:00:45.899Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 795
File: internal/exec/stack_processor_utils.go:378-386
Timestamp: 2024-11-19T23:00:45.899Z
Learning: In the `ProcessYAMLConfigFile` function within `internal/exec/stack_processor_utils.go`, directory traversal in stack imports is acceptable and should not be restricted.

Applied to files:

  • pkg/function/exec_test.go
📚 Learning: 2025-01-25T03:59:47.777Z
Learnt from: Listener430
Repo: cloudposse/atmos PR: 934
File: internal/exec/docs_generate.go:220-248
Timestamp: 2025-01-25T03:59:47.777Z
Learning: In the `downloadSource` function in `internal/exec/docs_generate.go`, the code is intentionally kept concise without additional security measures like content validation, download size limits, or domain validation to maintain simplicity and focus on core functionality.

Applied to files:

  • pkg/function/exec_test.go
📚 Learning: 2025-01-09T22:37:01.004Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 914
File: cmd/terraform_commands.go:260-265
Timestamp: 2025-01-09T22:37:01.004Z
Learning: In the terraform commands implementation (cmd/terraform_commands.go), the direct use of `os.Args[2:]` for argument handling is intentionally preserved to avoid extensive refactoring. While it could be improved to use cobra's argument parsing, such changes should be handled in a dedicated PR to maintain focus and minimize risk.

Applied to files:

  • pkg/function/exec_test.go
📚 Learning: 2025-12-13T06:10:25.156Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: internal/exec/workflow_utils.go:0-0
Timestamp: 2025-12-13T06:10:25.156Z
Learning: Atmos workflows: In internal/exec/workflow_utils.go ExecuteWorkflow, non-identity steps intentionally use baseWorkflowEnv, which is constructed from the parent environment with PATH modifications for the toolchain. Avoid appending os.Environ() again; prefer documenting this behavior and testing that standard environment variables are preserved.

Applied to files:

  • pkg/function/env.go
📚 Learning: 2025-11-08T19:56:18.660Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1697
File: internal/exec/oci_utils.go:0-0
Timestamp: 2025-11-08T19:56:18.660Z
Learning: In the Atmos codebase, when a function receives an `*schema.AtmosConfiguration` parameter, it should read configuration values from `atmosConfig.Settings` fields rather than using direct `os.Getenv()` or `viper.GetString()` calls. The Atmos pattern is: viper.BindEnv in cmd/root.go binds environment variables → Viper unmarshals into atmosConfig.Settings via mapstructure → business logic reads from the Settings struct. This provides centralized config management, respects precedence, and enables testability. Example: `atmosConfig.Settings.AtmosGithubToken` instead of `os.Getenv("ATMOS_GITHUB_TOKEN")` in functions like `getGHCRAuth` in internal/exec/oci_utils.go.

Applied to files:

  • pkg/function/env.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*.go : Use ATMOS_ prefix for environment variables with viper.BindEnv("ATMOS_VAR", "ATMOS_VAR", "FALLBACK")

Applied to files:

  • pkg/function/env.go
📚 Learning: 2025-01-19T22:30:27.600Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 0
File: :0-0
Timestamp: 2025-01-19T22:30:27.600Z
Learning: The Atmos YAML function `!env` is used to retrieve environment variables and assign them to sections in stack manifests. It supports both simple types (string, number, boolean) and complex types (JSON-encoded lists, maps, objects).

Applied to files:

  • pkg/function/env.go
📚 Learning: 2025-09-25T01:02:48.697Z
Learnt from: Benbentwo
Repo: cloudposse/atmos PR: 1475
File: pkg/auth/manager.go:304-312
Timestamp: 2025-09-25T01:02:48.697Z
Learning: The auth manager in pkg/auth/manager.go should remain cloud-agnostic and not contain AWS-specific logic or references to specific cloud providers. Keep the manager generic and extensible.

Applied to files:

  • pkg/aws/identity/identity.go
📚 Learning: 2025-09-07T18:07:00.549Z
Learnt from: Benbentwo
Repo: cloudposse/atmos PR: 1452
File: cmd/auth_login.go:43-44
Timestamp: 2025-09-07T18:07:00.549Z
Learning: In the atmos project, the identity flag is defined as a persistent flag on the auth root command (cmd/auth.go), making it available to all auth subcommands without needing to be redefined in each individual subcommand.

Applied to files:

  • pkg/aws/identity/identity.go
📚 Learning: 2025-07-05T20:59:02.914Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1363
File: internal/exec/template_utils.go:18-18
Timestamp: 2025-07-05T20:59:02.914Z
Learning: In the Atmos project, gomplate v4 is imported with a blank import (`_ "github.com/hairyhenderson/gomplate/v4"`) alongside v3 imports to resolve AWS SDK version conflicts. V3 uses older AWS SDK versions that conflict with newer AWS modules used by Atmos. A full migration to v4 requires extensive refactoring due to API changes and should be handled in a separate PR.

Applied to files:

  • pkg/aws/identity/identity.go
📚 Learning: 2025-11-10T20:03:56.875Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1775
File: pkg/auth/providers/aws/sso_provisioning.go:40-79
Timestamp: 2025-11-10T20:03:56.875Z
Learning: In the Atmos AWS SSO provider (pkg/auth/providers/aws/sso_provisioning.go), the OAuth access token from the AWS SSO device flow is intentionally stored in the `AccessKeyID` field of `AWSCredentials` during authentication. This token is then extracted and used for ListAccounts and ListAccountRoles API calls during identity provisioning. This design reuses the existing `AWSCredentials` type for token transport rather than creating a separate credential type.

Applied to files:

  • pkg/aws/identity/identity.go
🧬 Code graph analysis (8)
pkg/function/random_test.go (3)
pkg/function/random.go (1)
  • NewRandomFunction (28-38)
pkg/function/tags.go (1)
  • TagRandom (42-42)
pkg/function/phase.go (2)
  • PreMerge (9-9)
  • Phase (4-4)
pkg/aws/identity/mock_identity.go (2)
pkg/aws/identity/identity.go (2)
  • GetCallerIdentity (178-216)
  • CallerIdentity (21-26)
pkg/schema/schema.go (2)
  • AtmosConfiguration (54-99)
  • AWSAuthContext (629-644)
pkg/function/tags.go (1)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/function/include_test.go (5)
pkg/function/function.go (1)
  • Function (11-23)
pkg/function/include.go (2)
  • NewIncludeFunction (17-27)
  • NewIncludeRawFunction (58-68)
pkg/function/errors.go (1)
  • ErrSpecialYAMLHandling (23-23)
pkg/function/tags.go (2)
  • TagInclude (33-33)
  • TagIncludeRaw (36-36)
pkg/function/phase.go (2)
  • PreMerge (9-9)
  • Phase (4-4)
pkg/function/exec_test.go (3)
pkg/function/exec.go (1)
  • NewExecFunction (21-31)
pkg/function/tags.go (1)
  • TagExec (12-12)
pkg/function/phase.go (2)
  • PreMerge (9-9)
  • Phase (4-4)
pkg/function/exec.go (8)
pkg/function/function.go (1)
  • BaseFunction (27-31)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/function/tags.go (1)
  • TagExec (12-12)
pkg/function/phase.go (1)
  • PreMerge (9-9)
pkg/function/resolution/context.go (1)
  • Context (24-27)
pkg/function/context.go (1)
  • ExecutionContext (10-28)
pkg/function/errors.go (2)
  • ErrInvalidArguments (14-14)
  • ErrExecutionFailed (17-17)
pkg/utils/shell_utils.go (1)
  • ExecuteShellAndReturnOutput (33-62)
pkg/function/context.go (2)
pkg/schema/schema.go (2)
  • AtmosConfiguration (54-99)
  • ConfigAndStacksInfo (667-763)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/aws/identity/identity.go (3)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/logger/log.go (1)
  • Debug (24-26)
errors/errors.go (1)
  • ErrAwsGetCallerIdentity (95-95)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Acceptance Tests (windows)
  • GitHub Check: Acceptance Tests (macos)
  • GitHub Check: Summary
🔇 Additional comments (33)
pkg/function/git_root_test.go (2)

15-22: Skip logic properly addresses previous concerns.

The git availability and repository checks make this test safe for CI environments. The implementation correctly handles both missing git binary and non-repository contexts with clear skip messages.


44-53: Metadata test looks solid.

The test properly validates the public API surface with clear assertions. The implementation is straightforward and tests the expected behavior.

tools/lintroller/rule_perf_track.go (2)

118-120: Good defensive guard.

Prevents nil dereference on line 121. Returning early when package info is unavailable is the right behavior.


76-77: Verified: excluded receivers are correctly identified as trivial operations.

The ExecutionContext.With* mutators are O(1) struct copies with single field assignments, and Phase.String() is a trivial switch-based formatter. Both fit the exclusion criteria for perf tracking—adding overhead that exceeds actual method cost provides no actionable data.

pkg/aws/identity/mock_identity.go (1)

1-57: LGTM! Generated mock is correct.

The mock file is properly generated and matches the Getter interface. The past concern about the destination filename has been resolved—the directive now correctly generates mock_identity.go.

pkg/aws/identity/identity.go (3)

28-40: go:generate directive is now correct.

The directive has been fixed to use -destination=mock_identity.go, which aligns with codebase patterns. This resolves the previously flagged issue.


122-162: Solid caching implementation with thread-safe double-checked locking.

The caching logic correctly handles concurrent access with RLock for reads and Lock for writes, caches both successful results and errors, and uses proper cache key generation per auth context.


251-317: Auth context integration looks good.

The LoadConfigWithAuth function properly handles Atmos-managed credentials when auth context is provided while maintaining backward compatibility with standard AWS SDK credential resolution.

pkg/function/store_test.go (3)

16-136: Excellent test coverage with proper table-driven approach.

The test suite comprehensively covers StoreFunction behavior including edge cases, error handling, and nil context validation. Tests use gomock-generated mocks and follow all coding guidelines.


154-265: StoreGetFunction tests are well-structured.

Tests properly validate the StoreGetFunction behavior with comprehensive scenarios, including default value handling, query piping, and error cases. Good use of errors.Is for error checking.


267-495: Thorough parameter parsing test coverage.

The parsing tests validate all forms of store command syntax including pipe options (default, query), edge cases, and error conditions. Helper function tests complete the coverage.

pkg/store/mock_store.go (1)

1-84: LGTM! Mock correctly implements Store interface.

The generated mock properly includes all Store methods (Get, GetKey, Set) and follows gomock conventions. Aligns with the updated Store interface.

pkg/function/include_test.go (2)

11-41: Well-structured test for special YAML handling.

The table-driven test correctly verifies that both Include and IncludeRaw return ErrSpecialYAMLHandling using errors.Is(), with appropriate message validation. Good coverage for these special-case functions.


43-72: Complete metadata validation.

The test comprehensively validates constructor behavior and metadata methods for both Include variants, ensuring correct Name, Phase, and Aliases values.

pkg/function/random_test.go (5)

12-67: Comprehensive error case coverage with correct sentinel error checking.

The test properly uses errors.Is() to verify ErrInvalidArguments (line 63) and covers all invalid input scenarios. The past review feedback has been addressed.


69-81: Good boundary value testing.

Multiple iterations with min=0, max=1 properly validate the randomness and range constraints.


83-93: Negative range handling verified.

The test ensures the function correctly handles negative min values and returns results within the specified range.


95-101: Constructor test follows established pattern.

Metadata validation is complete and consistent with other function tests.


103-116: Internal helper test with correct error validation.

The test properly validates both success and error cases, using errors.Is() (line 115) for sentinel error checking as required by guidelines.

pkg/function/phase.go (1)

3-14: Clean phase type definition with helpful documentation.

The Phase type and constants are well-documented with examples of which functions belong to each phase. Using iota is idiomatic Go.

pkg/function/exec_test.go (3)

11-29: Invalid argument handling properly tested.

The test correctly uses errors.Is() to verify ErrInvalidArguments for empty and whitespace-only inputs.


31-80: Comprehensive output parsing validation.

The test thoroughly covers different output formats (plain string, JSON object, JSON array, non-JSON). The checkFunc pattern is clean and flexible. While these execute actual shell commands (making them integration tests), using echo is reliable enough for this case.


82-88: Metadata validation complete.

Standard constructor test verifying all interface methods.

pkg/function/exec.go (1)

15-31: Constructor follows established pattern.

The ExecFunction type and constructor are consistent with other function implementations in the package.

pkg/function/env.go (4)

14-30: Constructor follows established pattern.

Clean implementation consistent with other function types in the package.


32-52: Robust argument parsing with proper error handling.

The function correctly validates inputs, handles both single and double argument cases, and wraps errors with the sentinel ErrInvalidArguments plus context.


54-67: Well-designed context lookup with proper nil safety.

The function safely checks for nil contexts and returns both value and found status, allowing proper distinction between "not set" and "set to empty string."


69-99: Execute method implements proper precedence hierarchy.

The implementation correctly prioritizes:

  1. Stack manifest env section (context)
  2. OS environment variables
  3. Provided default
  4. Empty string fallback

The use of os.LookupEnv properly distinguishes between unset and empty variables.

pkg/function/context.go (1)

8-39: Well-designed context type with clear documentation.

The ExecutionContext provides all necessary runtime information for function execution. The constructor properly initializes core fields and uses the atmosConfig parameter for performance tracking.

pkg/function/tags.go (4)

7-61: Comprehensive tag constants with excellent documentation.

All tag constants are well-documented with clear descriptions of their purposes. The organization and naming are consistent.


63-85: AllTags function properly encapsulates tag list.

Returns a new slice on each call, preventing external mutation of internal state. Includes performance tracking as required.


87-112: IsValidTag properly encapsulates tag validation.

The previous concern about a mutable public TagsMap has been addressed. The map is now private, and IsValidTag() provides safe O(1) lookup without exposing internal state.


114-129: YAML tag helpers are clean and defensive.

YAMLTag and FromYAMLTag provide bidirectional conversion with proper handling of edge cases. FromYAMLTag safely handles input both with and without the prefix.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 22, 2025
@aknysh aknysh merged commit acaba0f into main Dec 22, 2025
57 checks passed
@aknysh aknysh deleted the osterman/refactor-yaml-pkg branch December 22, 2025 19:22
@github-actions
Copy link

These changes were released in v1.203.0-rc.1.

@github-actions
Copy link

These changes were released in v1.203.0-test.5.

@github-actions
Copy link

These changes were released in v1.203.0-test.10.

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

Labels

minor New features that do not break anything size/xl Extra large size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants