8000 Refactor Execution YAML Function Processing, Support !env, !exec,!include,!repo-root directive in configuration files by haitham911 · Pull Request #1202 · cloudposse/atmos · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Refactor Execution YAML Function Processing, Support !env, !exec,!include,!repo-root directive in configuration files #1202

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 56 commits into from
Apr 26, 2025

Conversation

haitham911
Copy link
Collaborator
@haitham911 haitham911 commented Apr 13, 2025

what

  • Move YAML function processing from internal/exec to pkg/utils
  • Updated Configuration processing to handle both !env and !exec directives consistently
  • Configuration processing will exit with an error if it cannot handle YAML functions !env, !exec,!include,!repo-root or default values.
  • Added explicit tests for YAML function processing in config loading

why

  • YAML function processing moved to pkg/utils so it can be shared between exec and config packages without code duplication
  • Added support for !exec directive in configuration files enables dynamic configuration like GITHUB_TOKEN: !exec gh auth token
  • Added support for !env directive in configuration files enables dynamic configuration
  • Added support for !include directive in configuration files enables dynamic configuration import files to atmos.yaml
  • Added support for !repo-root to retrieve the root directory of the Atmos repository. If the Git root is not found, it will return a default value if specified; otherwise, it returns an error

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Added support for executing shell commands directly from YAML configuration using a new YAML function.
    • Introduced enhanced YAML preprocessing to support custom directives including environment variable retrieval, shell command execution, YAML inclusion, and repository root path retrieval.
  • Bug Fixes
    • Improved error handling for environment variable and shell command processing in YAML functions.
    • Enhanced recursion depth protection for shell command execution to prevent infinite loops.
  • Refactor
    • Centralized YAML function logic and shell execution utilities for better maintainability.
    • Standardized logging throughout shell execution components.
    • Delegated shell execution and shell level management to utility package.
    • Updated YAML preprocessing to propagate errors and handle function results more robustly.
  • Tests
    • Added new test coverage for shell command execution, YAML inclusion, and repository root retrieval in configuration.
    • Added tests for environment variable YAML function defaults and error cases.
    • Added tests for shell level environment variable handling.
    • Added tests for string splitting utility function.
  • Chores
    • Removed deprecated constants and internal functions related to YAML function processing.
    • Removed obsolete YAML function execution code from internal packages.
  • Documentation
    • Added documentation for new YAML functions including !env, !exec, !include, and !repo-root.
    • Documented usage and examples for the !repo-root YAML function.

@haitham911 haitham911 requested a review from a team as a code owner April 13, 2025 19:28
Copy link
Contributor
coderabbitai bot commented Apr 13, 2025
📝 Walkthrough

Walkthrough

This update refactors how YAML function tags for shell execution and environment variable interpolation are handled. Shell command execution logic is moved from internal packages to shared utilities, consolidating related code and improving error handling. Constants for YAML function tags are relocated, and related imports are updated. Logging is standardized using a new logger. The test suite is expanded with a new case for shell command execution in YAML. Unused or redundant code is removed, and function signatures are updated to return errors instead of terminating the process directly.

Changes

File(s) Change Summary
cmd/cmd_utils.go Replaced internal error handling calls with direct log.Fatal calls; updated shell execution call to use utility function without atmosConfig argument; added logging package import.
internal/exec/shell_utils.go Removed internal shell execution logic, constants, and functions; switched to utility package for shell execution and shell level management; replaced custom logging with charmbracelet/log; removed ExecuteShellAndReturnOutput and shellRunner functions.
internal/exec/yaml_func_exec.go Deleted file; shell execution YAML function processing moved to utility package.
internal/exec/yaml_func_template.go,
internal/exec/yaml_func_terraform_output.go
Updated to use YAML function tag constants from utilities instead of config package; removed unused imports.
internal/exec/yaml_func_utils.go Refactored to use utility functions for processing exec and env tags with improved error handling via structured logging; replaced local processing calls.
pkg/config/config_test.go Renamed existing test case for clarity; added new test case for shell command execution YAML function; added tests for env function default value and error cases; added test for include YAML function.
pkg/config/const.go Removed constants related to Atmos YAML function tags (Exec, Template, TerraformOutput, Env).
pkg/config/load.go Removed old YAML preprocessing functions; replaced with new structured YAML preprocessing in separate file; removed yaml.v3 import.
pkg/config/process_yaml.go Added new file implementing structured YAML preprocessing for custom tags !env, !exec, !include, and !repo-root with recursive node processing and integration into Viper configuration.
pkg/utils/yaml_func_env.go Refactored environment variable YAML function processing to return (string, error); added getStringAfterTag helper; removed direct program termination calls; updated logging.
pkg/utils/yaml_func_exec.go Added new file implementing shell execution YAML function logic, including shell level management, command execution with environment handling, output capturing, JSON decoding, and recursion depth protection.
pkg/utils/git.go Added new function ProcessTagGitRoot to resolve Git repository root path with fallback.
pkg/utils/yaml_utils.go Added new constant AtmosYamlFuncGitRoot = "!repo-root".
tests/snapshots/TestCLICommands_atmos_circuit-breaker.stderr.golden Updated error message format for exceeding ATMOS_SHLVL max depth to use key-value pairs instead of parentheses.
tests/test-cases/complete.yaml Updated expected error message regex to match new ATMOS_SHLVL max depth error format with key-value pairs.
tests/fixtures/scenarios/atmos-repo-root-yaml-function/atmos.yaml Added fixture file with !repo-root YAML function usage.
pkg/config/process_yaml_test.go Added new test file with unit tests for YAML preprocessing of custom tags !env, !exec, and !include.
website/docs/cli/configuration/configuration.mdx Added new documentation section describing YAML functions !env, !exec, !include, and !repo-root supported in atmos.yaml configuration files.
website/docs/core-concepts/stacks/yaml-functions/repo-root.mdx Added new documentation page describing the !repo-root YAML function.

Sequence Diagram(s)

sequenceDiagram
    participant YAML_Config
    participant Utils
    participant Shell
    participant Logger

    YAML_Config->>Utils: ProcessTagExec(input)
    Utils->>Utils: getStringAfterTag(input, AtmosYamlFuncExec)
    Utils->>Logger: Log execution
    Utils->>Utils: ExecuteShellAndReturnOutput(command, ...)
    Utils->>Utils: GetNextShellLevel()
    Utils->>Shell: ShellRunner(command, ...)
    Shell-->>Utils: Output or error
    Utils->>Utils: Try to unmarshal output as JSON
    Utils-->>YAML_Config: Return result (JSON or string)
Loading

Possibly related PRs

  • Introduce Atmos YAML functions #810: Refactors internal shell execution and YAML function processing by moving shell command execution and YAML function logic into utility packages and replacing internal calls with utility calls; builds upon initial implementation of Atmos YAML functions including !exec.
  • Introduce Atmos YAML functions !include and !env #943: Refactors shell command execution and logging by moving shell execution logic to utility package and updating error handling; directly relates to the implementation of the !exec YAML function.
  • Migrate to Charmbracelet Logger #980: Migrates logging calls to use charmbracelet/log package and removes atmosConfig parameters from logging functions; aligns with this PR’s logging standardization.

Suggested reviewers

  • aknysh
  • osterman

📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1398a72 and 52e2260.

📒 Files selected for processing (1)
  • pkg/config/const.go (0 hunks)
