Skip to content

Conversation

@aknysh
Copy link
Member

@aknysh aknysh commented Jan 3, 2026

what

  • Fixed !terraform.state YAML function to use correct state file paths when Terraform workspaces are disabled (workspaces_enabled: false)
  • For S3 backend: state is now correctly read from <key> instead of <workspace_key_prefix>/default/<key>
  • For Local backend: state is now correctly read from terraform.tfstate instead of terraform.tfstate.d/default/terraform.tfstate
  • Added comprehensive unit tests for both S3 and local backend default workspace handling
  • Added integration tests with a new test fixture for workspaces-disabled scenario

why

  • When workspaces_enabled: false is set in atmos.yaml, Atmos sets the workspace to "default" but Terraform stores state at different paths for the default workspace vs named workspaces:
    • S3 backend: Default workspace stores state at <key>, not <workspace_key_prefix>/default/<key>
    • Local backend: Default workspace stores state at terraform.tfstate, not terraform.tfstate.d/default/terraform.tfstate
  • The !terraform.state YAML function was looking in the wrong locations, causing it to fail to find state files when workspaces are disabled
  • This aligns with Terraform's documented behavior for backend state storage

references

Summary by CodeRabbit

  • Bug Fixes

    • Fixed Terraform state lookup so default/disabled workspaces read state from the root terraform.tfstate location across backends (local, S3, Azure).
  • Tests

    • Added unit and end-to-end tests covering state reads and path resolution for default, empty, and named workspaces.
  • Documentation

    • Added fixtures and a blog post explaining behavior and upgrade notes when workspaces are disabled.

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

@aknysh aknysh requested a review from a team as a code owner January 3, 2026 04:14
@aknysh aknysh added the patch A minor, backward compatible change label Jan 3, 2026
@github-actions github-actions bot added the size/l Large size PR label Jan 3, 2026
@aknysh aknysh self-assigned this Jan 3, 2026
@github-actions
Copy link

github-actions bot commented Jan 3, 2026

Dependency Review

✅ No vulnerabilities or license issues found.

Scanned Files

None

@aknysh
Copy link
Member Author

aknysh commented Jan 3, 2026

@coderabbitai full review please

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 3, 2026

@aknysh: I'll conduct a full review of this PR, focusing on the Terraform state path fix for disabled workspaces, including the changes to S3 and local backend handling, along with the new tests.


🧠 Learnings used
Learnt from: Listener430
Repo: cloudposse/atmos PR: 1149
File: tests/snapshots/TestCLICommands_atmos_vendor_pull_ssh.stderr.golden:7-7
Timestamp: 2025-03-18T12:26:25.329Z
Learning: In the Atmos project, typos or inconsistencies in test snapshot files (such as "terrafrom" instead of "terraform") may be intentional as they capture the exact output of commands and should not be flagged as issues requiring correction.

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.

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.

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.
✅ Actions performed

Full review triggered.

…workspace

The test was placing state files at terraform.tfstate.d/default/terraform.tfstate,
but when workspace is "default", state should be at terraform.tfstate.

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

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 3, 2026

📝 Walkthrough

Walkthrough

Adjusts Terraform state path resolution so the default/disabled workspace uses the root state path (e.g., terraform.tfstate or the backend key) while named workspaces continue to use workspace-prefixed locations; adds tests, fixtures, and a blog post documenting the fix.

Changes

Cohort / File(s) Summary
Local backend logic
internal/terraform_backend/terraform_backend_local.go
Compute state path based on workspace: root terraform.tfstate for default/empty workspace; terraform.tfstate.d/<workspace>/terraform.tfstate for named workspaces.
Local backend tests
internal/terraform_backend/terraform_backend_local_test.go
Added tests for default/empty and named workspace paths; verifies wrong-location state is ignored.
S3 backend logic
internal/terraform_backend/terraform_backend_s3.go
Make S3 key workspace-aware: use key for default/empty workspace, else use workspace_key_prefix/.../<workspace>/<key> for named workspaces.
S3 backend tests
internal/terraform_backend/terraform_backend_s3_test.go
Added mock S3 client and tests validating requested S3 object key across default/named workspace and prefix permutations.
Terraform state YAML function tests
internal/exec/yaml_func_terraform_state_workspaces_disabled_test.go
New integration tests exercising !terraform.state with workspaces disabled; validates outputs and verifies state file location is root (not workspace path).
Fixtures: scenario and stack
tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml, .../stacks/deploy/test.yaml
New scenario and stack fixtures representing workspaces-disabled deployment with two components and !terraform.state lookups.
Test utils adjustments
internal/terraform_backend/terraform_backend_utils_test.go
Tests updated to create/read state files according to default vs named workspace layout (root vs terraform.tfstate.d/<ws>/terraform.tfstate).
Docs / Blog
website/blog/2026-01-02-terraform-state-workspaces-disabled-fix.mdx
New blog post describing the fix, affected backends, examples, and upgrade notes.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant YAMLFunc as !terraform.state
    participant BackendMgr as Backend Reader
    participant Storage as State Storage

    User->>YAMLFunc: Request state output for component
    YAMLFunc->>BackendMgr: ReadTerraformBackend(componentPath, backendConfig, workspace, key)

    alt workspace is default / empty
        BackendMgr->>BackendMgr: Resolve root path/key\n(local: terraform.tfstate, s3: key)
        BackendMgr->>Storage: Read from root path/key
    else workspace is named
        BackendMgr->>BackendMgr: Resolve workspace-prefixed path\n(local: terraform.tfstate.d/<ws>/..., s3: prefix/<ws>/key)
        BackendMgr->>Storage: Read from workspace path
    end

    Storage-->>BackendMgr: Return state bytes (or nil)
    BackendMgr-->>YAMLFunc: Parsed state outputs
    YAMLFunc-->>User: Return requested output value
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

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 63.64% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly and concisely summarizes the main change: fixing Terraform state path handling when workspaces are disabled.
Linked Issues check ✅ Passed PR fully addresses issue #1920 by fixing state path logic for both S3 and local backends to align with Terraform behavior when workspaces are disabled.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the !terraform.state function for disabled workspaces: backend logic updates, tests, fixtures, and documentation.
✨ 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 aknysh/fix-terraform-state-auth

📜 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 4572f19 and c3cc771.

📒 Files selected for processing (2)
  • internal/terraform_backend/terraform_backend_utils_test.go
  • website/blog/2026-01-02-terraform-state-workspaces-disabled-fix.mdx
🧰 Additional context used
📓 Path-based instructions (4)
website/**

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

website/**: Update website documentation in the website/ directory when adding new features, ensure consistency between CLI help text and website documentation, and follow the website's documentation structure and style
Keep website code in the website/ directory, follow the existing website architecture and style, and test website changes locally before committing
Keep CLI documentation and website documentation in sync and document new features on the website with examples and use cases

Files:

  • website/blog/2026-01-02-terraform-state-workspaces-disabled-fix.mdx
website/blog/**/*.mdx

📄 CodeRabbit inference engine (CLAUDE.md)

Blog posts MUST use .mdx with YAML front matter (slug, title, authors, tags), <!--truncate--> after intro. Only use tags defined in website/blog/tags.yml (never invent new tags). Only use authors from website/blog/authors.yml or add new entry. Valid tags: feature, enhancement, bugfix, dx, breaking-change, security, documentation, deprecation, core

Files:

  • website/blog/2026-01-02-terraform-state-workspaces-disabled-fix.mdx