💤 Files with no reviewable changes (1)
  • pkg/config/const.go
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: Analyze (go)
  • GitHub Check: Build (windows-latest, windows)
  • GitHub Check: Lint (golangci)
  • GitHub Check: website-deploy-preview
  • GitHub Check: Summary
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary or @auto-summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @auto-title anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

🧹 Nitpick comments (6)
pkg/utils/yaml_func_env.go (1)

55-65: New helper function with proper error handling

The extraction of the tag processing into its own helper function is a good refactoring practice. However, the static analysis tool flagged the dynamic error on line 60.

Consider using wrapped static errors instead:

-		err := fmt.Errorf("invalid Atmos YAML function: %s", input)
+		var ErrInvalidYamlFunction = fmt.Errorf("invalid Atmos YAML function")
+		err := fmt.Errorf("%w: %s", ErrInvalidYamlFunction, input)

Or alternatively, declare the error at package level:

// At package level
+var ErrInvalidYamlFunction = fmt.Errorf("invalid Atmos YAML function")

// In the function
-		err := fmt.Errorf("invalid Atmos YAML function: %s", input)
+		err := fmt.Errorf("%w: %s", ErrInvalidYamlFunction, input)
🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 60-60:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("invalid Atmos YAML function: %s", input)"

pkg/utils/shell_utils.go (2)

9-10: Good constant definition with documentation

The MaxShellDepth constant is well-defined with a clear comment. However, the static analysis tool suggests the comment should end with a period.

-// MaxShellDepth is the maximum number of nested shell commands that can be executed
+// MaxShellDepth is the maximum number of nested shell commands that can be executed.
🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 9-9:
Comment should end in a period


12-13: Function comment needs a period

Similar to the constant comment, this function comment should end with a period.

-// getNextShellLevel increments the ATMOS_SHLVL and returns the new value or an error if maximum depth is exceeded
+// GetNextShellLevel increments the ATMOS_SHLVL and returns the new value or an error if maximum depth is exceeded.

Also note that the comment starts with getNextShellLevel but the function is named GetNextShellLevel.

🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 12-12:
Comment should end in a period

internal/exec/yaml_func_utils.go (1)

7-7: Import statement needs alias

According to the static analysis tool, this import requires an alias to match project conventions.