**/*.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: All comments must end with periods (enforced by godot linter)
Never delete existing comments without a very strong reason; preserve helpful comments explaining why/how/what/where, and update comments to match code when refactoring
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
Use flags.NewStandardParser() for command-specific flags; NEVER call viper.BindEnv() or viper.BindPFlag() directly (enforced by Forbidigo)
All errors MUST be wrapped using static errors defined in errors/errors.go; use errors.Join for combining multiple errors, fmt.Errorf with %w for adding string context, error builder for complex errors, and errors.Is() for error checking. Never use dynamic errors directly
Define interfaces for all major funct...

Files:

  • internal/terraform_backend/terraform_backend_utils_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: Use cmd.NewTestKit(t) for cmd tests to auto-clean RootCmd state (flags, args)
Test behavior, not implementation; never test stub functions; avoid tautological tests; use errors.Is() for error checking; remove always-skipped tests
Prefer unit tests with mocks over integration tests; use interfaces and dependency injection for testability; use table-driven tests for comprehensive coverage; target >80% coverage; skip tests gracefully with helpers from tests/test_preconditions.go
Never manually edit golden snapshot files under tests/test-cases/ or tests/testdata/; always use -regenerate-snapshots flag. Never use pipe redirection when running tests as it breaks TTY detection

Files:

  • internal/terraform_backend/terraform_backend_utils_test.go
🧠 Learnings (17)
📓 Common learnings
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.
📚 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:

  • website/blog/2026-01-02-terraform-state-workspaces-disabled-fix.mdx
📚 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:

  • website/blog/2026-01-02-terraform-state-workspaces-disabled-fix.mdx
📚 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/terraform_backend/terraform_backend_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/terraform_backend/terraform_backend_utils_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:

  • internal/terraform_backend/terraform_backend_utils_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/terraform_backend/terraform_backend_utils_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/terraform_backend/terraform_backend_utils_test.go
📚 Learning: 2024-12-25T20:28:47.526Z
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.

Applied to files:

  • internal/terraform_backend/terraform_backend_utils_test.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:

  • internal/terraform_backend/terraform_backend_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: 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:

  • internal/terraform_backend/terraform_backend_utils_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:

  • internal/terraform_backend/terraform_backend_utils_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:

  • internal/terraform_backend/terraform_backend_utils_test.go
📚 Learning: 2026-01-01T18:25:25.942Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-01T18:25:25.942Z
Learning: Applies to **/*_test.go : Test behavior, not implementation; never test stub functions; avoid tautological tests; use `errors.Is()` for error checking; remove always-skipped tests

Applied to files:

  • internal/terraform_backend/terraform_backend_utils_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:

  • internal/terraform_backend/terraform_backend_utils_test.go
📚 Learning: 2024-12-17T07:10:26.295Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 863
File: internal/exec/stack_utils.go:201-223
Timestamp: 2024-12-17T07:10:26.295Z
Learning: Unit tests for remote state backend functionality are located in the `examples/tests` directory.

Applied to files:

  • internal/terraform_backend/terraform_backend_utils_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:

  • internal/terraform_backend/terraform_backend_utils_test.go
🪛 LanguageTool
website/blog/2026-01-02-terraform-state-workspaces-disabled-fix.mdx

[typographical] ~19-~19: The word ‘When’ starts a question. Add a question mark (“?”) at the end of the sentence.
Context: ...mos sets the workspace name to default. However, Terraform stores state differe...

(WRB_QUESTION_MARK)


[style] ~88-~88: Using many exclamation marks might seem excessive (in this case: 4 exclamation marks for a text that’s 1993 characters long)
Context: ...configuration changes are required. The !terraform.state function will automatic...

(EN_EXCESSIVE_EXCLAMATION)

⏰ 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 (2)
website/blog/2026-01-02-terraform-state-workspaces-disabled-fix.mdx (1)

1-96: Well-documented fix with clear examples.

The blog post effectively explains the problem, solution, and upgrade path. The table comparing backend behaviors is particularly helpful, and the examples clearly demonstrate both the issue and the fix.

internal/terraform_backend/terraform_backend_utils_test.go (1)

279-302: Workspace-aware path logic correctly implemented.

The test setup now properly handles both default and named workspaces:

  • Default workspace: terraform.tfstate at the component root
  • Named workspaces: terraform.tfstate.d/<workspace>/terraform.tfstate

The logic correctly checks for both empty string and "default" workspace, uses filepath.Join for cross-platform compatibility, and includes helpful comments explaining the path decisions.


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

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
internal/terraform_backend/terraform_backend_s3_test.go (1)

297-380: Comprehensive test coverage with one minor suggestion.

The table-driven tests thoroughly cover default workspace, named workspace, and various workspace_key_prefix scenarios. Well done.

At line 372, consider checking for errors even though the test focuses on path verification:

-			_, _ = tb.ReadTerraformBackendS3Internal(client, &componentSections, &backend)
+			_, err := tb.ReadTerraformBackendS3Internal(client, &componentSections, &backend)
+			assert.NoError(t, err)

This ensures the function succeeds and doesn't mask unexpected failures.

📜 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 1d39233 and 03a4dae.

📒 Files selected for processing (7)
  • internal/exec/yaml_func_terraform_state_workspaces_disabled_test.go
  • internal/terraform_backend/terraform_backend_local.go
  • internal/terraform_backend/terraform_backend_local_test.go
  • internal/terraform_backend/terraform_backend_s3.go
  • internal/terraform_backend/terraform_backend_s3_test.go
  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml
  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/stacks/deploy/test.yaml
🧰 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: All comments must end with periods (enforced by godot linter)
Never delete existing comments without a very strong reason; preserve helpful comments explaining why/how/what/where, and update comments to match code when refactoring
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
Use flags.NewStandardParser() for command-specific flags; NEVER call viper.BindEnv() or viper.BindPFlag() directly (enforced by Forbidigo)
All errors MUST be wrapped using static errors defined in errors/errors.go; use errors.Join for combining multiple errors, fmt.Errorf with %w for adding string context, error builder for complex errors, and errors.Is() for error checking. Never use dynamic errors directly
Define interfaces for all major funct...

Files:

  • internal/terraform_backend/terraform_backend_s3.go
  • internal/terraform_backend/terraform_backend_local_test.go
  • internal/exec/yaml_func_terraform_state_workspaces_disabled_test.go
  • internal/terraform_backend/terraform_backend_local.go
  • internal/terraform_backend/terraform_backend_s3_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: Use cmd.NewTestKit(t) for cmd tests to auto-clean RootCmd state (flags, args)
Test behavior, not implementation; never test stub functions; avoid tautological tests; use errors.Is() for error checking; remove always-skipped tests
Prefer unit tests with mocks over integration tests; use interfaces and dependency injection for testability; use table-driven tests for comprehensive coverage; target >80% coverage; skip tests gracefully with helpers from tests/test_preconditions.go
Never manually edit golden snapshot files under tests/test-cases/ or tests/testdata/; always use -regenerate-snapshots flag. Never use pipe redirection when running tests as it breaks TTY detection

Files:

  • internal/terraform_backend/terraform_backend_local_test.go
  • internal/exec/yaml_func_terraform_state_workspaces_disabled_test.go
  • internal/terraform_backend/terraform_backend_s3_test.go
internal/exec/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

New templates should support Go templating with FuncMap() from internal/exec/template_funcs.go

Files:

  • internal/exec/yaml_func_terraform_state_workspaces_disabled_test.go
🧠 Learnings (31)
📓 Common learnings
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.
📚 Learning: 2026-01-01T18:25:25.942Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-01T18:25:25.942Z
Learning: Stack pipeline: Load atmos.yaml → process imports/inheritance → apply overrides → render templates → generate config. Templates use Go templates + Gomplate with `atmos.Component()`, `!terraform.state`, `!terraform.output`, store integration

Applied to files:

  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml
  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/stacks/deploy/test.yaml
📚 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:

  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml
  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/stacks/deploy/test.yaml
📚 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:

  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml
  • internal/exec/yaml_func_terraform_state_workspaces_disabled_test.go
  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/stacks/deploy/test.yaml
📚 Learning: 2025-03-18T12:26:25.329Z
Learnt from: Listener430
Repo: cloudposse/atmos PR: 1149
File: tests/snapshots/TestCLICommands_atmos_vendor_pull_ssh.stderr.golden:7-7
Timestamp: 2025-03-18T12:26:25.329Z
Learning: In the Atmos project, typos or inconsistencies in test snapshot files (such as "terrafrom" instead of "terraform") may be intentional as they capture the exact output of commands and should not be flagged as issues requiring correction.

Applied to files:

  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml
  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/stacks/deploy/test.yaml
📚 Learning: 2025-01-25T03:51:57.689Z
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.

Applied to files:

  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml
📚 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:

  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml
📚 Learning: 2025-12-24T04:29:23.938Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1891
File: pkg/ci/terraform/templates/apply.md:17-17
Timestamp: 2025-12-24T04:29:23.938Z
Learning: In the cloudposse/atmos repository, Terraform CI templates (pkg/ci/terraform/templates/*.md) are rendered using TerraformTemplateContext (defined in pkg/ci/terraform/context.go), not the base ci.TemplateContext. TerraformTemplateContext provides top-level fields: .Resources (ci.ResourceCounts), .HasChanges() method, and .HasDestroy field, which are correctly accessed directly in templates without a .Result prefix.

Applied to files:

  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml
  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/stacks/deploy/test.yaml
📚 Learning: 2026-01-01T18:25:25.942Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-01T18:25:25.942Z
Learning: Atmos uses Gitleaks pattern library (120+ patterns) for secret masking. Disable masking with: `atmos terraform plan --mask=false`

Applied to files:

  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml
📚 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:

  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml
  • internal/terraform_backend/terraform_backend_s3.go
  • internal/exec/yaml_func_terraform_state_workspaces_disabled_test.go
📚 Learning: 2026-01-01T18:25:25.942Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-01T18:25:25.942Z
Learning: Configuration loading precedence: CLI flags → ENV vars → config files → defaults (use Viper). Environment variables require ATMOS_ prefix via `viper.BindEnv("ATMOS_VAR", ...)`

Applied to files:

  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml
📚 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:

  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml
📚 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:

  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml
📚 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:

  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml
📚 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:

  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml
📚 Learning: 2025-10-07T00:25:16.333Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1498
File: website/src/components/Screengrabs/atmos-terraform-metadata--help.html:25-55
Timestamp: 2025-10-07T00:25:16.333Z
Learning: In Atmos CLI, subcommands inherit flags from their parent commands via Cobra's command inheritance. For example, `atmos terraform metadata --help` shows `--affected` and related flags inherited from the parent `terraform` command (defined in cmd/terraform.go), even though the metadata subcommand doesn't explicitly define these flags. This is expected Cobra behavior and auto-generated help screengrabs accurately reflect this inheritance.

Applied to files:

  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml
📚 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/terraform_backend/terraform_backend_s3.go
  • internal/terraform_backend/terraform_backend_local_test.go
  • internal/exec/yaml_func_terraform_state_workspaces_disabled_test.go
  • internal/terraform_backend/terraform_backend_local.go
  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/stacks/deploy/test.yaml
  • internal/terraform_backend/terraform_backend_s3_test.go
📚 Learning: 2024-12-25T20:28:19.618Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 887
File: internal/exec/workflow_utils.go:167-169
Timestamp: 2024-12-25T20:28:19.618Z
Learning: The user plans to revert the change from `path.Join` to `filepath.Join` in this PR due to testing gaps and will open a new PR to safely handle the migration without breaking `main`.

Applied to files:

  • internal/terraform_backend/terraform_backend_s3.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/terraform_backend/terraform_backend_s3.go
  • internal/terraform_backend/terraform_backend_local_test.go
  • internal/exec/yaml_func_terraform_state_workspaces_disabled_test.go
  • internal/terraform_backend/terraform_backend_local.go
  • tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/stacks/deploy/test.yaml
📚 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:

  • internal/terraform_backend/terraform_backend_s3.go
  • internal/terraform_backend/terraform_backend_local_test.go
  • internal/exec/yaml_func_terraform_state_workspaces_disabled_test.go
  • internal/terraform_backend/terraform_backend_local.go
  • internal/terraform_backend/terraform_backend_s3_test.go
📚 Learning: 2026-01-01T18:25:25.942Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-01T18:25:25.942Z
Learning: Applies to internal/exec/**/*.go : New templates should support Go templating with `FuncMap()` from `internal/exec/template_funcs.go`

Applied to files:

  • internal/exec/yaml_func_terraform_state_workspaces_disabled_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/yaml_func_terraform_state_workspaces_disabled_test.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:

  • internal/exec/yaml_func_terraform_state_workspaces_disabled_test.go
  • internal/terraform_backend/terraform_backend_local.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/yaml_func_terraform_state_workspaces_disabled_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:

  • internal/exec/yaml_func_terraform_state_workspaces_disabled_test.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:

  • internal/exec/yaml_func_terraform_state_workspaces_disabled_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/yaml_func_terraform_state_workspaces_disabled_test.go
📚 Learning: 2025-12-17T20:55:47.884Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1874
File: internal/exec/describe_affected_utils_test.go:436-468
Timestamp: 2025-12-17T20:55:47.884Z
Learning: In the Atmos codebase, there are two different paths for the `locked` flag: (1) filtering logic in `internal/exec/component_utils.go` (`isComponentLocked()`) reads from `componentSection["metadata"]["locked"]` to determine which components to include/exclude, and (2) extraction/rendering logic in `pkg/list/extract/affected.go` reads from `settings.metadata.locked` to display the locked status in output. Tests for filtering behavior should use `metadata.locked`.

Applied to files:

  • internal/exec/yaml_func_terraform_state_workspaces_disabled_test.go
📚 Learning: 2024-10-27T04:34:08.011Z
Learnt from: haitham911
Repo: cloudposse/atmos PR: 727
File: internal/exec/terraform_clean.go:183-187
Timestamp: 2024-10-27T04:34:08.011Z
Learning: In the `getStackTerraformStateFolder` function, it's acceptable and not an error if no Terraform state folders are found for a stack.

Applied to files:

  • internal/exec/yaml_func_terraform_state_workspaces_disabled_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:

  • internal/exec/yaml_func_terraform_state_workspaces_disabled_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/terraform_backend/terraform_backend_s3_test.go
🧬 Code graph analysis (4)
internal/terraform_backend/terraform_backend_s3.go (1)
internal/terraform_backend/terraform_backend_utils.go (2)
  • GetTerraformWorkspace (16-23)
  • GetBackendAttribute (56-63)
internal/terraform_backend/terraform_backend_local_test.go (3)
pkg/schema/schema.go (1)
  • AtmosConfiguration (54-100)
internal/terraform_backend/terraform_backend_local.go (1)
  • ReadTerraformBackendLocal (22-55)
internal/terraform_backend/terraform_backend_utils.go (1)
  • ProcessTerraformStateFile (98-118)
internal/exec/yaml_func_terraform_state_workspaces_disabled_test.go (2)
internal/exec/terraform.go (1)
  • ExecuteTerraform (86-759)
internal/exec/describe_component.go (1)
  • ExecuteDescribeComponentParams (203-210)
internal/terraform_backend/terraform_backend_s3_test.go (1)
internal/terraform_backend/terraform_backend_s3.go (1)
  • ReadTerraformBackendS3Internal (115-196)
⏰ 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 (10)
tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/atmos.yaml (1)

1-41: LGTM! Well-documented test fixture.

The configuration properly sets workspaces_enabled: false and includes clear comments explaining the expected behavior for both local and S3 backends when workspaces are disabled.

internal/terraform_backend/terraform_backend_s3.go (1)

123-143: Excellent fix with proper documentation.

The workspace-aware path logic correctly implements Terraform's S3 backend behavior. The use of path.Join (not filepath.Join) is appropriate here since S3 paths always use forward slashes regardless of OS. The nolint directive is well-justified.

tests/fixtures/scenarios/atmos-terraform-state-yaml-function-workspaces-disabled/stacks/deploy/test.yaml (1)

1-39: LGTM! Clear test fixture with helpful documentation.

The fixture properly demonstrates the workspaces-disabled scenario with component-2 referencing component-1's state via !terraform.state. The comments clearly explain the expected behavior for both local and S3 backends.

internal/terraform_backend/terraform_backend_s3_test.go (1)

273-295: LGTM! Clean mock implementation.

The mock properly captures the requested S3 key for path verification and returns valid state data.

internal/terraform_backend/terraform_backend_local.go (1)

16-42: Excellent implementation with clear documentation.

The workspace-aware path logic correctly matches Terraform's local backend behavior. The use of filepath.Join is appropriate for local filesystem paths, and the conditional logic properly handles default vs. named workspaces.

internal/terraform_backend/terraform_backend_local_test.go (2)

102-179: Excellent test coverage for workspace-aware path resolution.

The table-driven test validates all three critical scenarios:

  • Default workspace reading from root terraform.tfstate
  • Empty workspace (treated as default)
  • Named workspace reading from terraform.tfstate.d/<workspace>/terraform.tfstate

The implementation follows Go best practices with proper test isolation, cross-platform path handling, and clear assertions.


181-217: Strong negative test case prevents regression.

This test ensures the implementation correctly ignores state files at the wrong location (terraform.tfstate.d/default/) when the workspace is "default". This validation is crucial for confirming the fix aligns with Terraform's documented behavior.

internal/exec/yaml_func_terraform_state_workspaces_disabled_test.go (3)

26-124: Comprehensive integration test validates end-to-end workflow.

This test exercises the complete flow:

  1. Deploys component-1 to create Terraform state
  2. Verifies workspaces are disabled in configuration
  3. Validates !terraform.state reads outputs correctly from root path
  4. Confirms component-2 can reference component-1's outputs

The test coverage aligns with the PR objective of fixing state path resolution when workspaces are disabled.


128-194: Focused validation of physical state file location.

This test specifically verifies the state file is created at the correct location (terraform.tfstate) and not at the workspace-prefixed path (terraform.tfstate.d/default/terraform.tfstate) when workspaces are disabled. The explicit positive and negative checks provide strong validation.

Good practice: resolving the absolute path to the mock component before t.Chdir() (line 142) prevents path resolution issues.


68-68: No action needed—t.Chdir() is valid in this project.

The atmos codebase implements a custom extension for t.Chdir() on *testing.T objects with automatic cleanup. The project's custom linter (tools/lintroller/rule_os_chdir.go) actively enforces t.Chdir() over os.Chdir() in test files, confirming this is the intended and supported approach.

@codecov
Copy link

codecov bot commented Jan 3, 2026

Codecov Report

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

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1929      +/-   ##
==========================================
- Coverage   73.66%   73.65%   -0.01%     
==========================================
  Files         746      746              
  Lines       67807    67816       +9     
==========================================
  Hits        49947    49947              
- Misses      14473    14483      +10     
+ Partials     3387     3386       -1     
Flag Coverage Δ
unittests 73.65% <100.00%> (-0.01%) ⬇️

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

Files with missing lines Coverage Δ
...ernal/terraform_backend/terraform_backend_local.go 88.88% <100.00%> (+2.22%) ⬆️
internal/terraform_backend/terraform_backend_s3.go 89.00% <100.00%> (+0.70%) ⬆️

... and 2 files with indirect coverage changes

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 3, 2026

Caution

Review failed

The head commit changed during the review from 03a4dae to 58c5209.

✨ 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 aknysh/fix-terraform-state-auth

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

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
website/blog/2026-01-02-terraform-state-workspaces-disabled-fix.mdx (1)

85-92: Consider adding Azure backend reference.

The upgrade instructions are clear. However, since Azure backend is mentioned in the table and fix section, consider adding a reference to the Azure Backend documentation for completeness.

Proposed addition to References section
 ## References
 
 - [Terraform S3 Backend Documentation](https://developer.hashicorp.com/terraform/language/settings/backends/s3)
 - [Terraform Local Backend Documentation](https://developer.hashicorp.com/terraform/language/settings/backends/local)
+- [Terraform Azure Backend Documentation](https://developer.hashicorp.com/terraform/language/settings/backends/azurerm)
📜 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 03a4dae and 4572f19.

📒 Files selected for processing (3)
  • internal/terraform_backend/terraform_backend_s3_test.go
  • internal/terraform_backend/terraform_backend_utils_test.go
  • website/blog/2026-01-02-terraform-state-workspaces-disabled-fix.mdx
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/terraform_backend/terraform_backend_s3_test.go
🧰 Additional context used
📓 Path-based instructions (4)
website/**

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

website/**: Update website documentation in the website/ directory when adding new features, ensure consistency between CLI help text and website documentation, and follow the website's documentation structure and style
Keep website code in the website/ directory, follow the existing website architecture and style, and test website changes locally before committing
Keep CLI documentation and website documentation in sync and document new features on the website with examples and use cases

Files:

  • website/blog/2026-01-02-terraform-state-workspaces-disabled-fix.mdx
website/blog/**/*.mdx

📄 CodeRabbit inference engine (CLAUDE.md)

Blog posts MUST use .mdx with YAML front matter (slug, title, authors, tags), <!--truncate--> after intro. Only use tags defined in website/blog/tags.yml (never invent new tags). Only use authors from website/blog/authors.yml or add new entry. Valid tags: feature, enhancement, bugfix, dx, breaking-change, security, documentation, deprecation, core

Files:

  • website/blog/2026-01-02-terraform-state-workspaces-disabled-fix.mdx
**/*.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: All comments must end with periods (enforced by godot linter)
Never delete existing comments without a very strong reason; preserve helpful comments explaining why/how/what/where, and update comments to match code when refactoring
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
Use flags.NewStandardParser() for command-specific flags; NEVER call viper.BindEnv() or viper.BindPFlag() directly (enforced by Forbidigo)
All errors MUST be wrapped using static errors defined in errors/errors.go; use errors.Join for combining multiple errors, fmt.Errorf with %w for adding string context, error builder for complex errors, and errors.Is() for error checking. Never use dynamic errors directly
Define interfaces for all major funct...

Files:

  • internal/terraform_backend/terraform_backend_utils_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: Use cmd.NewTestKit(t) for cmd tests to auto-clean RootCmd state (flags, args)
Test behavior, not implementation; never test stub functions; avoid tautological tests; use errors.Is() for error checking; remove always-skipped tests
Prefer unit tests with mocks over integration tests; use interfaces and dependency injection for testability; use table-driven tests for comprehensive coverage; target >80% coverage; skip tests gracefully with helpers from tests/test_preconditions.go
Never manually edit golden snapshot files under tests/test-cases/ or tests/testdata/; always use -regenerate-snapshots flag. Never use pipe redirection when running tests as it breaks TTY detection

Files:

  • internal/terraform_backend/terraform_backend_utils_test.go
🧠 Learnings (14)
📓 Common learnings
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.
Learnt from: aknysh
Repo: cloudposse/atmos PR: 863
File: internal/exec/stack_utils.go:201-223
Timestamp: 2024-12-17T07:10:26.295Z
Learning: Unit tests for remote state backend functionality are located in the `examples/tests` directory.
📚 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:

  • website/blog/2026-01-02-terraform-state-workspaces-disabled-fix.mdx
📚 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:

  • website/blog/2026-01-02-terraform-state-workspaces-disabled-fix.mdx
📚 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/terraform_backend/terraform_backend_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/terraform_backend/terraform_backend_utils_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:

  • internal/terraform_backend/terraform_backend_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: 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:

  • internal/terraform_backend/terraform_backend_utils_test.go
📚 Learning: 2026-01-01T18:25:25.942Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-01T18:25:25.942Z
Learning: Applies to **/*_test.go : Test behavior, not implementation; never test stub functions; avoid tautological tests; use `errors.Is()` for error checking; remove always-skipped tests

Applied to files:

  • internal/terraform_backend/terraform_backend_utils_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:

  • internal/terraform_backend/terraform_backend_utils_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/terraform_backend/terraform_backend_utils_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:

  • internal/terraform_backend/terraform_backend_utils_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:

  • internal/terraform_backend/terraform_backend_utils_test.go
📚 Learning: 2024-12-17T07:10:26.295Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 863
File: internal/exec/stack_utils.go:201-223
Timestamp: 2024-12-17T07:10:26.295Z
Learning: Unit tests for remote state backend functionality are located in the `examples/tests` directory.

Applied to files:

  • internal/terraform_backend/terraform_backend_utils_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:

  • internal/terraform_backend/terraform_backend_utils_test.go
🪛 LanguageTool
website/blog/2026-01-02-terraform-state-workspaces-disabled-fix.mdx

[typographical] ~19-~19: The word ‘When’ starts a question. Add a question mark (“?”) at the end of the sentence.
Context: ...mos sets the workspace name to default. However, Terraform stores state differe...

(WRB_QUESTION_MARK)


[style] ~88-~88: Using many exclamation marks might seem excessive (in this case: 4 exclamation marks for a text that’s 1900 characters long)
Context: ...configuration changes are required. The !terraform.state function will automatic...

(EN_EXCESSIVE_EXCLAMATION)

⏰ 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 (4)
website/blog/2026-01-02-terraform-state-workspaces-disabled-fix.mdx (4)

1-15: Front matter and structure look good.

The blog post follows the required format with proper YAML front matter, the bugfix tag, and the <!--truncate--> marker after the intro.


17-47: Clear problem description with good examples.

The issue is well-explained with concrete examples and a helpful table showing the different backend behaviors.


57-83: Solid example demonstrating the fix.

The example clearly shows how !terraform.state works after the fix with workspaces disabled.


49-55: No changes needed—Azure backend claim is accurate.

The Azure backend implementation already includes workspace-aware logic. The constructAzureBlobPath function correctly handles the default workspace (using <key> as-is) and named workspaces (using <key>env:<workspace> format), and this behavior is covered by existing tests. The blog post's statement that Azure "already worked correctly" is accurate.

@aknysh aknysh requested a review from osterman January 3, 2026 14:08
@aknysh aknysh merged commit 27045e4 into main Jan 3, 2026
57 checks passed
@aknysh aknysh deleted the aknysh/fix-terraform-state-auth branch January 3, 2026 15:28
@github-actions
Copy link

github-actions bot commented Jan 4, 2026

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

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

Labels

patch A minor, backward compatible change size/l Large size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

!terraform.state not aligned with terraform behavior when workspaces are disabled

3 participants