-import (
-	"fmt"
-	"strings"
-
-	"github.com/charmbracelet/log"
+import (
+	"fmt"
+	"strings"
+
+	log "github.com/charmbracelet/log"
🧰 Tools
🪛 golangci-lint (1.64.8)

[error] 7-7: import "github.com/charmbracelet/log" imported without alias but must be with alias "log" according to config

(importas)

🪛 GitHub Check: golangci-lint

[failure] 7-7:
import "github.com/charmbracelet/log" imported without alias but must be with alias "log" according to config

pkg/utils/yaml_func_exec.go (2)

40-40: Fix comment format

Add a period at the end of the comment to follow Go style conventions.

-// ExecuteShellAndReturnOutput runs a shell script and capture its standard output
+// ExecuteShellAndReturnOutput runs a shell script and captures its standard output.
🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 40-40:
Comment should end in a period


71-71: Fix comment format

Add a period at the end of the comment to follow Go style conventions.

-// shellRunner uses mvdan.cc/sh/v3's parser and interpreter to run a shell script and divert its stdout
+// shellRunner uses mvdan.cc/sh/v3's parser and interpreter to run a shell script and divert its stdout.
🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 71-71:
Comment should end in a period

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48a6b1f and 19ac285.

📒 Files selected for processing (12)
  • cmd/cmd_utils.go (1 hunks)
  • internal/exec/shell_utils.go (3 hunks)
  • internal/exec/yaml_func_exec.go (0 hunks)
  • internal/exec/yaml_func_template.go (1 hunks)
  • internal/exec/yaml_func_terraform_output.go (1 hunks)
  • internal/exec/yaml_func_utils.go (2 hunks)
  • pkg/config/config_test.go (2 hunks)
  • pkg/config/const.go (0 hunks)
  • pkg/config/load.go (2 hunks)
  • pkg/utils/shell_utils.go (1 hunks)
  • pkg/utils/yaml_func_env.go (2 hunks)
  • pkg/utils/yaml_func_exec.go (1 hunks)
💤 Files with no reviewable changes (2)
  • pkg/config/const.go
  • internal/exec/yaml_func_exec.go
🧰 Additional context used
🧬 Code Graph Analysis (7)
internal/exec/yaml_func_template.go (1)
pkg/utils/yaml_utils.go (1)
  • AtmosYamlFuncTemplate (21-21)
internal/exec/shell_utils.go (1)
pkg/utils/shell_utils.go (1)
  • GetNextShellLevel (13-31)
internal/exec/yaml_func_terraform_output.go (1)
pkg/utils/yaml_utils.go (1)
  • AtmosYamlFuncTerraformOutput (22-22)
pkg/utils/yaml_func_env.go (3)
pkg/utils/log_utils.go (1)
  • LogTrace (74-76)
pkg/utils/yaml_utils.go (1)
  • AtmosYamlFuncEnv (23-23)
pkg/utils/string_utils.go (1)
  • SplitStringByDelimiter (24-34)
pkg/config/config_test.go (1)
pkg/schema/schema.go (1)
  • AtmosConfiguration (13-45)
pkg/utils/yaml_func_exec.go (3)
pkg/utils/yaml_utils.go (1)
  • AtmosYamlFuncExec (19-19)
pkg/utils/shell_utils.go (1)
  • GetNextShellLevel (13-31)
pkg/utils/log_utils.go (1)
  • LogDebug (80-82)
internal/exec/yaml_func_utils.go (2)
pkg/utils/yaml_func_exec.go (1)
  • ProcessTagExec (18-38)
pkg/utils/yaml_func_env.go (1)
  • ProcessTagEnv (9-53)
🪛 GitHub Check: golangci-lint
pkg/config/load.go

[failure] 404-404:
deep-exit: calls to log.Fatal only in main() or init() functions

pkg/utils/shell_utils.go

[failure] 9-9:
Comment should end in a period


[failure] 12-12:
Comment should end in a period


[failure] 14-14:
use of os.Getenv forbidden because "Use viper.BindEnv for new environment variables instead of os.Getenv"


[failure] 19-19:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("invalid ATMOS_SHLVL value: %s", atmosShellLvl)"


[failure] 27-27:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("ATMOS_SHLVL (%d) exceeds maximum allowed depth (%d). Infinite recursion?",\n\tshellVal, MaxShellDepth)"

pkg/utils/yaml_func_env.go

[failure] 60-60:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("invalid Atmos YAML function: %s", input)"

pkg/utils/yaml_func_exec.go

[failure] 24-24:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 29-29:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 40-40:
Comment should end in a period


[failure] 54-54:
appendAssign: append result not assigned to the same slice


[failure] 71-71:
Comment should end in a period

internal/exec/yaml_func_utils.go

[failure] 7-7:
import "github.com/charmbracelet/log" imported without alias but must be with alias "log" according to config


[failure] 79-79:
deep-exit: calls to log.Fatal only in main() or init() functions

🪛 golangci-lint (1.64.8)
internal/exec/yaml_func_utils.go

[error] 7-7: import "github.com/charmbracelet/log" imported without alias but must be with alias "log" according to config

(importas)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build (windows-latest, windows)
  • GitHub Check: Summary
🔇 Additional comments (18)
pkg/utils/yaml_func_env.go (5)

9-11: Function signature properly exported and return type improved

Great job exporting this function to make it accessible from other packages. The return type change from any to (string, error) is a solid improvement that allows for better error handling by callers.


12-12: Logging approach seems consistent

The direct use of LogTrace is consistent with the function's new location in the utils package.


14-17: Good error handling pattern

The error handling pattern here is appropriate - forwarding errors to the caller rather than exiting directly.


23-40: Error handling consistent throughout

The consistent error handling pattern continues throughout the function. This is a good refactoring that improves the function's reusability.


45-53: Return values simplified and standardized

The function now consistently returns appropriate values and errors, which matches modern Go idioms for error handling.

internal/exec/yaml_func_template.go (1)

18-18: Updated function call to use the utils package

The change from using the config package to the utils package is consistent with the refactoring described in the PR objectives.

pkg/utils/shell_utils.go (2)

24-31: Good shell level management

The function properly increments the shell level and checks against the maximum depth. The early exit with error on exceeding MaxShellDepth prevents potential infinite recursion.

However, the static analysis tool flagged dynamic errors at lines 19 and 27. Consider defining errors at package level:

// At package level
+var (
+    ErrInvalidShellLevelValue = fmt.Errorf("invalid ATMOS_SHLVL value")
+    ErrMaxShellDepthExceeded = fmt.Errorf("ATMOS_SHLVL exceeds maximum allowed depth")
+)

// In the function
-			return 0, fmt.Errorf("invalid ATMOS_SHLVL value: %s", atmosShellLvl)
+			return 0, fmt.Errorf("%w: %s", ErrInvalidShellLevelValue, atmosShellLvl)

-		return 0, fmt.Errorf("ATMOS_SHLVL (%d) exceeds maximum allowed depth (%d). Infinite recursion?",
-			shellVal, MaxShellDepth)
+		return 0, fmt.Errorf("%w (%d > %d). Infinite recursion?", 
+			ErrMaxShellDepthExceeded, shellVal, MaxShellDepth)
🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 27-27:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("ATMOS_SHLVL (%d) exceeds maximum allowed depth (%d). Infinite recursion?",\n\tshellVal, MaxShellDepth)"


14-22:

✅ Verification successful

Environment variable handling

The static analysis tool suggests using viper.BindEnv instead of os.Getenv. However, this might be a direct access requirement given the nature of the function.


🏁 Script executed:

#!/bin/bash
# Check if viper is used elsewhere for environment variables
grep -r "viper.BindEnv" --include="*.go" .

Length of output: 42


Environment Variable Access Is Correct as Implemented

After verifying the codebase, there’s no evidence of viper.BindEnv usage elsewhere, and the direct access to the environment using os.Getenv in pkg/utils/shell_utils.go is intentional given the function’s requirements.

  • The static analysis suggestion to use viper.BindEnv doesn't align with the current project configuration.
  • Direct access appears to be the deliberate design choice for handling ATMOS_SHLVL.
🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 14-14:
use of os.Getenv forbidden because "Use viper.BindEnv for new environment variables instead of os.Getenv"


[failure] 19-19:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("invalid ATMOS_SHLVL value: %s", atmosShellLvl)"

internal/exec/yaml_func_terraform_output.go (1)

18-18: Updated function call to use the utils package

Similar to the changes in yaml_func_template.go, this change is consistent with centralizing YAML function constants in the utils package.

cmd/cmd_utils.go (1)

403-403: Function signature change for ExecuteShellAndReturnOutput

The function call has been updated to use the utility package implementation (u.ExecuteShellAndReturnOutput) without passing the atmosConfig parameter that was previously required.

This change aligns with the PR objective of relocating YAML function processing from internal/exec to pkg/utils for shared functionality.

pkg/config/load.go (2)

396-396: Added support for !exec directive

The allowedDirectives now includes both u.AtmosYamlFuncEnv and u.AtmosYamlFuncExec, enabling the configuration processing to handle both directives as stated in the PR objectives.


411-417: Added !exec directive support

Implementation for the !exec directive has been added, fulfilling the PR objective to support dynamic configuration capabilities like GITHUB_TOKEN: !exec gh auth token.

internal/exec/yaml_func_utils.go (1)

71-71: Updated to use utility package implementation

Replaced local tag processing function with the shared utility implementation, which simplifies the code and removes duplication as part of the refactoring.

internal/exec/shell_utils.go (3)

34-34: Updated to use utility package implementation

The code now uses u.GetNextShellLevel() from the utils package instead of a local implementation, which helps eliminate code duplication and provides a centralized place for shell level management.


92-92: Updated to use utility package implementation

Consistent use of u.GetNextShellLevel() from the utils package for shell level management in the ExecuteShell function.


119-119: Updated to use utility package implementation

Consistent use of u.GetNextShellLevel() from the utils package for shell level management in the ExecuteShellAndReturnOutput function.

pkg/config/config_test.go (2)

148-148: Clear test case description update

Good job renaming the test case to explicitly mention the YAML function env tag. This improves clarity and aligns with the new test case.


164-176: Good test coverage for new YAML function exec feature

The new test case properly verifies the shell command execution functionality in YAML configurations. Great attention to detail with the newline assertion on line 174.

Copy link
codecov bot commented Apr 13, 2025

Codecov Report

Attention: Patch coverage is 36.52695% with 212 lines in your changes missing coverage. Please review.

Project coverage is 31.43%. Comparing base (1895246) to head (52e2260).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
pkg/utils/yaml_func_exec.go 23.94% 53 Missing and 1 partial ⚠️
pkg/utils/yaml_func_env.go 0.00% 45 Missing ⚠️
pkg/config/process_yaml.go 69.23% 30 Missing and 10 partials ⚠️
pkg/utils/git.go 0.00% 35 Missing ⚠️
cmd/cmd_utils.go 5.88% 16 Missing ⚠️
internal/exec/shell_utils.go 35.29% 11 Missing ⚠️
internal/exec/yaml_func_utils.go 0.00% 10 Missing ⚠️
internal/exec/yaml_func_terraform_output.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1202      +/-   ##
==========================================
+ Coverage   31.31%   31.43%   +0.12%     
==========================================
  Files         206      208       +2     
  Lines       23044    23189     +145     
==========================================
+ Hits         7216     7290      +74     
- Misses      14747    14818      +71     
  Partials     1081     1081              
Flag Coverage Δ
unittests 31.43% <36.52%> (+0.12%) ⬆️

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

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

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

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 (2)
internal/exec/yaml_func_utils.go (1)

77-81: ⚠️ Potential issue

Avoid using log.Fatal in a library function.

Calling log.Fatal abruptly ends the process and hinders graceful error handling for calling code. Returning the error is preferable.

Consider applying this change:

- res, err := u.ProcessTagEnv(input)
- if err != nil {
-     log.Fatal(err)
- }
- return res
+ res, err := u.ProcessTagEnv(input)
+ if err != nil {
+     return nil // or another sensible fallback
+ }
+ return res
🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 79-79:
deep-exit: calls to log.Fatal only in main() or init() functions

pkg/utils/yaml_func_exec.go (1)

28-29: ⚠️ Potential issue

Use error returns instead of log.Fatal.

Library code shouldn’t terminate the entire process. Return the error so callers can handle it gracefully.

- if err != nil {
-     log.Fatal(err)
- }
...
- if err != nil {
-     log.Fatal(err)
- }
+ if err != nil {
+     return err // or wrap the error

8000
+ }

Also applies to: 33-34

🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 28-28:
deep-exit: calls to log.Fatal only in main() or init() functions

🧹 Nitpick comments (3)
internal/exec/shell_utils.go (2)

61-61: Repeated debug logs.

Multiple lines output “Executing” logs. Consider extracting this into a helper if further logging expansions are planned.

Also applies to: 67-67, 89-89, 91-91


178-182: Minor key mismatch: “workingDirector”.

Consider renaming the key “workingDirector” to “workingDirectory” to avoid confusion:

- "workingDirector", workingDir,
+ "workingDirectory", workingDir,
pkg/utils/yaml_func_exec.go (1)

102-102: Consider static error patterns.

The project’s linter warns about dynamic error construction. Define these as well-known constants or wrap static errors to maintain consistency.

Also applies to: 110-110

🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 102-102:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("invalid ATMOS_SHLVL value: %s", atmosShellLvl)"

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 19ac285 and 8bbdd87.

📒 Files selected for processing (3)
  • internal/exec/shell_utils.go (10 hunks)
  • internal/exec/yaml_func_utils.go (2 hunks)
  • pkg/utils/yaml_func_exec.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
internal/exec/yaml_func_utils.go (2)
Learnt from: samtholiya
PR: cloudposse/atmos#1077
File: pkg/downloader/custom_github_detector.go:0-0
Timestamp: 2025-02-21T20:56:05.539Z
Learning: The `github.com/charmbracelet/log` package should be imported with the alias `log` according to the project's import alias configuration.
Learnt from: samtholiya
PR: cloudposse/atmos#1077
File: pkg/downloader/custom_github_detector_test.go:0-0
Timestamp: 2025-02-21T20:56:20.761Z
Learning: The `github.com/charmbracelet/log` package should be imported with the alias `log`, not `clog`.
🧬 Code Graph Analysis (3)
internal/exec/shell_utils.go (1)
pkg/utils/yaml_func_exec.go (2)
  • GetNextShellLevel (96-114)
  • ShellRunner (75-93)
pkg/utils/yaml_func_exec.go (1)
pkg/utils/yaml_utils.go (1)
  • AtmosYamlFuncExec (19-19)
internal/exec/yaml_func_utils.go (3)
pkg/utils/yaml_func_exec.go (1)
  • ProcessTagExec (22-42)
pkg/utils/yaml_utils.go (3)
  • AtmosYamlFuncStore (20-20)
  • AtmosYamlFuncTerraformOutput (22-22)
  • AtmosYamlFuncEnv (23-23)
pkg/utils/yaml_func_env.go (1)
  • ProcessTagEnv (9-53)
🪛 GitHub Check: golangci-lint
pkg/utils/yaml_func_exec.go

[failure] 28-28:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 33-33:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 58-58:
appendAssign: append result not assigned to the same slice


[failure] 97-97:
use of os.Getenv forbidden because "Use viper.BindEnv for new environment variables instead of os.Getenv"


[failure] 102-102:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("invalid ATMOS_SHLVL value: %s", atmosShellLvl)"


[failure] 110-110:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("ATMOS_SHLVL (%d) exceeds maximum allowed depth (%d). Infinite recursion?",\n\tshellVal, MaxShellDepth)"

internal/exec/yaml_func_utils.go

[failure] 79-79:
deep-exit: calls to log.Fatal only in main() or init() functions

⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: Acceptance Tests (macos-latest, macos)
  • GitHub Check: Acceptance Tests (windows-latest, windows)
  • GitHub Check: Acceptance Tests (ubuntu-latest, linux)
  • GitHub Check: [localstack] demo-localstack
  • GitHub Check: Summary
🔇 Additional comments (14)
internal/exec/yaml_func_utils.go (2)

7-7: Good import alias usage for "github.com/charmbracelet/log".

This aligns with the project's convention of importing this package as log.


71-71: Proper delegation to new utility function.

Replacing any local logic with u.ProcessTagExec() promotes a consistent, reusable approach for YAML “exec” directives.

internal/exec/shell_utils.go (11)

14-14: Consistency with package alias "log".

This import alias is correctly following the project's convention.


19-19: Helpful documentation comment.

This brief comment clearly describes the function’s purpose.


29-29: Good call to shared shell level logic.

Using u.GetNextShellLevel() centralizes recursion depth checks and prevents code duplication.


54-54: Non-blocking warning log.

Logging a warning and returning an error provides a balanced approach for error reporting in library code.


85-85: Centralized shell level retrieval.

Consistent usage of GetNextShellLevel() enhances maintainability.


97-97: Delegates command execution to the new utility.

Allows u.ShellRunner() to handle the entire command lifecycle.


132-133: Graceful fallback on parse failure.

Logging and returning without error is acceptable within a deferred context when ATMOS_SHLVL is non-critical.


141-142: Non-blocking warning log for environment update.

This warns the user but doesn't halt execution, maintaining user workflow continuity.


184-184: Informative debug statement.

Helps users track environment variables being set in the shell.


246-246: Helpful exit log.

Communicates when the interactive shell has been terminated.


291-291: Transparent environment logging.

Showing each variable’s name and value supports better debugging.

pkg/utils/yaml_func_exec.go (1)

58-58: Reassigned environment slice.

Appending to env in a new variable updatedEnv is acceptable. The linter’s note can be safely ignored if you need a separate variable.

🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 58-58:
appendAssign: append result not assigned to the same slice

@haitham911 haitham911 requested review from osterman and aknysh April 15, 2025 17:30
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

♻️ Duplicate comments (1)
internal/exec/yaml_func_utils.go (1)

81-85: ⚠️ Potential issue

Same termination issue as above.
Repeats the log.Fatal call. Please consider returning the error or logging at a suitable level and proceeding.

-case strings.HasPrefix(input, u.AtmosYamlFuncEnv) && !skipFunc(skip, u.AtmosYamlFuncEnv):
-	res, err := u.ProcessTagEnv(input)
-	if err != nil {
-		log.Fatal(err)
-	}
-	return res
+case strings.HasPrefix(input, u.AtmosYamlFuncEnv) && !skipFunc(skip, u.AtmosYamlFuncEnv):
+	res, err := u.ProcessTagEnv(input)
+	if err != nil {
+		log.Error("Failed to process env tag", "error", err)
+		return nil
+	}
+	return res
🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 83-83:
deep-exit: calls to log.Fatal only in main() or init() functions

🧹 Nitpick comments (4)
pkg/config/load.go (1)

401-406: Consider surfacing errors instead of continuing silently.
Note that if ProcessTagEnv fails, the code logs at debug level and then continues. This might hide issues from users.

pkg/utils/yaml_func_exec.go (3)

19-20: Maximum shell depth constant.
Consider making this limit configurable if user scenarios require different recursion depths.


74-93: ShellRunner uses context.TODO.
Providing a user-supplied context or a timeout could help manage long-running commands more gracefully.


95-114: Use os.Getenv carefully.
Project guidelines prefer viper.BindEnv for environment variables, though this is specialized logic. If uniform environment retrieval is desired, consider migrating to viper-based lookups.

🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 97-97:
use of os.Getenv forbidden because "Use viper.BindEnv for new environment variables instead of os.Getenv"


[failure] 102-102:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("invalid ATMOS_SHLVL value: %s", atmosShellLvl)"


[failure] 110-110:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("ATMOS_SHLVL (%d) exceeds maximum allowed depth (%d). Infinite recursion?",\n\tshellVal, MaxShellDepth)"

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8bbdd87 and 89da957.

📒 Files selected for processing (3)
  • internal/exec/yaml_func_utils.go (2 hunks)
  • pkg/config/load.go (2 hunks)
  • pkg/utils/yaml_func_exec.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
internal/exec/yaml_func_utils.go (2)
Learnt from: samtholiya
PR: cloudposse/atmos#1077
File: pkg/downloader/custom_github_detector.go:0-0
Timestamp: 2025-02-21T20:56:05.539Z
Learning: The `github.com/charmbracelet/log` package should be imported with the alias `log` according to the project's import alias configuration.
Learnt from: samtholiya
PR: cloudposse/atmos#1077
File: pkg/downloader/custom_github_detector_test.go:0-0
Timestamp: 2025-02-21T20:56:20.761Z
Learning: The `github.com/charmbracelet/log` package should be imported with the alias `log`, not `clog`.
🧬 Code Graph Analysis (2)
pkg/config/load.go (3)
pkg/utils/yaml_utils.go (2)
  • AtmosYamlFuncEnv (23-23)
  • AtmosYamlFuncExec (19-19)
pkg/utils/yaml_func_env.go (1)
  • ProcessTagEnv (9-53)
pkg/utils/yaml_func_exec.go (1)
  • ProcessTagExec (22-42)
internal/exec/yaml_func_utils.go (3)
pkg/utils/yaml_func_exec.go (1)
  • ProcessTagExec (22-42)
pkg/utils/yaml_utils.go (3)
  • AtmosYamlFuncStore (20-20)
  • AtmosYamlFuncTerraformOutput (22-22)
  • AtmosYamlFuncEnv (23-23)
pkg/utils/yaml_func_env.go (1)
  • ProcessTagEnv (9-53)
🪛 GitHub Check: golangci-lint
pkg/utils/yaml_func_exec.go

[failure] 58-58:
appendAssign: append result not assigned to the same slice


[failure] 97-97:
use of os.Getenv forbidden because "Use viper.BindEnv for new environment variables instead of os.Getenv"


[failure] 102-102:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("invalid ATMOS_SHLVL value: %s", atmosShellLvl)"


[failure] 110-110:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("ATMOS_SHLVL (%d) exceeds maximum allowed depth (%d). Infinite recursion?",\n\tshellVal, MaxShellDepth)"

internal/exec/yaml_func_utils.go

[failure] 73-73:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 83-83:
deep-exit: calls to log.Fatal only in main() or init() functions

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build (windows-latest, windows)
  • GitHub Check: Summary
🔇 Additional comments (7)
internal/exec/yaml_func_utils.go (1)

7-7: Import alias usage confirmed.
The alias log for github.com/charmbracelet/log aligns with the project's style guidelines.

pkg/config/load.go (2)

15-15: Consistent import alias usage.
Using u for the utils package matches existing patterns in the codebase.


396-396: Newly introduced allowedDirectives for YAML tags.
This approach is clean and extensible. No concerns.

pkg/utils/yaml_func_exec.go (4)

1-2: Package and file structure created.
This introduction of a dedicated file for YAML exec functionality is organized and transparent.


3-17: Imports look good.
The chosen libraries and log alias usage follow the project conventions.


22-42: Flexible JSON decoding.
The approach of returning any as decoded result is flexible. Ensure callers handle complex data types as needed.


44-72: Appended environment variables.
Appending to updatedEnv is valid, but ensure consistent usage throughout. The overall function logic is straightforward.

🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 58-58:
appendAssign: append result not assigned to the same slice

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
pkg/utils/yaml_func_exec.go (1)

61-61: 🛠️ Refactor suggestion

Assign the result of append back to the original slice.

The result of append should be assigned to env (not updatedEnv) to avoid confusion and potential bugs. This is a common Go pitfall.

Here's a quick fix:

-	updatedEnv := append(env, fmt.Sprintf("ATMOS_SHLVL=%d", newShellLevel))
+	env = append(env, fmt.Sprintf("ATMOS_SHLVL=%d", newShellLevel))

Then use env in the call to ShellRunner.

🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 61-61:
appendAssign: append result not assigned to the same slice

🧹 Nitpick comments (3)
pkg/utils/yaml_func_exec.go (3)

100-100: Prefer viper.BindEnv over direct os.Getenv for new environment variables.

Project guidelines recommend using viper.BindEnv for environment variable management. This makes configuration more flexible and testable.

If possible, refactor to use viper for ATMOS_SHLVL retrieval.

🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 100-100:
use of os.Getenv forbidden because "Use viper.BindEnv for new environment variables instead of os.Getenv"


105-105: Use wrapped static errors instead of dynamic errors.

Instead of dynamically creating errors with fmt.Errorf, define static error variables and wrap them as needed. This helps with error comparison and handling.

You could define:

var ErrInvalidShellLevel = errors.New("invalid ATMOS_SHLVL value")

And then:

return 0, fmt.Errorf("%w: %s", ErrInvalidShellLevel, atmosShellLvl)
🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 105-105:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("invalid ATMOS_SHLVL value: %s", atmosShellLvl)"


77-96: Shell execution is well-contained, but consider context propagation.

Currently, context.TODO() is used. If you want to support timeouts or cancellation, consider passing a context from the caller.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57b88b1 and f954686.

📒 Files selected for processing (1)
  • pkg/utils/yaml_func_exec.go (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
pkg/utils/yaml_func_exec.go (1)
pkg/utils/yaml_utils.go (1)
  • AtmosYamlFuncExec (19-19)
🪛 GitHub Check: golangci-lint
pkg/utils/yaml_func_exec.go

[failure] 61-61:
appendAssign: append result not assigned to the same slice


[failure] 100-100:
use of os.Getenv forbidden because "Use viper.BindEnv for new environment variables instead of os.Getenv"


[failure] 105-105:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("invalid ATMOS_SHLVL value: %s", atmosShellLvl)"

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build (windows-latest, windows)
  • GitHub Check: Summary
🔇 Additional comments (3)
pkg/utils/yaml_func_exec.go (3)

25-45: Solid error handling and clear separation of concerns.

The function cleanly extracts, executes, and decodes YAML exec tags, returning errors instead of terminating the process. This is a good move for library code.


47-75: Good encapsulation of shell execution with output capture.

The function is straightforward and handles dry-run logic well. Consider adding a timeout/context to avoid hanging on long-running commands, but otherwise, this is tidy.

🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 61-61:
appendAssign: append result not assigned to the same slice


98-116: Recursion guard is clear and effective.

The shell level increment and guard against runaway recursion is well-implemented. Just a reminder: static error wrapping (see above) would make this even more robust.

🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 100-100:
use of os.Getenv forbidden because "Use viper.BindEnv for new environment variables instead of os.Getenv"


[failure] 105-105:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("invalid ATMOS_SHLVL value: %s", atmosShellLvl)"

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 (2)
pkg/utils/yaml_func_exec.go (2)

100-100: ⚠️ Potential issue

Use viper.BindEnv instead of os.Getenv

Per project guidelines, use viper.BindEnv for environment variables instead of os.Getenv.

🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 100-100:
use of os.Getenv forbidden because "Use viper.BindEnv for new environment variables instead of os.Getenv"


47-75: 🛠️ Refactor suggestion

Consider adding timeout mechanism

The shell command execution doesn't have a timeout, which could lead to hung processes if a command never completes.

Consider implementing a context with timeout:

func ExecuteShellAndReturnOutput(
	command string,
	name string,
	dir string,
	env []string,
	dryRun bool,
+	timeout time.Duration,
) (string, error) {
	var b bytes.Buffer

	newShellLevel, err := GetNextShellLevel()
	if err != nil {
		return "", err
	}
	env = append(env, fmt.Sprintf("ATMOS_SHLVL=%d", newShellLevel))

	log.Debug("Executing", "command", command)

	if dryRun {
		return "", nil
	}

+	ctx := context.Background()
+	if timeout > 0 {
+		var cancel context.CancelFunc
+		ctx, cancel = context.WithTimeout(context.Background(), timeout)
+		defer cancel()
+	}

-	err = ShellRunner(command, name, dir, env, &b)
+	err = ShellRunner(command, name, dir, env, &b, ctx)
	if err != nil {
		return "", err
	}

	return b.String(), nil
}
🧹 Nitpick comments (4)
pkg/utils/yaml_func_exec.go (4)

22-23: Add period to comment

Comment should end with a period for consistency with other code comments in the codebase.

-// MaxShellDepth is the maximum number of nested shell commands that can be executed .
+// MaxShellDepth is the maximum number of nested shell commands that can be executed.

47-48: Fix comment formatting

Remove extra space before the period for consistency with Go documentation standards.

-// ExecuteShellAndReturnOutput runs a shell script and capture its standard output .
+// ExecuteShellAndReturnOutput runs a shell script and capture its standard output.

77-78: Fix comment formatting

Remove extra space before the period for consistency with Go documentation standards.

-// ShellRunner uses mvdan.cc/sh/v3's parser and interpreter to run a shell script and divert its stdout .
+// ShellRunner uses mvdan.cc/sh/v3's parser and interpreter to run a shell script and divert its stdout.

98-99: Fix comment formatting

Remove extra space before the period for consistency with Go documentation standards.

-// getNextShellLevel increments the ATMOS_SHLVL and returns the new value or an error if maximum depth is exceeded .
+// GetNextShellLevel increments the ATMOS_SHLVL and returns the new value or an error if maximum depth is exceeded.
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f954686 and 45e66dd.

📒 Files selected for processing (3)
  • pkg/utils/yaml_func_exec.go (1 hunks)
  • tests/snapshots/TestCLICommands_atmos_circuit-breaker.stderr.golden (1 hunks)
  • tests/test-cases/complete.yaml (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • tests/test-cases/complete.yaml
🧰 Additional context used
🧬 Code Graph Analysis (1)
pkg/utils/yaml_func_exec.go (1)
pkg/utils/yaml_utils.go (1)
  • AtmosYamlFuncExec (19-19)
🪛 GitHub Check: golangci-lint
pkg/utils/yaml_func_exec.go

[failure] 100-100:
use of os.Getenv forbidden because "Use viper.BindEnv for new environment variables instead of os.Getenv"


[failure] 105-105:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("ATMOS_SHLVL (%d) exceeds maximum allowed depth (%d). Infinite recursion?",\n\tshellVal, MaxShellDepth)"

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build (windows-latest, windows)
  • GitHub Check: Summary
🔇 Additional comments (3)
pkg/utils/yaml_func_exec.go (2)

113-115: Good error handling with wrapping

Properly using the defined error constant with wrapping to provide context-specific details. This is a good practice.


25-45: ⚠️ Potential issue

Return JSON decoding errors to caller

Currently, the function suppresses JSON parsing errors and returns the raw string. This could lead to unexpected behavior when callers expect a decoded object.

 var decoded any
 if err = json.Unmarshal([]byte(res), &decoded); err != nil {
-		return nil, err
+		return res, nil
 }

Likely an incorrect or invalid review comment.

tests/snapshots/TestCLICommands_atmos_circuit-breaker.stderr.golden (1)

1-1: Improved error message format

The error message now uses a more structured format with labeled key-value pairs (current=11, max=10) instead of parenthesized values. This improves readability and makes it clearer what each number represents.

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/utils/yaml_func_exec.go (1)

99-117: 💡 Verification agent

❓ Verification inconclusive

Environment variable retrieval: project prefers viper over os.Getenv.

The project guidelines recommend using viper.BindEnv for new environment variables instead of os.Getenv. Also, the error message at line 105 uses shellVal before it's set, which could be misleading. Consider using the raw string value for clarity.

Here's a patch for the error message:

-			return 0, fmt.Errorf("ATMOS_SHLVL (%d) exceeds maximum allowed depth (%d). Infinite recursion?",
-				shellVal, MaxShellDepth)
+			return 0, fmt.Errorf("invalid ATMOS_SHLVL value: %s", atmosShellLvl)

Let me know if you want help refactoring to use viper.


Action Required – Refactor Environment Variable Retrieval and Update Error Message

Your changes in pkg/utils/yaml_func_exec.go still use os.Getenv("ATMOS_SHLVL"), whereas our guidelines recommend using viper.BindEnv for retrieving environment variables. In addition, the error message in the conversion failure block references shellVal (which is still zero) instead of the raw input, potentially causing confusion.

Pl 10000 ease address the following:

  • Environment Variable Access:
    Replace the direct call to os.Getenv("ATMOS_SHLVL") with the appropriate viper.BindEnv usage as per project guidelines.

  • Error Message Clarity:
    Change the error message when the conversion from string to integer fails. For example, update the snippet as follows:

    -			return 0, fmt.Errorf("ATMOS_SHLVL (%d) exceeds maximum allowed depth (%d). Infinite recursion?",
    -				shellVal, MaxShellDepth)
    +			return 0, fmt.Errorf("invalid ATMOS_SHLVL value: %s", atmosShellLvl)

Let me know if you’d like further assistance with the viper refactoring.

🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 100-100:
use of os.Getenv forbidden because "Use viper.BindEnv for new environment variables instead of os.Getenv"


[failure] 105-105:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("ATMOS_SHLVL (%d) exceeds maximum allowed depth (%d). Infinite recursion?",\n\tshellVal, MaxShellDepth)"

🧹 Nitpick comments (1)
pkg/utils/yaml_func_exec.go (1)

47-75: Nice modularization. Consider adding a timeout for shell execution.

This function is tidy, but running shell commands without a timeout can lead to hanging processes. Passing a context.Context with a timeout to ShellRunner would make this more robust. Also, logging the command at debug level is a good touch.

Want a code sample for adding a timeout?

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 45e66dd and 70fd2de.

📒 Files selected for processing (3)
  • pkg/utils/yaml_func_exec.go (1 hunks)
  • tests/snapshots/TestCLICommands_atmos_circuit-breaker.stderr.golden (1 hunks)
  • tests/test-cases/complete.yaml (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • tests/snapshots/TestCLICommands_atmos_circuit-breaker.stderr.golden
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test-cases/complete.yaml
🧰 Additional context used
🧬 Code Graph Analysis (1)
pkg/utils/yaml_func_exec.go (1)
pkg/utils/yaml_utils.go (1)
  • AtmosYamlFuncExec (19-19)
🪛 GitHub Check: golangci-lint
pkg/utils/yaml_func_exec.go

[failure] 100-100:
use of os.Getenv forbidden because "Use viper.BindEnv for new environment variables instead of os.Getenv"


[failure] 105-105:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("ATMOS_SHLVL (%d) exceeds maximum allowed depth (%d). Infinite recursion?",\n\tshellVal, MaxShellDepth)"

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build (windows-latest, windows)
  • GitHub Check: Summary
🔇 Additional comments (1)
pkg/utils/yaml_func_exec.go (1)

25-45:

❓ Verification inconclusive

Solid refactor, but consider command validation and error handling tweaks.

The function is clear and concise, but a couple of things to keep in mind:

  • Executing arbitrary shell from YAML is risky. Consider validating or at least logging the command for audit purposes.
  • Error handling is good, but the error from json.Unmarshal will cause the function to fail if the shell output isn't valid JSON. If you want to support plain string outputs, you might want to return res as a fallback instead of erroring out.

Let me know if you want a code snippet for either suggestion.


Action: Enhance Shell Command Validation and Error Resilience

Overall, the refactored implementation in pkg/utils/yaml_func_exec.go is clear and concise. However, I recommend a couple of improvements:

  • Command Security: Since shell commands are invoked based on YAML inputs, it would be wise to validate or at least log the commands being executed. This audit trail can help monitor and mitigate potential security risks from arbitrary shell execution.
  • Error Handling Flexibility: The current approach fails the function if json.Unmarshal returns an error. If supporting plain string outputs is a goal, consider returning the raw output (res) as a fallback instead of triggering an error.

Let me know if you'd like a concrete code snippet for either suggestion.

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

🧹 Nitpick comments (4)
pkg/utils/yaml_func_exec.go (3)

57-70: Optional: add execution timeout & cancellable context

Long‑running or hanging shell commands can block configuration loading.
Consider threading a context.Context (with optional timeout) from the caller into ExecuteShellAndReturnOutput and down to ShellRunner.
This keeps the utility responsive while remaining backward‑compatible when the caller passes context.Background().


78-96: Replace context.TODO() with caller‑supplied context

Inside ShellRunner the hard‑coded context.TODO() prevents cancelation signals from propagating.
Suggestion:

-func ShellRunner(command string, name string, dir string, env []string, out io.Writer) error {
+func ShellRunner(ctx context.Context, command string, name string, dir string, env []string, out io.Writer) error {
 ...
-	return runner.Run(context.TODO(), parser)
+	return runner.Run(ctx, parser)
}

Caller changes are mechanical (pass the context received in the previous comment).


98-99: Doc‑comment mismatch

The comment references getNextShellLevel (lower‑case) while the exported symbol is GetNextShellLevel.
Small rename keeps go doc output tidy.

-// getNextShellLevel increments the ATMOS_SHLVL ...
+// GetNextShellLevel increments the ATMOS_SHLVL ...
cmd/cmd_utils.go (1)

178-187: Mixed exit paths

Most new branches exit with log.Fatal, yet some older code (e.g. line 140) still uses u.LogErrorAndExit.
Using both patterns inside the same command package is slightly jarring and complicates grepping for exit points.
Might be worth standardising on one helper (even if the wider refactor is pending).

🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 180-180:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 186-186:
deep-exit: calls to log.Fatal only in main() or init() functions

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 809cb01 and 979b8f5.

📒 Files selected for processing (2)
  • cmd/cmd_utils.go (11 hunks)
  • pkg/utils/yaml_func_exec.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
cmd/cmd_utils.go (4)
Learnt from: aknysh
PR: cloudposse/atmos#1185
File: internal/exec/yaml_func_store.go:71-72
Timestamp: 2025-04-04T02:03:21.906Z
Learning: The codebase currently uses `log.Fatal` for error handling in library functions, which terminates the program. There is a plan to refactor this approach in a separate PR to improve API design by returning error messages instead of terminating execution.
Learnt from: aknysh
PR: cloudposse/atmos#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: samtholiya
PR: cloudposse/atmos#1077
File: pkg/downloader/custom_github_detector.go:0-0
Timestamp: 2025-02-21T20:56:05.539Z
Learning: The `github.com/charmbracelet/log` package should be imported with the alias `log` according to the project's import alias configuration.
Learnt from: samtholiya
PR: cloudposse/atmos#1077
File: pkg/downloader/custom_github_detector_test.go:0-0
Timestamp: 2025-02-21T20:56:20.761Z
Learning: The `github.com/charmbracelet/log` package should be imported with the alias `log`, not `clog`.
🧬 Code Graph Analysis (2)
cmd/cmd_utils.go (3)
internal/exec/shell_utils.go (1)
  • ExecuteShell (77-98)
pkg/utils/yaml_func_exec.go (1)
  • ExecuteShellAndReturnOutput (48-75)
internal/exec/template_utils.go (1)
  • ProcessTmpl (24-61)
pkg/utils/yaml_func_exec.go (1)
pkg/utils/yaml_utils.go (1)
  • AtmosYamlFuncExec (19-19)
🪛 GitHub Check: golangci-lint
cmd/cmd_utils.go

[failure] 180-180:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 186-186:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 336-336:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 342-342:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 361-361:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 371-371:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 381-381:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 398-398:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 406-406:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 413-413:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 420-420:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 435-435:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 442-442:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 504-504:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 524-524:
deep-exit: calls to log.Fatal only in main() or init() functions


[failure] 664-664:
deep-exit: calls to log.Fatal only in main() or init() functions

pkg/utils/yaml_func_exec.go

[failure] 100-100:
use of os.Getenv forbidden because "Use viper.BindEnv for new environment variables instead of os.Getenv"

⏰ Context from checks skipped due to timeout of 90000ms (15)
  • GitHub Check: [mock-macos] tests/fixtures/scenarios/complete
  • GitHub Check: [mock-windows] tests/fixtures/scenarios/complete
  • GitHub Check: [mock-windows] examples/demo-vendoring
  • GitHub Check: [mock-windows] examples/demo-component-versions
  • GitHub Check: [mock-windows] examples/demo-atlantis
  • GitHub Check: [mock-linux] tests/fixtures/scenarios/complete
  • GitHub Check: [mock-linux] examples/demo-vendoring
  • GitHub Check: [mock-linux] examples/demo-atlantis
  • GitHub Check: Acceptance Tests (macos-latest, macos)
  • GitHub Check: [k3s] demo-helmfile
  • GitHub Check: [lint] Dockerfile
  • GitHub Check: Acceptance Tests (windows-latest, windows)
  • GitHub Check: Acceptance Tests (ubuntu-latest, linux)
  • GitHub Check: [localstack] demo-localstack
  • GitHub Check: Summary
🔇 Additional comments (1)
pkg/utils/yaml_func_exec.go (1)

39-44: Verify behaviour change on non‑JSON output

ProcessTagExec now hard‑fails when json.Unmarshal can’t parse the command’s stdout.
Previously callers received the raw string, which let them use plain values (e.g. "v1.2.3").
Please double‑check that every existing !exec usage returns valid JSON, otherwise this is a silent breaking change in configuration files.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
tests/test-cases/complete.yaml (1)

18-18: Remove trailing whitespace

Line 18 has extra spaces after the closing quote, which triggers a YAMLLint trailing‑spaces error. Please trim the whitespace to satisfy the linter.

🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 18-18: trailing spaces

(trailing-spaces)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3680473 and 61b721a.

📒 Files selected for processing (1)
  • tests/test-cases/complete.yaml (1 hunks)
🧰 Additional context used
🪛 YAMLlint (1.35.1)
tests/test-cases/complete.yaml

[error] 18-18: trailing spaces

(trailing-spaces)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Build (windows-latest, windows)
  • GitHub Check: Build (ubuntu-latest, linux)
  • GitHub Check: website-deploy-previ 3D11 ew
  • GitHub Check: Lint (golangci)
  • GitHub Check: Analyze (go)
  • GitHub Check: Summary
🔇 Additional comments (1)
tests/test-cases/complete.yaml (1)

15-17: Correctly updated error messages

The stderr assertions now reflect the new infinite‐recursion check with explicit current and max depth values, matching the refactor in yaml_func_exec.go.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
tests/test-cases/complete.yaml (1)

16-16: Minor nit: trim trailing spaces.
There are extra spaces after the closing quote on this line, which triggers a trailing-spaces error in yamllint. You can safely delete them to keep the YAML clean.

🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 16-16: trailing spaces

(trailing-spaces)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 61b721a and 0f15ed0.

📒 Files selected for processing (1)
  • tests/test-cases/complete.yaml (1 hunks)
🧰 Additional context used
🪛 YAMLlint (1.35.1)
tests/test-cases/complete.yaml

[error] 16-16: trailing spaces

(trailing-spaces)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Analyze (go)
  • GitHub Check: Build (windows-latest, windows)
  • GitHub Check: Lint (golangci)
  • GitHub Check: Build (ubuntu-latest, linux)
  • GitHub Check: website-deploy-preview
  • GitHub Check: Summary
🔇 Additional comments (1)
tests/test-cases/complete.yaml (1)

15-15: Heads-up: check the regex escape.
In single‑quoted YAML strings, backslashes are preserved literally, so '\?' becomes \? at runtime. Make sure your test harness treats these entries as regular expressions—so \? actually matches the question mark—instead of looking for a backslash-plus-?.

< 10000 span class="Button-content"> Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f15ed0 and 14dff43.

📒 Files selected for processing (2)
  • pkg/utils/string_utils.go (1 hunks)
  • pkg/utils/string_utils_test.go (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
pkg/utils/string_utils_test.go (1)
pkg/utils/string_utils.go (1)
  • SplitStringByDelimiter (24-43)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build (windows-latest, windows)
  • GitHub Check: Summary
🔇 Additional comments (5)
pkg/utils/string_utils.go (3)

23-23: Comment improvement looks good.

The updated documentation now properly ends with a period, maintaining consistent comment formatting.


27-27: Good enhancement for field processing.

Setting TrimLeadingSpace to true improves the robustness of the function by handling extra spaces in the input, which is particularly valuable for YAML function argument parsing.


34-42: Solid improvement for handling multiple delimiters.

This filtering logic effectively removes empty strings that could result from consecutive delimiters in the input. The implementation is clean and efficient, using a pre-allocated slice with the correct capacity.

pkg/utils/string_utils_test.go (2)

5-69: Well-structured table-driven tests.

Your test cases comprehensively cover the function's behavior including:

  • Basic splitting
  • Handling quoted substrings
  • Edge cases (empty input, no delimiter)
  • Special case with multiple spaces
  • Error handling

This provides robust coverage for the enhanced SplitStringByDelimiter function.


71-82: Helpful helper function.

The equalSlices function provides better error messages than a simple deep equality check by logging the exact index and values that don't match. This makes test failures much easier to debug.

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/utils/yaml_func_exec.go (1)

98-101: context.TODO() still in library code

We previously discussed replacing the placeholder with a real context passed from the caller (or at least context.Background()), so downstream code can inject cancellation/timeout. Re‑raising here in case it was missed.

🧹 Nitpick comments (1)
pkg/utils/yaml_env_test.go (1)

41-63: Restore ATMOS_SHLVL after each test case

Mutating process‑wide env vars can leak state to subsequent tests running in the same process (or in parallel later on). Capture the original value and restore it with t.Cleanup for isolation.

 		t.Run(tt.name, func(t *testing.T) {
-			// Set up the environment variable
+			// Preserve original value and restore after the sub‑test
+			orig := os.Getenv("ATMOS_SHLVL")
+			t.Cleanup(func() {                   // restore once the sub‑test finishes
+				if orig == "" {
+					os.Unsetenv("ATMOS_SHLVL")
+				} else {
+					os.Setenv("ATMOS_SHLVL", orig)
+				}
+			})
+
+			// Set up the environment variable for this case
 			if tt.initialEnv != "" {
 				os.Setenv("ATMOS_SHLVL", tt.initialEnv)
 			} else {
 				os.Unsetenv("ATMOS_SHLVL")
 			}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 14dff43 and b196d94.

📒 Files selected for processing (2)
  • pkg/utils/yaml_env_test.go (1 hunks)
  • pkg/utils/yaml_func_exec.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
pkg/utils/yaml_func_exec.go (2)
Learnt from: osterman
PR: cloudposse/atmos#1202
File: pkg/utils/yaml_func_exec.go:104-104
Timestamp: 2025-04-23T15:02:50.237Z
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.
Learnt from: osterman
PR: cloudposse/atmos#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.
🧬 Code Graph Analysis (2)
pkg/utils/yaml_env_test.go (1)
pkg/utils/yaml_func_exec.go (1)
  • GetNextShellLevel (104-124)
pkg/utils/yaml_func_exec.go (1)
pkg/utils/yaml_utils.go (1)
  • AtmosYamlFuncExec (19-19)
🪛 golangci-lint (1.64.8)
pkg/utils/yaml_func_exec.go

[error] 107-107: Error return value of v.BindEnv is not checked

(errcheck)

🪛 GitHub Check: golangci-lint
pkg/utils/yaml_func_exec.go

[failure] 107-107:
Error return value of v.BindEnv is not checked

⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: Hadolint
  • GitHub Check: [mock-macos] examples/demo-vendoring
  • GitHub Check: [mock-macos] examples/demo-context
  • GitHub Check: [localstack] demo-localstack
  • GitHub Check: Acceptance Tests (macos-latest, macos)
  • GitHub Check: [k3s] demo-helmfile
  • GitHub Check: [lint] Dockerfile
  • GitHub Check: Acceptance Tests (ubuntu-latest, linux)
  • GitHub Check: Summary

@haitham911 haitham911 added the minor New features that do not break anything label Apr 23, 2025
@haitham911 haitham911 requested a review from osterman April 24, 2025 01:22
@haitham911 haitham911 changed the title Refactor Execution YAML Function Processing, Support !exec directive in configuration files Refactor Execution YAML Function Processing, Support !env, !exec,!include,!repo-root directive in configuration files Apr 24, 2025
@aknysh aknysh merged commit 0264c72 into main Apr 26, 2025
51 checks passed
@aknysh aknysh deleted the implement-yaml-func-config branch April 26, 2025 13:59
Copy link

These changes were released in v1.173.0.

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
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
0