Skip to content

Conversation

@osterman
Copy link
Member

@osterman osterman commented Oct 28, 2025

Summary

This PR fixes a critical bug where atmos terraform plan and other Terraform commands failed to use file-based credentials, while atmos auth whoami and similar commands worked correctly.

The root cause was duplicate credential retrieval code across three methods with inconsistent fallback behavior. Two methods had keyring → identity storage fallback logic, but one (retrieveCachedCredentials) did not, causing Terraform commands to fail when credentials were in files instead of the keyring.

Root Cause Analysis

Three separate code paths retrieved credentials:

  • GetCachedCredentials - Had fallback ✓
  • findFirstValidCachedCredentials - Had fallback ✓
  • retrieveCachedCredentials - NO fallback ✗ (used by Terraform execution)

When users authenticated via AWS SSO, credentials were written to files, not cached in the keyring. Terraform commands would fail because the retrieveCachedCredentials path didn't check identity storage.

Solution

Extracted a shared retrieveCredentialWithFallback method as the single source of truth for credential retrieval:

  • Fast path: Try keyring cache first (immediate)
  • Slow path: Fall back to identity storage if not in keyring (AWS files, etc.)
  • All three code paths now delegate to this single method
  • Ensures consistent behavior across all operations

Changes

  • Added retrieveCredentialWithFallback() method (38 lines)
  • Refactored GetCachedCredentials() - 40% code reduction
  • Refactored findFirstValidCachedCredentials() - 57% code reduction
  • Refactored retrieveCachedCredentials() - Now uses shared method
  • Fixed TestManager_GetCachedCredentials_Paths to use proper test data
  • Added regression test TestManager_retrieveCachedCredentials_TerraformFlow_Regression
  • Added integration test TestRetrieveCachedCredentials_KeyringMiss_IdentityStorageFallback
  • Show active identities image

Testing

✅ All auth tests pass (12/12 test suites)
✅ Regression test reproduces original bug, passes with fix
✅ Integration tests verify fallback behavior works
✅ Code compiles successfully

Impact

✅ Terraform commands now work with file-based credentials
✅ ~110 lines of duplicate code eliminated
✅ Single source of truth for credential retrieval
✅ Impossible to have divergent fallback behavior in future
✅ Consistent behavior across all auth operations

References

This PR addresses the issue where valid authenticated sessions would fail during Terraform execution with "credentials not found" error, even though atmos auth whoami showed valid credentials.

See docs/prd/credential-retrieval-consolidation.md for detailed architectural analysis.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Interactive identity selection when using --identity without a value (CLI and Terraform).
    • New auth logout --all to sign out all identities.
    • ATMOS_IDENTITY env var honored; CLI env outputs add AWS region defaults.
    • Identity list now shows authentication status and credential expiry.
  • Bug Fixes

    • More reliable credential retrieval with keyring → identity-storage fallback.
    • Safer, clearer logout behavior and plain-text summaries.
    • Default files display path updated to ~/.config/atmos.
  • Documentation

    • Help pages and docs updated for interactive identity modes, logout options, and examples.

@osterman osterman requested a review from a team as a code owner October 28, 2025 17:21
@github-actions github-actions bot added the size/l Large size PR label Oct 28, 2025
@github-actions
Copy link

github-actions bot commented Oct 28, 2025

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@osterman osterman added the patch A minor, backward compatible change label Oct 28, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

Warning

Rate limit exceeded

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

⌛ How to resolve this issue?

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

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

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

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

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 3e12eb0 and 8d4c40a.

📒 Files selected for processing (1)
  • CLAUDE.md (1 hunks)
📝 Walkthrough

Walkthrough

Consolidates credential retrieval into a single keyring→identity-storage fallback, adds interactive no-value --identity selection, preserves identity name casing from atmos.yaml, adds AWS credential file locking and per-identity deletion, threads AuthManager into list renderers to show auth status/expiry, and propagates auth-provided env vars to subprocesses.

Changes

Cohort / File(s) Summary
PRD & Architecture
docs/prd/credential-retrieval-consolidation.md
New PRD describing credential retrieval consolidation (keyring → identity storage), architecture options, testing strategy, and implementation plan.
Auth manager & credential fallback
pkg/auth/manager.go, pkg/auth/manager_caching_test.go, pkg/auth/manager_test.go, pkg/auth/manager_case_test.go, pkg/auth/manager_logout.go, pkg/auth/manager_logout_test.go
Add loadCredentialsWithFallback and getChainCredentials, centralize expiry checks (15m buffer), implement case-insensitive resolveIdentityName, change GetDefaultIdentity to GetDefaultIdentity(forceSelect bool), refactor auth/logout flows, and add/update unit tests.
Auth types, hooks & mocks
pkg/auth/types/interfaces.go, pkg/auth/types/mock_interfaces.go, pkg/auth/hooks.go, pkg/auth/hooks_test.go, pkg/auth/providers/mock/identity.go, pkg/auth/providers/mock/identity_test.go
Update AuthManager interface and mocks for GetDefaultIdentity(forceSelect bool), add PostAuthenticateParams.Manager and GetEnvironmentVariables(identityName), implement disk-backed mock identity credential persistence and ErrNoStoredCredentials, and extend tests.
Identity implementations (AWS)
pkg/auth/identities/aws/...
Thread manager/root-provider into identities, resolve root provider for file paths/env, call DeleteIdentity on logout, improve expiration logging/formatting, and adjust file/path behavior.
AWS file manager & locking
pkg/auth/cloud/aws/files.go, pkg/auth/cloud/aws/files_test.go
Add flock-based file locking with retry/timeout, masked access-key logging, per-profile removal helpers, rename CleanupIdentityDeleteIdentity, and update tests.
Keyring & credentials logging
pkg/auth/credentials/keyring_system.go
Probe system keyring availability and add richer debug logging for store/retrieve (including expiry info).
Identity listing & formatting
pkg/auth/list/formatter_table.go, pkg/auth/list/formatter_tree.go, pkg/auth/list/formatter_utils.go, pkg/auth/list/formatter_utils_test.go, pkg/auth/list/formatter_test.go
Thread authManager through renderers, add status indicator and EXPIRES column, implement authStatus utilities and formatting helpers, and add tests.
Environment propagation to subprocesses
internal/exec/component_env.go, internal/exec/terraform.go, internal/exec/helmfile.go, internal/exec/packer.go, internal/exec/*_test.go
Add ConvertComponentEnvSectionToList and call sites to convert ComponentEnvSection map → ComponentEnvList slice (KEY=VALUE) so auth-provided env vars are passed into spawned processes; tests added.
CLI identity selection & flags
cmd/auth.go, cmd/auth_console.go, cmd/auth_env.go, cmd/auth_exec.go, cmd/auth_login.go, cmd/auth_shell.go, cmd/auth_whoami.go, cmd/auth_console_test.go, cmd/auth_integration_test.go
Add IdentityFlagSelectValue and NoOptDefVal for --identity; support three modes (with value, no value → interactive select, omitted) and update call sites to GetDefaultIdentity(forceSelect).
CLI logout command & docs
cmd/auth_logout.go, cmd/auth_logout_test.go, website/docs/cli/commands/auth/logout.mdx
Add --all flag to logout all identities/providers, dry-run support, switch logout messaging to plain text, and add tests/docs.
CLI utilities & env fallback
internal/exec/cli_utils.go, internal/exec/cli_utils_test.go, cmd/terraform_utils.go
Support ATMOS_IDENTITY env fallback when flag absent, require TTY for interactive selection in some flows, wire interactive selection into Terraform flow, update terraformRun return type and tests.
CLI tests & snapshots / normalization
tests/cli_test.go, tests/snapshots/*, tests/test-cases/auth-cli.yaml
Normalize timestamps/durations, add AWS_REGION/AWS_DEFAULT_REGION to snapshots, remove/reduce some log lines, and update help/expiry outputs for determinism.
Logger utility
pkg/logger/log.go, pkg/logger/log_test.go
Add LevelToString(level charm.Level) string helper and unit test.
Config & identity case preservation
pkg/config/load.go, pkg/schema/schema_auth.go
Preserve original identity casing by building Auth.IdentityCaseMap from raw YAML post-load; add schema field and helper.
Misc docs & website
website/docs/cli/commands/auth/*.mdx, website/docs/cli/commands/terraform/usage.mdx, website/blog/2025-10-29-auth-ux-improvements.mdx, CLAUDE.md
Document interactive identity selection, --all logout, env-var behavior, identity-case preservation, and add a blog post summarizing UX improvements.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant CLI as CLI
    participant AuthMgr as AuthManager
    participant Keyring as Keyring
    participant Identity as IdentityStore
    participant Proc as Spawned Process

    User->>CLI: atmos auth env [--identity]
    alt --identity without value
        CLI->>AuthMgr: GetDefaultIdentity(forceSelect=true)
        AuthMgr->>User: Prompt selector
        User-->>AuthMgr: Select identity
    else explicit identity
        CLI->>AuthMgr: use provided identity
    else omitted
        CLI->>AuthMgr: GetDefaultIdentity(forceSelect=false)
    end

    CLI->>AuthMgr: loadCredentialsWithFallback(identity)
    AuthMgr->>Keyring: Retrieve credentials
    alt keyring hit
        Keyring-->>AuthMgr: creds
    else keyring miss
        AuthMgr->>Identity: LoadCredentials(identity)
        Identity-->>AuthMgr: creds / ErrNoStoredCredentials
    end

    AuthMgr->>AuthMgr: validate expiry (15m buffer)
    alt creds valid
        AuthMgr->>AuthMgr: GetEnvironmentVariables(identity)
        AuthMgr-->>CLI: env map
        CLI->>Proc: ConvertComponentEnvSectionToList + inject env
    else expired
        AuthMgr-->>CLI: prompt re-auth or return error
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • pkg/auth/manager.go — new fallback logic, expiry-buffer semantics, and many refactored call sites.
  • pkg/auth/cloud/aws/files.go — file-lock correctness, retry/timeouts, and atomic profile removal semantics.
  • pkg/auth/list/* — threading AuthManager through renderers and validating status/expiry presentation.
  • internal/exec/component_env.go and callers — ensure env conversion and ordering are correct and idempotent.
  • pkg/auth/providers/mock/identity.go — disk-backed credential persistence impacts on test lifecycle and cleanup.

Possibly related PRs

Suggested reviewers

  • aknysh

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.17% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "fix: Consolidate credential retrieval logic to fix terraform auth" directly aligns with the primary objective of this changeset. The PR's core contribution is extracting a unified loadCredentialsWithFallback() method that implements consistent keyring-with-identity-storage fallback logic across three credential retrieval paths, specifically to resolve the bug where Terraform commands failed to access file-based credentials. The title accurately captures both the approach (consolidation) and the outcome (fixing terraform auth). While the changeset includes numerous supporting enhancements (interactive identity selection, logout improvements, case-insensitive identities, environment variables), these are secondary refinements that reinforce the primary auth fix rather than competing focal points.

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

Caution

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

⚠️ Outside diff range comments (1)
pkg/auth/manager.go (1)

202-216: Unify expiration logic with isCredentialValid.

GetCachedCredentials uses creds.IsExpired() while other paths use isCredentialValid (5‑min buffer). Aligning avoids “whoami says valid” while auth treats as near‑expiry.

Suggested change:

-    // Check if credentials are expired.
-    if creds.IsExpired() {
-        log.Debug("Cached credentials are expired", logKeyIdentity, identityName)
-        return nil, fmt.Errorf(errFormatWithString, errUtils.ErrExpiredCredentials, fmt.Sprintf(backtickedFmt, identityName))
-    }
+    // Consistent validation (treat expiring within 5m as invalid).
+    valid, _ := m.isCredentialValid(identityName, creds)
+    if !valid {
+        log.Debug("Cached credentials are expired", logKeyIdentity, identityName)
+        return nil, fmt.Errorf(errFormatWithString, errUtils.ErrExpiredCredentials, fmt.Sprintf(backtickedFmt, identityName))
+    }

Also applies to: 218-225

🧹 Nitpick comments (8)
pkg/auth/manager_caching_test.go (1)

349-351: Strengthen failure assertions.

When expectSuccess is false, also assert the error type once retrieveCredentialWithFallback standardizes “no creds in identity storage” to a static error. This will harden the regression signal.

Example:

-            } else {
-                require.Error(t, err, "retrieveCachedCredentials should fail when no credentials available")
-            }
+            } else {
+                require.Error(t, err, "retrieveCachedCredentials should fail when no credentials available")
+                assert.ErrorIs(t, err, errUtils.ErrNoCredentialsFound)
+            }

(Based on learnings)

pkg/auth/manager_test.go (1)

583-631: Minor: add a negative scenario (optional).

Consider a second subtest with hasCredentials = false to assert the retrieval error, mirroring the positive path.

docs/prd/credential-retrieval-consolidation.md (3)

89-109: Add language to fenced code blocks.

Label unlabeled fences to satisfy MD040 and improve readability.

Example:

-```
+```text
 cmd/auth_whoami.go
   → pkg/auth/hooks.go: AuthWhoami()
 ...
-```
+```

Repeat for the other unlabeled block(s). (From static analysis)

Also applies to: 97-109, 240-246


5-7: Trim wording.

Replace “exact same credentials” with “the same credentials.”


381-399: Phase note may be outdated.

This PR already implements the consolidation; update “Phase 2: Next PR” to reflect completion (e.g., “Completed in PR #1720.”).

pkg/auth/manager.go (3)

507-534: Context and target-step retrieval consistency.

  • Using context.Background() here loses caller cancellation. Prefer threading ctx through findFirstValidCachedCredentials (signature change) and its callers.
  • When this returns len(chain)-1, authenticateHierarchical later re-fetches via keyring only; consider using retrieveCredentialWithFallback there too to avoid a redundant miss then re-fetch later.

632-671: Error semantics and log keys in fallback retriever.

Standardize errors and log keys for consistency with the rest of the package.

Proposed tweaks:

-    log.Debug("Retrieved credentials from keyring", "identity", identityName)
+    log.Debug("Retrieved credentials from keyring", logKeyIdentity, identityName)

-    if !errors.Is(err, credentials.ErrCredentialsNotFound) {
-        return nil, fmt.Errorf("keyring error for identity %q: %w", identityName, err)
-    }
+    if !errors.Is(err, credentials.ErrCredentialsNotFound) {
+        return nil, fmt.Errorf("%w: keyring read failed for %s: %w",
+            errUtils.ErrNoCredentialsFound, fmt.Sprintf(backtickedFmt, identityName), err)
+    }

-    if !exists {
-        return nil, fmt.Errorf("identity %q not found in configuration", identityName)
-    }
+    if !exists {
+        return nil, fmt.Errorf(errFormatWithString, errUtils.ErrIdentityNotFound, fmt.Sprintf(backtickedFmt, identityName))
+    }

-    loadedCreds, loadErr := identity.LoadCredentials(ctx)
+    loadedCreds, loadErr := identity.LoadCredentials(ctx)
     if loadErr != nil {
-        return nil, fmt.Errorf("failed to load credentials from identity storage for %q: %w", identityName, loadErr)
+        return nil, fmt.Errorf("%w: failed to load credentials from identity storage for %s: %w",
+            errUtils.ErrNoCredentialsFound, fmt.Sprintf(backtickedFmt, identityName), loadErr)
     }
     if loadedCreds == nil {
-        return nil, fmt.Errorf("loaded credentials are nil for identity %q", identityName)
+        return nil, fmt.Errorf(errFormatWithString, errUtils.ErrNoCredentialsFound, fmt.Sprintf(backtickedFmt, identityName))
     }

-    log.Debug("Successfully loaded credentials from identity storage", "identity", identityName)
+    log.Debug("Successfully loaded credentials from identity storage", logKeyIdentity, identityName)

This keeps callers free to use errors.Is while maintaining detailed context. (As per coding guidelines)


676-683: Avoid Background; thread context into retrieval.

retrieveCachedCredentials uses context.Background(). Consider adding ctx to fetchCachedCredentials/retrieveCachedCredentials to preserve cancellation across terraform flows.

Example signature:

-func (m *manager) fetchCachedCredentials(startIndex int) (types.ICredentials, int)
+func (m *manager) fetchCachedCredentials(ctx context.Context, startIndex int) (types.ICredentials, int)

and pass ctx through to retrieveCredentialWithFallback.

📜 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 1eda3a2 and 4da5aab.

📒 Files selected for processing (4)
  • docs/prd/credential-retrieval-consolidation.md (1 hunks)
  • pkg/auth/manager.go (5 hunks)
  • pkg/auth/manager_caching_test.go (1 hunks)
  • pkg/auth/manager_test.go (3 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
pkg/**/*.go

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

Place business logic in pkg rather than in cmd

Files:

  • pkg/auth/manager_caching_test.go
  • pkg/auth/manager.go
  • pkg/auth/manager_test.go
**/*_test.go

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

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

**/*_test.go: Prefer unit tests with mocks over integration tests; use interfaces + DI; table-driven tests for coverage; target >80% coverage.
Tests must exercise production code paths; avoid duplicating implementation logic in tests.
Use t.Skipf("reason") with clear context when skipping tests; CLI tests may auto-build temp binaries.

Files:

  • pkg/auth/manager_caching_test.go
  • pkg/auth/manager_test.go
**/*.go

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

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

**/*.go: Define interfaces for major functionality and use dependency injection to enable testability.
Generate mocks using go.uber.org/mock/mockgen via //go:generate directives (never manual mocks).
Prefer functional Options pattern instead of functions with many parameters for configuration.
Use context.Context only for cancellation, deadlines/timeouts, and request-scoped values; never for configuration or dependencies; context should be the first parameter.
All comments must end with periods (godot linter).
Preserve helpful existing comments; update them to match code when refactoring; only remove when incorrect, redundant, or code removed.
Organize imports in three groups (stdlib, third-party, Atmos) separated by blank lines and sorted alphabetically; maintain aliases cfg, log, u, errUtils.
Add defer perf.Track(atmosConfig, "pkg.FuncName")() (or nil if no atmosConfig) plus a blank line at the start of all public functions for performance tracking.
Use static errors from errors/errors.go; wrap with fmt.Errorf("%w") for chaining; use errors.Join for independent errors; avoid dynamic errors and string comparison; use errors.Is for checks.
Never use //revive:disable:file-length-limit to bypass file size limits; keep files small and focused (<600 lines).
Environment variables must be bound with viper.BindEnv using ATMOS_ prefix (e.g., viper.BindEnv("ATMOS_VAR", "ATMOS_VAR", "FALLBACK")).
Ensure cross-platform compatibility: use filepath.Join instead of hardcoded path separators and prefer SDKs over shelling out to binaries.

Files:

  • pkg/auth/manager_caching_test.go
  • pkg/auth/manager.go
  • pkg/auth/manager_test.go
**/!(*_test).go

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

Document all exported functions, types, and methods with Go doc comments

Files:

  • pkg/auth/manager.go
docs/prd/[a-z0-9-]*.md

📄 CodeRabbit inference engine (CLAUDE.md)

All PRDs must be placed under docs/prd/ with kebab-case filenames.

Files:

  • docs/prd/credential-retrieval-consolidation.md
🧬 Code graph analysis (3)
pkg/auth/manager_caching_test.go (1)
pkg/schema/schema_auth.go (1)
  • AuthConfig (4-9)
pkg/auth/manager.go (3)
pkg/logger/log.go (1)
  • Debug (24-26)
pkg/auth/types/interfaces.go (1)
  • ICredentials (230-241)
pkg/auth/credentials/keyring_file.go (1)
  • ErrCredentialsNotFound (36-36)
pkg/auth/manager_test.go (3)
pkg/auth/credentials/keyring_file.go (1)
  • ErrCredentialsNotFound (36-36)
pkg/schema/schema_auth.go (2)
  • AuthConfig (4-9)
  • Identity (45-53)
pkg/auth/types/interfaces.go (3)
  • Identity (70-114)
  • ICredentials (230-241)
  • PostAuthenticateParams (61-67)
🪛 LanguageTool
docs/prd/credential-retrieval-consolidation.md

[style] ~5-~5: ‘exact same’ might be wordy. Consider a shorter alternative.
Context: ...(e.g., atmos terraform plan) with the exact same credentials. ## Current Architecture A...

(EN_WORDINESS_PREMIUM_EXACT_SAME)


[typographical] ~82-~82: Consider using a typographic opening quote here.
Context: ...tion** without a shared abstraction for "retrieve credential from any available s...

(EN_QUOTES)


[typographical] ~82-~82: Consider using a typographic close quote here.
Context: ...ve credential from any available source." ## Why This Manifested in Terraform Co...

(EN_QUOTES)


[typographical] ~114-~114: Consider using typographic quotation marks here.
Context: ...ion** Three separate implementations of "retrieve credential" logic with inconsistent behavior. ### ...

(EN_QUOTES)


[typographical] ~117-~117: Consider using a typographic opening quote here.
Context: ...ngle method/interface that encapsulates "retrieve credential from any available s...

(EN_QUOTES)


[typographical] ~117-~117: Consider using a typographic close quote here.
Context: ...y available source with proper fallback." ### 3. Implicit Assumptions `retri...

(EN_QUOTES)


[typographical] ~124-~124: Consider using a typographic opening quote here.
Context: ...s, headless systems, no D-Bus) - We use "noop" keyring in these environments ###...

(EN_QUOTES)


[typographical] ~124-~124: Consider using a typographic close quote here.
Context: ...adless systems, no D-Bus) - We use "noop" keyring in these environments ### 4. *...

(EN_QUOTES)

🪛 markdownlint-cli2 (0.18.1)
docs/prd/credential-retrieval-consolidation.md

89-89: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


97-97: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


240-240: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Build (linux)
  • GitHub Check: Build (macos)
  • GitHub Check: Build (windows)
  • GitHub Check: Analyze (go)
  • GitHub Check: Lint (golangci)
  • GitHub Check: Review Dependency Licenses
  • GitHub Check: autofix
  • GitHub Check: Summary
🔇 Additional comments (3)
pkg/auth/manager_test.go (3)

533-581: Nice regression test; this nails the original bug.

Covers the terraform execution path precisely and verifies identity-storage fallback. LGTM.


389-397: Time pointer setup looks good.

Switch to explicit expired/valid times via variables is clearer and avoids inline address-of on temporaries. LGTM.

Also applies to: 395-397


539-547: Fix comment punctuation for linters.

End each comment line with a period to satisfy the godot linter.

Also applies to: 570-581

⛔ Skipped due to learnings
Learnt from: CR
PR: cloudposse/atmos#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-24T14:51:19.355Z
Learning: Applies to **/*.go : All comments must end with periods (godot linter).

@osterman osterman force-pushed the fix-terraform-auth-context branch from 4da5aab to 4219b23 Compare October 28, 2025 17:37
coderabbitai[bot]
coderabbitai bot previously approved these changes Oct 28, 2025
This commit fixes a bug where `atmos terraform plan` and other Terraform commands
failed to use file-based credentials, while `atmos auth whoami` and similar
commands worked correctly. The root cause was duplicate credential retrieval
code across three methods with inconsistent fallback behavior.

## Problem

Three separate code paths retrieved credentials:
1. `GetCachedCredentials` - Had keyring → identity storage fallback ✓
2. `findFirstValidCachedCredentials` - Had fallback logic ✓
3. `retrieveCachedCredentials` - NO fallback (the bug!) ✗

When users authenticated via AWS SSO (credentials in files, not keyring),
Terraform commands would fail because only `retrieveCachedCredentials` was
used in that code path, and it didn't have fallback logic.

## Solution

Extracted a shared `retrieveCredentialWithFallback` method that implements
the single source of truth for credential retrieval. All three code paths
now delegate to this method, ensuring consistent behavior:

- Tries keyring cache first (fast path)
- Falls back to identity storage if not in keyring (slow path)
- Provides detailed logging and error handling

## Changes

- Added `retrieveCredentialWithFallback()` method (38 lines)
- Refactored `GetCachedCredentials()` (40% code reduction)
- Refactored `findFirstValidCachedCredentials()` (57% code reduction)
- Refactored `retrieveCachedCredentials()` (now uses shared method)
- Fixed `TestManager_GetCachedCredentials_Paths` to use proper test data

## Result

✅ All three credential retrieval paths now behave identically
✅ Terraform commands work with file-based credentials
✅ ~110 lines of duplicate code eliminated
✅ All tests pass
✅ Regression test included to prevent this class of bug

Fixes the issue where Terraform commands failed with valid session credentials.

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

Co-Authored-By: Claude <[email protected]>
- Fix mock Identity.LoadCredentials() to return error when no credentials exist
  - Mimics real AWS identity behavior where LoadCredentials fails if credentials
    haven't been written to ~/.aws/credentials via authentication
  - This ensures auth fallback logic only uses identity storage when credentials
    actually exist, not as a bypass for authentication
- Add AWS_REGION and AWS_DEFAULT_REGION to mock Identity.Environment()
  - Matches real AWS identity environment variable exports
- Update test snapshots to reflect correct authentication flow
  - INFO log 'Chained identity' now appears when authentication occurs
  - Mock identity tests now expect LoadCredentials to return error
- Add regex to normalize 'Last Updated' timestamps in auth whoami output
- Replaces dynamic timestamps like '2025-10-28 13:10:27 CDT' with fixed '2025-01-01 12:00:00 UTC'
- Prevents snapshot test failures due to time-sensitive credential metadata
- Update affected snapshot with normalized timestamp
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
pkg/auth/manager_caching_test.go (1)

289-293: Fix comment punctuation for linters.

End the first comment line with a period to satisfy godot.

-// TestRetrieveCachedCredentials_KeyringMiss_IdentityStorageFallback tests that
+// TestRetrieveCachedCredentials_KeyringMiss_IdentityStorageFallback tests that.

Based on learnings.

🧹 Nitpick comments (9)
pkg/auth/providers/mock/identity.go (1)

86-88: Region defaults in mock env: good; consider a shared constant.

LGTM. To avoid drift with Authenticate()’s "us-east-1", consider a file‑level const and reuse.

Example:

const mockRegion = "us-east-1"

Use mockRegion in Environment() and Authenticate().

pkg/auth/manager_caching_test.go (1)

322-328: Run subtests in parallel.

Safe here and speeds up the suite.

 for _, tt := range tests {
   t.Run(tt.name, func(t *testing.T) {
+    t.Parallel()
pkg/auth/providers/mock/identity_test.go (1)

329-335: Prefer ErrorContains over stringing err.Error().

Improves readability and failure messages.

- require.Error(t, err)
- require.Nil(t, creds)
- assert.Contains(t, err.Error(), "no stored credentials")
+ require.Error(t, err)
+ require.Nil(t, creds)
+ assert.ErrorContains(t, err, "no stored credentials")
docs/prd/credential-retrieval-consolidation.md (2)

89-109: Add language to fenced code blocks (markdownlint MD040).

Mark these as text.

-```
+```text
 cmd/auth_whoami.go
   → pkg/auth/hooks.go: AuthWhoami()
   → pkg/auth/manager.go: Whoami()
     → pkg/auth/manager.go: GetCachedCredentials()  ✅ Has fallback
-```
+```

-```
+```text
 cmd/terraform.go
   → internal/exec/terraform.go: ExecuteTerraform()
   → pkg/auth/hooks.go: TerraformPreHook()
   → pkg/auth/manager.go: Authenticate()
   → pkg/auth/manager.go: authenticateHierarchical()
   → pkg/auth/manager.go: authenticateFromIndex()
   → pkg/auth/manager.go: authenticateProviderChain()
   → pkg/auth/manager.go: fetchCachedCredentials()
   → pkg/auth/manager.go: retrieveCachedCredentials() ❌ No fallback
-```
+```

240-244: Use a heading instead of bold (markdownlint MD036).

Convert emphasized line to a proper heading.

-**Option 1: Extract Common Retrieval Logic**
+### Option 1: Extract Common Retrieval Logic
pkg/auth/manager.go (4)

507-512: Prefer propagating context over context.Background().

Consider threading a ctx into findFirstValidCachedCredentials and using it here, to allow cancellation/timeouts in long-running checks.

Example (sketch):

  • func (m *manager) findFirstValidCachedCredentials(ctx context.Context) int
  • call m.loadCredentialsWithFallback(ctx, identityName)

Also applies to: 526-531


632-671: Unify “identity not found” error sentinel.

Elsewhere you use ErrIdentityNotFound; here it’s ErrIdentityNotInConfig. Use a single sentinel for easier errors.Is checks.

- if !exists {
-     return nil, fmt.Errorf("%w: %s", errUtils.ErrIdentityNotInConfig, identityName)
- }
+ if !exists {
+     return nil, fmt.Errorf("%w: %s", errUtils.ErrIdentityNotFound, identityName)
+ }

673-683: Consider passing ctx into getChainCredentials.

Avoid context.Background() inside; add ctx param and forward to loadCredentialsWithFallback for consistency.

Sketch:

  • func (m *manager) getChainCredentials(ctx context.Context, chain []string, startIndex int) (...)
  • callers pass ctx from authenticateProviderChain/fetchCachedCredentials.

477-497: Optional: use the unified fallback when target has valid cached creds.

When validFromIndex == last, you re-read keyring directly; if it’s a keyring miss but files exist, you’ll fetch later anyway. Using loadCredentialsWithFallback here is simpler and avoids the extra miss.

- if validFromIndex == len(m.chain)-1 {
-     last := m.chain[len(m.chain)-1]
-     if cachedCreds, err := m.credentialStore.Retrieve(last); err == nil {
-         log.Debug("Using cached credentials for target identity", logKeyIdentity, targetIdentity)
-         return cachedCreds, nil
-     }
- }
+ if validFromIndex == len(m.chain)-1 {
+     last := m.chain[len(m.chain)-1]
+     if cachedCreds, err := m.loadCredentialsWithFallback(context.Background(), last); err == nil {
+         log.Debug("Using cached credentials for target identity", logKeyIdentity, targetIdentity)
+         return cachedCreds, nil
+     }
+ }
📜 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 4da5aab and 1f3fab3.

📒 Files selected for processing (13)
  • docs/prd/credential-retrieval-consolidation.md (1 hunks)
  • pkg/auth/manager.go (8 hunks)
  • pkg/auth/manager_caching_test.go (1 hunks)
  • pkg/auth/manager_test.go (3 hunks)
  • pkg/auth/providers/mock/identity.go (2 hunks)
  • pkg/auth/providers/mock/identity_test.go (2 hunks)
  • tests/snapshots/TestCLICommands_atmos_auth_env_--format_dotenv_--identity_mock-identity.stdout.golden (1 hunks)
  • tests/snapshots/TestCLICommands_atmos_auth_env_--format_json_--identity_mock-identity.stdout.golden (1 hunks)
  • tests/snapshots/TestCLICommands_atmos_auth_env_--login_--format_bash.stderr.golden (1 hunks)
  • tests/snapshots/TestCLICommands_atmos_auth_env_--login_without_cached_credentials.stderr.golden (1 hunks)
  • tests/snapshots/TestCLICommands_atmos_auth_env_without_--login_returns_config_env_vars.stdout.golden (1 hunks)
  • tests/snapshots/TestCLICommands_atmos_auth_exec_without_authentication.stderr.golden (1 hunks)
  • tests/snapshots/TestCLICommands_atmos_auth_whoami_without_authentication.stderr.golden (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • tests/snapshots/TestCLICommands_atmos_auth_exec_without_authentication.stderr.golden
  • tests/snapshots/TestCLICommands_atmos_auth_env_--format_json_--identity_mock-identity.stdout.golden
  • tests/snapshots/TestCLICommands_atmos_auth_env_--login_--format_bash.stderr.golden
🧰 Additional context used
📓 Path-based instructions (6)
pkg/**/*.go

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

Place business logic in pkg rather than in cmd

Files:

  • pkg/auth/providers/mock/identity_test.go
  • pkg/auth/providers/mock/identity.go
  • pkg/auth/manager.go
  • pkg/auth/manager_caching_test.go
  • pkg/auth/manager_test.go
**/*_test.go

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

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

**/*_test.go: Prefer unit tests with mocks over integration tests; use interfaces + DI; table-driven tests for coverage; target >80% coverage.
Tests must exercise production code paths; avoid duplicating implementation logic in tests.
Use t.Skipf("reason") with clear context when skipping tests; CLI tests may auto-build temp binaries.

Files:

  • pkg/auth/providers/mock/identity_test.go
  • pkg/auth/manager_caching_test.go
  • pkg/auth/manager_test.go
**/*.go

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

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

**/*.go: Define interfaces for major functionality and use dependency injection to enable testability.
Generate mocks using go.uber.org/mock/mockgen via //go:generate directives (never manual mocks).
Prefer functional Options pattern instead of functions with many parameters for configuration.
Use context.Context only for cancellation, deadlines/timeouts, and request-scoped values; never for configuration or dependencies; context should be the first parameter.
All comments must end with periods (godot linter).
Preserve helpful existing comments; update them to match code when refactoring; only remove when incorrect, redundant, or code removed.
Organize imports in three groups (stdlib, third-party, Atmos) separated by blank lines and sorted alphabetically; maintain aliases cfg, log, u, errUtils.
Add defer perf.Track(atmosConfig, "pkg.FuncName")() (or nil if no atmosConfig) plus a blank line at the start of all public functions for performance tracking.
Use static errors from errors/errors.go; wrap with fmt.Errorf("%w") for chaining; use errors.Join for independent errors; avoid dynamic errors and string comparison; use errors.Is for checks.
Never use //revive:disable:file-length-limit to bypass file size limits; keep files small and focused (<600 lines).
Environment variables must be bound with viper.BindEnv using ATMOS_ prefix (e.g., viper.BindEnv("ATMOS_VAR", "ATMOS_VAR", "FALLBACK")).
Ensure cross-platform compatibility: use filepath.Join instead of hardcoded path separators and prefer SDKs over shelling out to binaries.

Files:

  • pkg/auth/providers/mock/identity_test.go
  • pkg/auth/providers/mock/identity.go
  • pkg/auth/manager.go
  • pkg/auth/manager_caching_test.go
  • pkg/auth/manager_test.go
**/!(*_test).go

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

Document all exported functions, types, and methods with Go doc comments

Files:

  • pkg/auth/providers/mock/identity.go
  • pkg/auth/manager.go
docs/prd/[a-z0-9-]*.md

📄 CodeRabbit inference engine (CLAUDE.md)

All PRDs must be placed under docs/prd/ with kebab-case filenames.

Files:

  • docs/prd/credential-retrieval-consolidation.md
tests/snapshots/**

📄 CodeRabbit inference engine (CLAUDE.md)

Never manually edit golden snapshot files; regenerate using -regenerate-snapshots.

Files:

  • tests/snapshots/TestCLICommands_atmos_auth_env_--format_dotenv_--identity_mock-identity.stdout.golden
  • tests/snapshots/TestCLICommands_atmos_auth_env_without_--login_returns_config_env_vars.stdout.golden
  • tests/snapshots/TestCLICommands_atmos_auth_env_--login_without_cached_credentials.stderr.golden
  • tests/snapshots/TestCLICommands_atmos_auth_whoami_without_authentication.stderr.golden
🧠 Learnings (1)
📚 Learning: 2025-10-24T14:51:19.355Z
Learnt from: CR
PR: cloudposse/atmos#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-24T14:51:19.355Z
Learning: Applies to **/*.go : All comments must end with periods (godot linter).

Applied to files:

  • pkg/auth/manager_caching_test.go
🧬 Code graph analysis (4)
pkg/auth/providers/mock/identity.go (2)
pkg/auth/types/interfaces.go (2)
  • Identity (70-114)
  • ICredentials (230-241)
pkg/perf/perf.go (1)
  • Track (121-138)
pkg/auth/manager.go (4)
pkg/logger/log.go (1)
  • Debug (24-26)
pkg/auth/types/interfaces.go (1)
  • ICredentials (230-241)
pkg/auth/credentials/keyring_file.go (1)
  • ErrCredentialsNotFound (36-36)
errors/errors.go (2)
  • ErrIdentityNotInConfig (428-428)
  • ErrNoCredentialsFound (388-388)
pkg/auth/manager_caching_test.go (1)
pkg/schema/schema_auth.go (1)
  • AuthConfig (4-9)
pkg/auth/manager_test.go (2)
pkg/auth/credentials/keyring_file.go (1)
  • ErrCredentialsNotFound (36-36)
pkg/auth/types/interfaces.go (3)
  • Identity (70-114)
  • ICredentials (230-241)
  • PostAuthenticateParams (61-67)
🪛 LanguageTool
docs/prd/credential-retrieval-consolidation.md

[style] ~5-~5: ‘exact same’ might be wordy. Consider a shorter alternative.
Context: ...(e.g., atmos terraform plan) with the exact same credentials. ## Current Architecture A...

(EN_WORDINESS_PREMIUM_EXACT_SAME)


[typographical] ~82-~82: Consider using a typographic opening quote here.
Context: ...tion** without a shared abstraction for "retrieve credential from any available s...

(EN_QUOTES)


[typographical] ~82-~82: Consider using a typographic close quote here.
Context: ...ve credential from any available source." ## Why This Manifested in Terraform Co...

(EN_QUOTES)


[typographical] ~114-~114: Consider using typographic quotation marks here.
Context: ...ion** Three separate implementations of "retrieve credential" logic with inconsistent behavior. ### ...

(EN_QUOTES)


[typographical] ~117-~117: Consider using a typographic opening quote here.
Context: ...ngle method/interface that encapsulates "retrieve credential from any available s...

(EN_QUOTES)


[typographical] ~117-~117: Consider using a typographic close quote here.
Context: ...y available source with proper fallback." ### 3. Implicit Assumptions `retri...

(EN_QUOTES)


[typographical] ~124-~124: Consider using a typographic opening quote here.
Context: ...s, headless systems, no D-Bus) - We use "noop" keyring in these environments ###...

(EN_QUOTES)


[typographical] ~124-~124: Consider using a typographic close quote here.
Context: ...adless systems, no D-Bus) - We use "noop" keyring in these environments ### 4. *...

(EN_QUOTES)

🪛 markdownlint-cli2 (0.18.1)
docs/prd/credential-retrieval-consolidation.md

89-89: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


97-97: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


240-240: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Build (linux)
  • GitHub Check: Build (windows)
  • GitHub Check: autofix
  • GitHub Check: Analyze (go)
  • GitHub Check: Lint (golangci)
  • GitHub Check: Review Dependency Licenses
  • GitHub Check: Run pre-commit hooks
  • GitHub Check: Summary
🔇 Additional comments (9)
tests/snapshots/TestCLICommands_atmos_auth_env_--login_without_cached_credentials.stderr.golden (1)

3-3: Snapshot addition looks fine; please confirm it was auto‑generated.

Ensure this golden was updated via the snapshot regeneration flow, not manual edits.

Per guidelines, regenerate with -regenerate-snapshots and commit the result to avoid drift.

tests/snapshots/TestCLICommands_atmos_auth_env_--format_dotenv_--identity_mock-identity.stdout.golden (1)

2-2: Added region exports are expected.

LGTM. Please confirm the snapshot was regenerated (not hand‑edited).

Also applies to: 4-4

tests/snapshots/TestCLICommands_atmos_auth_whoami_without_authentication.stderr.golden (1)

3-3: Output changes align with the new mock behavior.

Looks good. Please ensure these goldens were regenerated via the sanctioned flag.

Also applies to: 11-12

tests/snapshots/TestCLICommands_atmos_auth_env_without_--login_returns_config_env_vars.stdout.golden (1)

2-2: Region exports are expected with the new mock env.

Looks good. Please confirm this golden was regenerated via the supported workflow.

Also applies to: 4-4

pkg/auth/manager_test.go (1)

522-530: LGTM: switch to getChainCredentials.

Matches the unified fallback path and aligns tests with prod behavior.

pkg/auth/manager.go (4)

202-216: LGTM: centralize retrieval via loadCredentialsWithFallback.

Good consolidation and error wrapping with ErrNoCredentialsFound; keeps behavior consistent.


219-221: LGTM: explicit expired check.

Clear and matches whoami semantics.


141-146: LGTM: authenticateChain rename and call site.

Name and flow read better; no behavior concerns.


599-607: LGTM: fetchCachedCredentials now uses the unified path.

This removes the pre-fix inconsistency that broke Terraform flows.

Redesign mock identity to support stateful credential storage simulation,
making it provider-agnostic and suitable for testing diverse provider types
(AWS, Azure, GitHub, etc.).

Changes:
- Add hasStoredCredentials flag to track credential persistence state
- Update PostAuthenticate() to mark credentials as stored (simulates writing
  to ~/.aws/credentials, GitHub token file, etc.)
- Update LoadCredentials() to check storage state:
  - Returns error if no credentials stored (before authentication)
  - Returns mock credentials if stored (after authentication)
- Update Logout() to clear stored credentials state
- Enhance tests to cover both credential states:
  - Before authentication: LoadCredentials fails (no stored credentials)
  - After authentication: LoadCredentials succeeds (credentials loaded)
  - After logout: LoadCredentials fails again (credentials cleared)

Benefits:
- Provider-agnostic design supports testing different storage mechanisms:
  * File-based: AWS ~/.aws/credentials, Azure ~/.azure/
  * Token-based: GitHub environment variables or token files
  * Certificate-based: SSL/TLS certs on disk
  * Ephemeral: No persistent storage
- Tests both credential fallback paths (keyring miss → identity storage)
- More realistic simulation of real provider authentication lifecycle
- Maintains deterministic fixed timestamps for snapshot stability

All tests pass with no snapshot changes required.
@codecov
Copy link

codecov bot commented Oct 28, 2025

Codecov Report

❌ Patch coverage is 72.07891% with 184 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.63%. Comparing base (b1068c1) to head (8d4c40a).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
pkg/auth/cloud/aws/files.go 69.89% 38 Missing and 18 partials ⚠️
pkg/auth/list/formatter_utils.go 63.09% 21 Missing and 10 partials ⚠️
cmd/terraform_utils.go 5.00% 17 Missing and 2 partials ⚠️
pkg/auth/identities/aws/assume_role.go 50.00% 9 Missing and 4 partials ⚠️
pkg/auth/identities/aws/permission_set.go 62.85% 9 Missing and 4 partials ⚠️
pkg/auth/manager.go 84.52% 11 Missing and 2 partials ⚠️
pkg/auth/manager_logout.go 81.39% 6 Missing and 2 partials ⚠️
pkg/config/load.go 71.42% 4 Missing and 4 partials ⚠️
pkg/auth/hooks.go 72.72% 5 Missing and 1 partial ⚠️
pkg/auth/list/formatter_table.go 73.33% 3 Missing and 1 partial ⚠️
... and 7 more
Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff            @@
##             main    #1720    +/-   ##
========================================
  Coverage   68.62%   68.63%            
========================================
  Files         378      379     +1     
  Lines       34154    34600   +446     
========================================
+ Hits        23439    23748   +309     
- Misses       8528     8623    +95     
- Partials     2187     2229    +42     
Flag Coverage Δ
unittests 68.63% <72.07%> (+<0.01%) ⬆️

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

Files with missing lines Coverage Δ
cmd/auth.go 63.63% <100.00%> (+13.63%) ⬆️
cmd/auth_console.go 51.61% <100.00%> (+0.31%) ⬆️
cmd/auth_env.go 59.75% <100.00%> (+4.20%) ⬆️
cmd/auth_login.go 84.50% <100.00%> (-2.64%) ⬇️
cmd/auth_shell.go 57.14% <100.00%> (+0.89%) ⬆️
cmd/terraform_commands.go 93.00% <100.00%> (+0.14%) ⬆️
internal/exec/cli_utils.go 72.79% <100.00%> (+1.39%) ⬆️
internal/exec/component_env.go 100.00% <100.00%> (ø)
internal/exec/packer.go 63.20% <100.00%> (+0.29%) ⬆️
internal/exec/terraform.go 58.92% <100.00%> (+0.23%) ⬆️
... and 18 more

... and 3 files with indirect coverage changes

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

- Fix PRD documentation to use correct function name loadCredentialsWithFallback
  (was incorrectly referencing retrieveCredentialWithFallback)
- Update function references to getChainCredentials (formerly retrieveCachedCredentials)
- Fix XDG directory references (Atmos uses XDG directories, not ~/.aws/credentials)

- Add terminal punctuation to test comment in manager_test.go to satisfy godot linter
- Capitalize continuation sentence per godot linter requirements

- Add static sentinel error ErrNoStoredCredentials in mock identity
  - Follows error-handling guidelines for static errors
  - Removes dynamic error with nolint directive
  - Use fmt.Errorf with %w to wrap sentinel error with context
- Update LoadCredentials comment to mention ErrNoStoredCredentials return type
- Clarify XDG directory usage in comments

All tests pass with no behavioral changes.
coderabbitai[bot]
coderabbitai bot previously approved these changes Oct 28, 2025
Make auth manager more provider-agnostic by removing AWS-specific assumptions:

- Remove '(non-AWS)' from log messages
  - Replace with provider-agnostic 'expiration: none' for non-expiring credentials
  - Clarifies that nil expiration means long-lived tokens/API keys, not 'non-AWS'
  - Supports GitHub tokens, API keys, certificates, and other non-expiring credentials

- Fix default credential path from ~/.aws/atmos to ~/.config/atmos
  - Follows XDG Base Directory Specification
  - Makes fallback path provider-agnostic
  - Update test expectations to match

Note: AWS-specific code still exists for aws/user identity kind (lines 422-424, 557-558).
There's a TODO comment acknowledging this should be handled via identity interface.
That's a larger refactor beyond this PR's scope.

All tests pass.
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

Caution

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

⚠️ Outside diff range comments (1)
pkg/auth/providers/mock/identity.go (1)

126-133: Fix inconsistency between CredentialsExist and LoadCredentials.

CredentialsExist() always returns true, but LoadCredentials() will fail if hasStoredCredentials is false. This breaks the contract: callers expect that if CredentialsExist() returns true, LoadCredentials() should succeed.

Apply this diff:

-// CredentialsExist always returns true for mock identities (credentials are in-memory).
+// CredentialsExist checks if credentials have been stored via PostAuthenticate.
 func (i *Identity) CredentialsExist() (bool, error) {
 	defer perf.Track(nil, "mock.Identity.CredentialsExist")()
 
-	// Mock identities don't use file-based storage.
-	// Credentials are always available in-memory.
-	return true, nil
+	// Return whether credentials have been stored after authentication.
+	return i.hasStoredCredentials, nil
 }
♻️ Duplicate comments (1)
docs/prd/credential-retrieval-consolidation.md (1)

136-168: Unify function name across PRD to match code.

Doc uses both “retrieveCredentialWithFallback” and “loadCredentialsWithFallback”. Code implements loadCredentialsWithFallback; keep PRD consistent.

- ## Recommended Approach
- **Option 1: Extract Common Retrieval Logic**
- 1. Create `retrieveCredentialWithFallback` as the single retrieval method
+ ## Recommended Approach
+ **Option 1: Extract Common Retrieval Logic**
+ 1. Create `loadCredentialsWithFallback` as the single retrieval method

Also ensure all bullet references (GetCachedCredentials/findFirstValidCachedCredentials/getChainCredentials) point to loadCredentialsWithFallback.

Also applies to: 240-245

🧹 Nitpick comments (12)
tests/cli_test.go (1)

396-401: Harden the “Last Updated” regex to cover numeric offset timezones.

Current pattern misses forms like +00:00 or GMT+01. Slight tweak improves resilience; behavior unchanged.

- lastUpdatedRegex := regexp.MustCompile(`Last Updated\s+\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\s+[A-Z]{3,4}`)
+ // Accept UTC/abbr or numeric offsets (e.g. +00:00, +0000).
+ lastUpdatedRegex := regexp.MustCompile(
+   `Last Updated\s+\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\s+(?:UTC|[A-Z]{3,4}|[+-]\d{2}:?\d{2})`,
+ )

Optional: precompile at package scope to avoid recompiling per call.

docs/prd/credential-retrieval-consolidation.md (2)

89-109: Add code-fence languages for markdown linters.

The two flow blocks lack a language spec (MD040). Use text for call flows.

- ```
+ ```text
 cmd/auth_whoami.go
   → pkg/auth/hooks.go: AuthWhoami()
   ...
- ```
+ ```

Repeat for the broken path block.


5-5: Tighten phrasing.

“exact same credentials” → “the same credentials”.

pkg/auth/providers/mock/identity_test.go (2)

227-245: Prefer errors.Is for sentinel error over string contains.

Use the mock’s ErrNoStoredCredentials to avoid brittle string matching.

- creds, err = identity.LoadCredentials(ctx)
- require.Error(t, err)
- require.Nil(t, creds)
- assert.Contains(t, err.Error(), "no stored credentials")
+ creds, err = identity.LoadCredentials(ctx)
+ require.Error(t, err)
+ require.Nil(t, creds)
+ assert.ErrorIs(t, err, ErrNoStoredCredentials)

353-383: Use errors.Is in table test too.

Align error assertions with the sentinel error.

- if tt.expectedError {
-   require.Error(t, err)
-   assert.Contains(t, err.Error(), "no stored credentials")
- } else {
+ if tt.expectedError {
+   require.Error(t, err)
+   assert.ErrorIs(t, err, ErrNoStoredCredentials)
+ } else {
   require.NoError(t, err)
 }
pkg/auth/manager.go (3)

477-498: Keep retrieval via the single path (avoid direct keyring call).

This shortcut bypasses the new fallback and duplicates logic. Use getChainCredentials (which delegates to loadCredentialsWithFallback) for the target step too.

- if validFromIndex == len(m.chain)-1 {
-   last := m.chain[len(m.chain)-1]
-   if cachedCreds, err := m.credentialStore.Retrieve(last); err == nil {
-     log.Debug("Using cached credentials for target identity", logKeyIdentity, targetIdentity)
-     return cachedCreds, nil
-   }
- }
+ if validFromIndex == len(m.chain)-1 {
+   // Use unified retrieval with fallback for the target step.
+   if cachedCreds, err := m.getChainCredentials(m.chain, validFromIndex); err == nil {
+     log.Debug("Using cached credentials for target identity", logKeyIdentity, targetIdentity)
+     return cachedCreds, nil
+   }
+ }

633-673: Optional: cache fallback-loaded credentials.

After loading from identity storage, consider storing to the credential store (best‑effort) to speed subsequent calls.

   loadedCreds, loadErr := identity.LoadCredentials(ctx)
   if loadErr != nil {
     return nil, fmt.Errorf("failed to load credentials from identity storage for %q: %w", identityName, loadErr)
   }
   if loadedCreds == nil {
     return nil, fmt.Errorf("%w: credentials loaded from storage are nil for identity %q", errUtils.ErrNoCredentialsFound, identityName)
   }
+  // Best-effort cache.
+  if err := m.credentialStore.Store(identityName, loadedCreds); err != nil {
+    log.Debug("Failed to cache credentials loaded from identity storage", logKeyIdentity, identityName, "error", err)
+  }

675-684: Thread context instead of context.Background().

Propagate the caller’s ctx through getChainCredentials to enable cancellation/timeouts during fallback loads.

- func (m *manager) getChainCredentials(chain []string, startIndex int) (types.ICredentials, error) {
+ func (m *manager) getChainCredentials(ctx context.Context, chain []string, startIndex int) (types.ICredentials, error) {
   identityName := chain[startIndex]
-  creds, err := m.loadCredentialsWithFallback(context.Background(), identityName)
+  creds, err := m.loadCredentialsWithFallback(ctx, identityName)

And update the single caller:

- currentCreds, actualStartIndex = m.fetchCachedCredentials(actualStartIndex)
+ currentCreds, actualStartIndex = m.fetchCachedCredentials(ctx, actualStartIndex)

(And pass ctx through fetchCachedCredentials similarly.)

pkg/auth/manager_test.go (2)

389-397: Minor: stabilize time anchors.

Use a single captured now to avoid edge flake if the clock jumps between assignments.

- expiredTime := time.Now().Add(-1 * time.Hour)
+ now := time.Now()
+ expiredTime := now.Add(-1 * time.Hour)
...
- validTime := time.Now().Add(1 * time.Hour)
+ validTime := now.Add(1 * time.Hour)

533-582: Great regression coverage; consider asserting Authenticate wasn’t invoked.

You already make Authenticate return an error; add a flag to assert it wasn’t called.

 type mockIdentityForFallback struct {
   hasCredentials bool
   creds          types.ICredentials
+  calledAuth     *bool
 }
 ...
 func (m *mockIdentityForFallback) Authenticate(ctx context.Context, previousCredentials types.ICredentials) (types.ICredentials, error) {
-  return nil, fmt.Errorf("authenticate should not be called in retrieval path")
+  if m.calledAuth != nil { *m.calledAuth = true }
+  return nil, fmt.Errorf("authenticate should not be called in retrieval path")
 }
 ...
- creds, err := m.getChainCredentials(m.chain, 1)
+ called := false
+ identity.calledAuth = &called
+ creds, err := m.getChainCredentials(m.chain, 1)
 ...
  require.NoError(t, err, ...)
  assert.NotNil(t, creds, ...)
+ assert.False(t, called, "Authenticate should not be called for fallback retrieval")
pkg/auth/providers/mock/identity.go (2)

154-156: Extract fixedExpiration to a helper function.

The fixedExpiration creation is duplicated here and in Authenticate() (line 63). Consider extracting to a helper function for DRY.

Add a helper function in the file:

// getFixedExpiration returns a deterministic expiration timestamp for testing.
func getFixedExpiration() time.Time {
	return time.Date(MockExpirationYear, MockExpirationMonth, MockExpirationDay, MockExpirationHour, MockExpirationMinute, MockExpirationSecond, 0, time.UTC)
}

Then update both Authenticate() and LoadCredentials():

-	fixedExpiration := time.Date(MockExpirationYear, MockExpirationMonth, MockExpirationDay, MockExpirationHour, MockExpirationMinute, MockExpirationSecond, 0, time.UTC)
+	fixedExpiration := getFixedExpiration()

160-166: Consider using consistent credential values.

LoadCredentials() returns generic credentials ("mock-access-key"), while Authenticate() (line 65) returns identity-specific credentials (fmt.Sprintf("MOCK_KEY_%s", i.name)). Using identity-specific values here would improve consistency and make debugging easier.

Apply this diff for consistency:

 	return &Credentials{
-		AccessKeyID:     "mock-access-key",
-		SecretAccessKey: "mock-secret-key",
-		SessionToken:    "mock-session-token",
+		AccessKeyID:     fmt.Sprintf("MOCK_KEY_%s", i.name),
+		SecretAccessKey: fmt.Sprintf("MOCK_SECRET_%s", i.name),
+		SessionToken:    fmt.Sprintf("MOCK_TOKEN_%s", i.name),
 		Region:          "us-east-1",
 		Expiration:      fixedExpiration,
 	}, nil
📜 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 1f3fab3 and a813f42.

📒 Files selected for processing (7)
  • docs/prd/credential-retrieval-consolidation.md (1 hunks)
  • pkg/auth/manager.go (8 hunks)
  • pkg/auth/manager_test.go (4 hunks)
  • pkg/auth/providers/mock/identity.go (5 hunks)
  • pkg/auth/providers/mock/identity_test.go (3 hunks)
  • tests/cli_test.go (1 hunks)
  • tests/snapshots/TestCLICommands_atmos_auth_whoami_without_authentication.stderr.golden (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/snapshots/TestCLICommands_atmos_auth_whoami_without_authentication.stderr.golden
🧰 Additional context used
📓 Path-based instructions (6)
**/*_test.go

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

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

**/*_test.go: Prefer unit tests with mocks over integration tests; use interfaces + DI; table-driven tests for coverage; target >80% coverage.
Tests must exercise production code paths; avoid duplicating implementation logic in tests.
Use t.Skipf("reason") with clear context when skipping tests; CLI tests may auto-build temp binaries.

Files:

  • tests/cli_test.go
  • pkg/auth/providers/mock/identity_test.go
  • pkg/auth/manager_test.go
**/*.go

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

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

**/*.go: Define interfaces for major functionality and use dependency injection to enable testability.
Generate mocks using go.uber.org/mock/mockgen via //go:generate directives (never manual mocks).
Prefer functional Options pattern instead of functions with many parameters for configuration.
Use context.Context only for cancellation, deadlines/timeouts, and request-scoped values; never for configuration or dependencies; context should be the first parameter.
All comments must end with periods (godot linter).
Preserve helpful existing comments; update them to match code when refactoring; only remove when incorrect, redundant, or code removed.
Organize imports in three groups (stdlib, third-party, Atmos) separated by blank lines and sorted alphabetically; maintain aliases cfg, log, u, errUtils.
Add defer perf.Track(atmosConfig, "pkg.FuncName")() (or nil if no atmosConfig) plus a blank line at the start of all public functions for performance tracking.
Use static errors from errors/errors.go; wrap with fmt.Errorf("%w") for chaining; use errors.Join for independent errors; avoid dynamic errors and string comparison; use errors.Is for checks.
Never use //revive:disable:file-length-limit to bypass file size limits; keep files small and focused (<600 lines).
Environment variables must be bound with viper.BindEnv using ATMOS_ prefix (e.g., viper.BindEnv("ATMOS_VAR", "ATMOS_VAR", "FALLBACK")).
Ensure cross-platform compatibility: use filepath.Join instead of hardcoded path separators and prefer SDKs over shelling out to binaries.

Files:

  • tests/cli_test.go
  • pkg/auth/providers/mock/identity_test.go
  • pkg/auth/manager_test.go
  • pkg/auth/manager.go
  • pkg/auth/providers/mock/identity.go
tests/**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*_test.go: Integration tests, when necessary, must live under tests/.
Use test precondition helpers from tests/test_preconditions.go so tests skip gracefully when prerequisites are missing.

Files:

  • tests/cli_test.go
pkg/**/*.go

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

Place business logic in pkg rather than in cmd

Files:

  • pkg/auth/providers/mock/identity_test.go
  • pkg/auth/manager_test.go
  • pkg/auth/manager.go
  • pkg/auth/providers/mock/identity.go
**/!(*_test).go

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

Document all exported functions, types, and methods with Go doc comments

Files:

  • pkg/auth/manager.go
  • pkg/auth/providers/mock/identity.go
docs/prd/[a-z0-9-]*.md

📄 CodeRabbit inference engine (CLAUDE.md)

All PRDs must be placed under docs/prd/ with kebab-case filenames.

Files:

  • docs/prd/credential-retrieval-consolidation.md
🧬 Code graph analysis (4)
pkg/auth/providers/mock/identity_test.go (2)
pkg/auth/types/interfaces.go (1)
  • PostAuthenticateParams (61-67)
pkg/auth/providers/mock/credentials.go (1)
  • Credentials (11-17)
pkg/auth/manager_test.go (3)
pkg/auth/credentials/keyring_file.go (1)
  • ErrCredentialsNotFound (36-36)
pkg/schema/schema_auth.go (2)
  • AuthConfig (4-9)
  • Identity (45-53)
pkg/auth/types/interfaces.go (3)
  • Identity (70-114)
  • ICredentials (230-241)
  • PostAuthenticateParams (61-67)
pkg/auth/manager.go (4)
pkg/logger/log.go (1)
  • Debug (24-26)
pkg/auth/types/interfaces.go (1)
  • ICredentials (230-241)
pkg/auth/credentials/keyring_file.go (1)
  • ErrCredentialsNotFound (36-36)
errors/errors.go (2)
  • ErrIdentityNotInConfig (428-428)
  • ErrNoCredentialsFound (388-388)
pkg/auth/providers/mock/identity.go (4)
pkg/auth/types/interfaces.go (3)
  • Identity (70-114)
  • PostAuthenticateParams (61-67)
  • ICredentials (230-241)
pkg/schema/schema_auth.go (1)
  • Identity (45-53)
pkg/auth/providers/mock/provider.go (6)
  • MockExpirationYear (15-15)
  • MockExpirationMonth (17-17)
  • MockExpirationDay (19-19)
  • MockExpirationHour (21-21)
  • MockExpirationMinute (23-23)
  • MockExpirationSecond (25-25)
pkg/auth/providers/mock/credentials.go (1)
  • Credentials (11-17)
🪛 LanguageTool
docs/prd/credential-retrieval-consolidation.md

[style] ~5-~5: ‘exact same’ might be wordy. Consider a shorter alternative.
Context: ...(e.g., atmos terraform plan) with the exact same credentials. ## Current Architecture A...

(EN_WORDINESS_PREMIUM_EXACT_SAME)


[typographical] ~82-~82: Consider using a typographic opening quote here.
Context: ...tion** without a shared abstraction for "retrieve credential from any available s...

(EN_QUOTES)


[typographical] ~82-~82: Consider using a typographic close quote here.
Context: ...ve credential from any available source." ## Why This Manifested in Terraform Co...

(EN_QUOTES)


[typographical] ~114-~114: Consider using typographic quotation marks here.
Context: ...ion** Three separate implementations of "retrieve credential" logic with inconsistent behavior. ### ...

(EN_QUOTES)


[typographical] ~117-~117: Consider using a typographic opening quote here.
Context: ...ngle method/interface that encapsulates "retrieve credential from any available s...

(EN_QUOTES)


[typographical] ~117-~117: Consider using a typographic close quote here.
Context: ...y available source with proper fallback." ### 3. Implicit Assumptions `retri...

(EN_QUOTES)


[typographical] ~124-~124: Consider using a typographic opening quote here.
Context: ...s, headless systems, no D-Bus) - We use "noop" keyring in these environments ###...

(EN_QUOTES)


[typographical] ~124-~124: Consider using a typographic close quote here.
Context: ...adless systems, no D-Bus) - We use "noop" keyring in these environments ### 4. *...

(EN_QUOTES)

🪛 markdownlint-cli2 (0.18.1)
docs/prd/credential-retrieval-consolidation.md

89-89: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


97-97: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


240-240: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Run pre-commit hooks
  • GitHub Check: Build (windows)
  • GitHub Check: Build (linux)
  • GitHub Check: autofix
  • GitHub Check: Review Dependency Licenses
  • GitHub Check: Lint (golangci)
  • GitHub Check: Analyze (go)
  • GitHub Check: Summary
🔇 Additional comments (11)
pkg/auth/manager.go (3)

202-221: Good consolidation and error context.

Keyring→identity-storage fallback centralization here reads well; error includes identity/provider/store for triage. LGTM.


219-221: Expiry check is correct and minimal.

Directly defers to credentials’ IsExpired; matches interface intent.


436-438: Default files path fallback is reasonable.

Using ~/.config/atmos as XDG-aligned fallback is consistent with tests and docs.

pkg/auth/manager_test.go (3)

14-14: Import addition is appropriate.

Brings in credentials.ErrCredentialsNotFound for precise simulation.


523-531: Updated call to getChainCredentials looks good.

Matches the new internal API and exercises the fallback.


1247-1248: Updated default path expectation matches code.

Test reflects ~/.config/atmos fallback.

pkg/auth/providers/mock/identity.go (5)

14-16: Nice work addressing the past review.

The static sentinel error follows Go idioms and the coding guidelines. The documentation is clear.


19-25: Clean state tracking addition.

The hasStoredCredentials field and documentation clearly explain the storage simulation behavior.


95-96: Good environment setup for AWS SDK testing.

The region variables align with the existing mock configuration.


112-124: Correct simulation of credential persistence.

Setting the flag after authentication properly mimics real provider behavior.


169-179: Proper cleanup of stored state.

Clearing the flag simulates credential removal and requires re-authentication as expected.

coderabbitai[bot]
coderabbitai bot previously approved these changes Oct 28, 2025
…edential storage

The mock identity now persists credentials to a temporary file instead of using
in-memory state. This fixes the issue where terraform commands couldn't find
credentials because each process invocation created a new mock identity instance
with hasStoredCredentials=false.

Changes:
- PostAuthenticate() now writes credentials to a temp file (simulates XDG directory)
- LoadCredentials() reads from the temp file (simulates loading from ~/.config/atmos/)
- Logout() deletes the temp file
- Removed hasStoredCredentials in-memory flag
- Added getCredentialsFilePath() helper method
- Added constants MockRegion and MockFilePermissions (linter compliance)
- Updated tests to use unique identity names and cleanup with t.Cleanup()

This makes the mock identity behave like real providers (AWS, GitHub, Azure) where
credentials persist to disk after authentication and can be loaded in subsequent
process invocations.

Real-world flow:
1. atmos auth login → PostAuthenticate writes /tmp/atmos-mock-{name}.json
2. atmos terraform plan → LoadCredentials reads /tmp/atmos-mock-{name}.json
3. atmos auth logout → Logout deletes /tmp/atmos-mock-{name}.json

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

Co-Authored-By: Claude <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (2)
pkg/auth/providers/mock/identity.go (2)

120-129: Honor “return a NEW map” contract.

PrepareEnvironment must return a new map, not the input reference.

Apply:

 func (i *Identity) PrepareEnvironment(_ context.Context, environ map[string]string) (map[string]string, error) {
 	defer perf.Track(nil, "mock.Identity.PrepareEnvironment")()
 
-	// Mock identities don't need to modify environment for external processes.
-	// Just return the environment unchanged.
-	return environ, nil
+	// Return a shallow copy to honor the interface contract.
+	out := make(map[string]string, len(environ))
+	for k, v := range environ {
+		out[k] = v
+	}
+	return out, nil
 }

As per coding guidelines.


165-172: Report existence based on storage, not unconditionally true.

Aligns with the interface semantics and avoids false positives (e.g., whoami with noop keyring).

Apply:

 func (i *Identity) CredentialsExist() (bool, error) {
 	defer perf.Track(nil, "mock.Identity.CredentialsExist")()
 
-	// Mock identities don't use file-based storage.
-	// Credentials are always available in-memory.
-	return true, nil
+	_, err := os.Stat(i.getCredentialsFilePath())
+	if err == nil {
+		return true, nil
+	}
+	if os.IsNotExist(err) {
+		return false, nil
+	}
+	return false, fmt.Errorf("stat creds: %w", err)
 }

As per coding guidelines.

🧹 Nitpick comments (4)
pkg/auth/providers/mock/identity_test.go (1)

378-388: Make expiration assertion deterministic.

Since mocks use a fixed 2099-12-31T23:59:59Z, assert exact equality to avoid future flakiness.

Apply:

-				assert.False(t, mockCreds.Expiration.IsZero())
-				assert.True(t, mockCreds.Expiration.After(time.Now()))
+				expected := time.Date(
+					MockExpirationYear, MockExpirationMonth, MockExpirationDay,
+					MockExpirationHour, MockExpirationMinute, MockExpirationSecond,
+					0, time.UTC,
+				)
+				assert.Equal(t, expected, mockCreds.Expiration)
pkg/auth/providers/mock/identity.go (3)

48-56: Sanitize filename to avoid path components in identity names.

Defensive coding: ensure identity names with slashes don’t escape the temp dir.

Apply:

 func (i *Identity) getCredentialsFilePath() string {
-	// Use a temp directory that's cleaned up by the OS.
-	// In production, real providers would use XDG directories like ~/.config/atmos/aws/{provider}/.
-	tmpDir := os.TempDir()
-	return filepath.Join(tmpDir, "atmos-mock-"+i.name+".json")
+	tmpDir := os.TempDir()
+	safe := filepath.Base(i.name)
+	safe = strings.ReplaceAll(safe, string(os.PathSeparator), "_")
+	return filepath.Join(tmpDir, "atmos-mock-"+safe+".json")
 }

And import:

+	"strings"

As per coding guidelines.


131-163: Write atomically and prefer provided credentials when available.

Avoid partial writes; use temp+rename. If params.Credentials is set, persist those for better end‑to‑end tests.

Apply:

 func (i *Identity) PostAuthenticate(ctx context.Context, params *types.PostAuthenticateParams) error {
  	defer perf.Track(nil, "mock.Identity.PostAuthenticate")()
 
-	// Write credentials to disk to simulate persistent storage.
-	// Use a fixed timestamp for deterministic testing.
-	fixedExpiration := time.Date(MockExpirationYear, MockExpirationMonth, MockExpirationDay, MockExpirationHour, MockExpirationMinute, MockExpirationSecond, 0, time.UTC)
-
-	creds := &Credentials{
-		AccessKeyID:     "mock-access-key",
-		SecretAccessKey: "mock-secret-key",
-		SessionToken:    "mock-session-token",
-		Region:          MockRegion,
-		Expiration:      fixedExpiration,
-	}
+	fixedExpiration := time.Date(MockExpirationYear, MockExpirationMonth, MockExpirationDay, MockExpirationHour, MockExpirationMinute, MockExpirationSecond, 0, time.UTC)
+	var creds *Credentials
+	if mc, ok := params.Credentials.(*Credentials); ok && mc != nil {
+		// Persist provided mock credentials, normalizing expiration/region for determinism.
+		cp := *mc
+		if cp.Expiration.IsZero() {
+			cp.Expiration = fixedExpiration
+		}
+		if cp.Region == "" {
+			cp.Region = MockRegion
+		}
+		creds = &cp
+	} else {
+		// Default deterministic creds.
+		creds = &Credentials{
+			AccessKeyID:     "mock-access-key",
+			SecretAccessKey: "mock-secret-key",
+			SessionToken:    "mock-session-token",
+			Region:          MockRegion,
+			Expiration:      fixedExpiration,
+		}
+	}
 
 	// Serialize credentials to JSON.
 	data, err := json.Marshal(creds)
 	if err != nil {
 		return fmt.Errorf("failed to marshal mock credentials: %w", err)
 	}
 
-	// Write to temp file (simulates writing to XDG directory).
-	credPath := i.getCredentialsFilePath()
-	if err := os.WriteFile(credPath, data, MockFilePermissions); err != nil {
-		return fmt.Errorf("failed to write mock credentials to %s: %w", credPath, err)
-	}
+	credPath := i.getCredentialsFilePath()
+	dir := filepath.Dir(credPath)
+	tmp, err := os.CreateTemp(dir, "creds-*.tmp")
+	if err != nil {
+		return fmt.Errorf("create temp file: %w", err)
+	}
+	tmpName := tmp.Name()
+	if _, err := tmp.Write(data); err != nil {
+		_ = tmp.Close()
+		_ = os.Remove(tmpName)
+		return fmt.Errorf("write temp creds: %w", err)
+	}
+	if err := tmp.Close(); err != nil {
+		_ = os.Remove(tmpName)
+		return fmt.Errorf("close temp creds: %w", err)
+	}
+	if err := os.Chmod(tmpName, MockFilePermissions); err != nil {
+		_ = os.Remove(tmpName)
+		return fmt.Errorf("chmod temp creds: %w", err)
+	}
+	if err := os.Rename(tmpName, credPath); err != nil {
+		_ = os.Remove(tmpName)
+		return fmt.Errorf("rename temp creds to %s: %w", credPath, err)
+	}
 
 	return nil
 }

As per coding guidelines.


17-23: Minor: permissions on Windows are advisory.

0600 won’t be enforced on Windows; fine for tests, but note if you ever assert perms.

Add a comment:

 	// MockFilePermissions are the file permissions for credential files (owner read/write only).
-	MockFilePermissions = 0o600
+	MockFilePermissions = 0o600 // On Windows, permissions are best-effort.
📜 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 a813f42 and 19e37b3.

📒 Files selected for processing (2)
  • pkg/auth/providers/mock/identity.go (6 hunks)
  • pkg/auth/providers/mock/identity_test.go (3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
pkg/**/*.go

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

Place business logic in pkg rather than in cmd

Files:

  • pkg/auth/providers/mock/identity.go
  • pkg/auth/providers/mock/identity_test.go
**/*.go

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

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

**/*.go: Define interfaces for major functionality and use dependency injection to enable testability.
Generate mocks using go.uber.org/mock/mockgen via //go:generate directives (never manual mocks).
Prefer functional Options pattern instead of functions with many parameters for configuration.
Use context.Context only for cancellation, deadlines/timeouts, and request-scoped values; never for configuration or dependencies; context should be the first parameter.
All comments must end with periods (godot linter).
Preserve helpful existing comments; update them to match code when refactoring; only remove when incorrect, redundant, or code removed.
Organize imports in three groups (stdlib, third-party, Atmos) separated by blank lines and sorted alphabetically; maintain aliases cfg, log, u, errUtils.
Add defer perf.Track(atmosConfig, "pkg.FuncName")() (or nil if no atmosConfig) plus a blank line at the start of all public functions for performance tracking.
Use static errors from errors/errors.go; wrap with fmt.Errorf("%w") for chaining; use errors.Join for independent errors; avoid dynamic errors and string comparison; use errors.Is for checks.
Never use //revive:disable:file-length-limit to bypass file size limits; keep files small and focused (<600 lines).
Environment variables must be bound with viper.BindEnv using ATMOS_ prefix (e.g., viper.BindEnv("ATMOS_VAR", "ATMOS_VAR", "FALLBACK")).
Ensure cross-platform compatibility: use filepath.Join instead of hardcoded path separators and prefer SDKs over shelling out to binaries.

Files:

  • pkg/auth/providers/mock/identity.go
  • pkg/auth/providers/mock/identity_test.go
**/!(*_test).go

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

Document all exported functions, types, and methods with Go doc comments

Files:

  • pkg/auth/providers/mock/identity.go
**/*_test.go

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

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

**/*_test.go: Prefer unit tests with mocks over integration tests; use interfaces + DI; table-driven tests for coverage; target >80% coverage.
Tests must exercise production code paths; avoid duplicating implementation logic in tests.
Use t.Skipf("reason") with clear context when skipping tests; CLI tests may auto-build temp binaries.

Files:

  • pkg/auth/providers/mock/identity_test.go
🧬 Code graph analysis (2)
pkg/auth/providers/mock/identity.go (3)
pkg/auth/types/interfaces.go (3)
  • Identity (70-114)
  • PostAuthenticateParams (61-67)
  • ICredentials (230-241)
pkg/auth/providers/mock/provider.go (6)
  • MockExpirationYear (15-15)
  • MockExpirationMonth (17-17)
  • MockExpirationDay (19-19)
  • MockExpirationHour (21-21)
  • MockExpirationMinute (23-23)
  • MockExpirationSecond (25-25)
pkg/auth/providers/mock/credentials.go (1)
  • Credentials (11-17)
pkg/auth/providers/mock/identity_test.go (3)
pkg/auth/types/interfaces.go (1)
  • PostAuthenticateParams (61-67)
pkg/auth/providers/mock/identity.go (1)
  • NewIdentity (39-46)
pkg/auth/providers/mock/credentials.go (1)
  • Credentials (11-17)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Build (windows)
  • GitHub Check: Build (linux)
  • GitHub Check: Run pre-commit hooks
  • GitHub Check: Review Dependency Licenses
  • GitHub Check: Lint (golangci)
  • GitHub Check: Analyze (go)
  • GitHub Check: autofix
  • GitHub Check: Summary
🔇 Additional comments (1)
pkg/auth/providers/mock/identity.go (1)

174-205: Nit: wrap unmarshal/read errors with paths (already done) — LGTM.

Read, not-exist handling, and wrapping look good.

The logout command was using PrintfMarkdownToTUI with theme.Styles.Checkmark
which already contains ANSI color codes. When the markdown renderer processed
the string, it didn't strip the existing ANSI codes, causing them to leak
through to the output as raw escape sequences like [38;5;40m.

Changed to use PrintfMessageToTUI instead, which doesn't try to render markdown
and properly handles pre-styled content.

Affected messages:
- "Logged out {identity}"
- "Logged out {identity} with warnings"
- "Failed to log out {identity}"
- "Logged out provider {provider} (N identities)"

Before: [38;5;40m✓[0m Logged out core-identity/managers
After:  ✓ Logged out core-identity/managers

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

Co-Authored-By: Claude <[email protected]>
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)
cmd/auth_logout.go (1)

132-145: Inconsistent formatting for partial logout messages.

The identity partial logout (line 135) now uses plain text, but provider (line 192) and logout-all (line 312) partial messages still use markdown formatting (**bold**). Consider standardizing these to either all markdown or all plain text.

📜 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 19e37b3 and 9f33840.

📒 Files selected for processing (1)
  • cmd/auth_logout.go (2 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
cmd/**/*.go

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

cmd/**/*.go: Use Cobra's recommended command structure with a root command and subcommands
Implement each CLI command in a separate file under cmd/
Use Viper for managing configuration, environment variables, and flags in the CLI
Keep separation of concerns between CLI interface (cmd) and business logic
Use kebab-case for command-line flags
Provide comprehensive help text for all commands and flags
Include examples in Cobra command help
Use Viper for configuration management; support files, env vars, and flags with precedence flags > env > config > defaults
Follow single responsibility; separate command interface from business logic
Provide meaningful user feedback and include progress indicators for long-running operations
Provide clear error messages to users and troubleshooting hints where appropriate

cmd/**/*.go: New CLI commands must use the command registry pattern via CommandProvider and register through the command registry (see cmd/internal/registry.go).
Commands should embed *_usage.md via //go:embed and render with utils.PrintfMarkdown().
UI (prompts/status) must write to stderr; data outputs to stdout; logging only for system events (never for UI).
For non-standard execution paths, capture telemetry with telemetry.CaptureCmd(); normal paths are auto-enabled via RootCmd.ExecuteC(). Never capture user data.

Files:

  • cmd/auth_logout.go
**/*.go

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

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

**/*.go: Define interfaces for major functionality and use dependency injection to enable testability.
Generate mocks using go.uber.org/mock/mockgen via //go:generate directives (never manual mocks).
Prefer functional Options pattern instead of functions with many parameters for configuration.
Use context.Context only for cancellation, deadlines/timeouts, and request-scoped values; never for configuration or dependencies; context should be the first parameter.
All comments must end with periods (godot linter).
Preserve helpful existing comments; update them to match code when refactoring; only remove when incorrect, redundant, or code removed.
Organize imports in three groups (stdlib, third-party, Atmos) separated by blank lines and sorted alphabetically; maintain aliases cfg, log, u, errUtils.
Add defer perf.Track(atmosConfig, "pkg.FuncName")() (or nil if no atmosConfig) plus a blank line at the start of all public functions for performance tracking.
Use static errors from errors/errors.go; wrap with fmt.Errorf("%w") for chaining; use errors.Join for independent errors; avoid dynamic errors and string comparison; use errors.Is for checks.
Never use //revive:disable:file-length-limit to bypass file size limits; keep files small and focused (<600 lines).
Environment variables must be bound with viper.BindEnv using ATMOS_ prefix (e.g., viper.BindEnv("ATMOS_VAR", "ATMOS_VAR", "FALLBACK")).
Ensure cross-platform compatibility: use filepath.Join instead of hardcoded path separators and prefer SDKs over shelling out to binaries.

Files:

  • cmd/auth_logout.go
**/!(*_test).go

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

Document all exported functions, types, and methods with Go doc comments

Files:

  • cmd/auth_logout.go
{cmd,pkg/ui}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use theme colors from pkg/ui/theme/colors.go for UI elements.

Files:

  • cmd/auth_logout.go
🧬 Code graph analysis (1)
cmd/auth_logout.go (1)
pkg/utils/log_utils.go (1)
  • PrintfMessageToTUI (33-35)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Build (linux)
  • GitHub Check: Build (windows)
  • GitHub Check: Analyze (go)
  • GitHub Check: Run pre-commit hooks
  • GitHub Check: autofix
  • GitHub Check: Review Dependency Licenses
  • GitHub Check: Lint (golangci)
  • GitHub Check: Summary
🔇 Additional comments (3)
cmd/auth_logout.go (3)

139-142: Looks good.

Switching to plain text formatting is appropriate here, and the change is consistent with other failure messages in the file (lines 198, 318).


144-145: Looks good.

Plain text formatting is appropriate for this success message, and it's consistent with similar messages at line 324.


203-203: Looks good.

The switch to plain text formatting is appropriate and consistent with the identity logout success message.

osterman and others added 2 commits October 28, 2025 15:24
…ommands

Added fallback to ATMOS_IDENTITY environment variable when the --identity flag
is not provided for terraform commands. This makes the behavior consistent with
auth commands which already support ATMOS_IDENTITY.

Previously, ATMOS_IDENTITY only worked with `atmos auth` commands because the
environment variable binding was only set up in cmd/auth.go. Terraform commands
parsed flags manually in internal/exec/cli_utils.go without checking environment
variables.

Now users can set ATMOS_IDENTITY once and have it work across all commands:
- atmos auth whoami  # Uses ATMOS_IDENTITY ✅
- atmos terraform plan  # Uses ATMOS_IDENTITY ✅ (NEW)
- atmos terraform apply  # Uses ATMOS_IDENTITY ✅ (NEW)

Changes:
- Added viper.BindEnv("atmos_identity_temp", "ATMOS_IDENTITY") in ProcessCommandLineArgs()
- Import viper package in cli_utils.go
- Checks environment variable only if --identity flag not provided
- Uses viper instead of os.Getenv to comply with linter forbidigo rules

Priority: Flag > Environment Variable > Default Identity > Interactive Prompt

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

Co-Authored-By: Claude <[email protected]>
Added comprehensive tests to verify that the ATMOS_IDENTITY environment variable
is correctly used as a fallback when the --identity flag is not provided for
terraform commands.

Test coverage:
- Environment variable used when flag not provided
- Flag takes precedence over environment variable
- Empty identity when neither flag nor env var provided
- Works with both --identity and --identity=value syntax
- Identity flag parsing in different positions

These tests prevent future regressions of the ATMOS_IDENTITY environment variable
support that was added in commit 06c9239.

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

Co-Authored-By: Claude <[email protected]>
@github-actions github-actions bot removed the size/l Large size PR label Oct 28, 2025
Added regex sanitization to normalize credential_store backend names
in error messages that vary by platform:
- Mac/Windows: "system-keyring"
- Linux CI: "noop"

The keyring backend is selected at runtime based on platform availability.
Linux CI environments lack system keyring access, so they fall back to
the noop backend. This causes snapshot mismatches between platforms.

Solution: Sanitize all credential_store values to "<KEYRING_BACKEND>"
placeholder, similar to how we sanitize other environment-specific values.

Changes:
- Added step 13 in sanitizeOutput() to normalize credential_store values
- Regex matches "system-keyring", "noop", and "file" backends
- Regenerated snapshot with platform-agnostic placeholder
- Test now passes consistently across Mac, Windows, and Linux

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

Co-Authored-By: Claude <[email protected]>
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/cli_test.go (1)

402-407: Hardcoded "(mock)" pattern may miss other auth providers.

The regex only normalizes durations for the mock provider. If auth list includes AWS, Azure, or other providers, their expiration durations won't be normalized, potentially causing snapshot mismatches.

Consider a more general pattern:

-	// 12. Normalize credential expiration durations in auth list output.
-	// These appear as "● mock-identity (mock) [DEFAULT] 650202h14m" in tree output.
+	// 12. Normalize credential expiration durations in auth list output.
+	// These appear as "● identity-name (provider) [DEFAULT] 650202h14m" in tree output.
 	// The duration changes every minute, so normalize to "1h 0m" like other duration normalizations.
 	// Matches patterns like "650202h14m", "1h30m", "45m", etc. at the end of identity lines.
-	expirationDurationRegex := regexp.MustCompile(`(\(mock\)(?:\s+\[DEFAULT\])?)\s+\d+h\d+m\b`)
+	expirationDurationRegex := regexp.MustCompile(`(\([a-z-]+\)(?:\s+\[DEFAULT\])?)\s+\d+h\d+m\b`)
 	result = expirationDurationRegex.ReplaceAllString(result, "$1 1h 0m")

Alternatively, if mock-only tests are intentional, update the comment to clarify this limitation.

📜 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 7786c8f and c014d28.

📒 Files selected for processing (4)
  • tests/cli_test.go (1 hunks)
  • tests/snapshots/TestCLICommands_atmos_auth_list.stdout.golden (1 hunks)
  • tests/snapshots/TestCLICommands_atmos_auth_whoami_--identity_nonexistent.stderr.golden (1 hunks)
  • tests/test-cases/auth-cli.yaml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/snapshots/TestCLICommands_atmos_auth_whoami_--identity_nonexistent.stderr.golden
  • tests/snapshots/TestCLICommands_atmos_auth_list.stdout.golden
🧰 Additional context used
📓 Path-based instructions (3)
**/*_test.go

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

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

**/*_test.go: Prefer unit tests with mocks over integration tests; use interfaces + DI; table-driven tests for coverage; target >80% coverage.
Tests must exercise production code paths; avoid duplicating implementation logic in tests.
Use t.Skipf("reason") with clear context when skipping tests; CLI tests may auto-build temp binaries.

Files:

  • tests/cli_test.go
**/*.go

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

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

**/*.go: Define interfaces for major functionality and use dependency injection to enable testability.
Generate mocks using go.uber.org/mock/mockgen via //go:generate directives (never manual mocks).
Prefer functional Options pattern instead of functions with many parameters for configuration.
Use context.Context only for cancellation, deadlines/timeouts, and request-scoped values; never for configuration or dependencies; context should be the first parameter.
All comments must end with periods (godot linter).
Preserve helpful existing comments; update them to match code when refactoring; only remove when incorrect, redundant, or code removed.
Organize imports in three groups (stdlib, third-party, Atmos) separated by blank lines and sorted alphabetically; maintain aliases cfg, log, u, errUtils.
Add defer perf.Track(atmosConfig, "pkg.FuncName")() (or nil if no atmosConfig) plus a blank line at the start of all public functions for performance tracking.
Use static errors from errors/errors.go; wrap with fmt.Errorf("%w") for chaining; use errors.Join for independent errors; avoid dynamic errors and string comparison; use errors.Is for checks.
Never use //revive:disable:file-length-limit to bypass file size limits; keep files small and focused (<600 lines).
Environment variables must be bound with viper.BindEnv using ATMOS_ prefix (e.g., viper.BindEnv("ATMOS_VAR", "ATMOS_VAR", "FALLBACK")).
Ensure cross-platform compatibility: use filepath.Join instead of hardcoded path separators and prefer SDKs over shelling out to binaries.

Files:

  • tests/cli_test.go
tests/**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*_test.go: Integration tests, when necessary, must live under tests/.
Use test precondition helpers from tests/test_preconditions.go so tests skip gracefully when prerequisites are missing.

Files:

  • tests/cli_test.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Analyze (go)
  • GitHub Check: Lint (golangci)
  • GitHub Check: Build (macos)
  • GitHub Check: Build (windows)
  • GitHub Check: Build (linux)
  • GitHub Check: website-deploy-preview
  • GitHub Check: autofix
  • GitHub Check: Review Dependency Licenses
  • GitHub Check: Run pre-commit hooks
  • GitHub Check: Summary
🔇 Additional comments (3)
tests/test-cases/auth-cli.yaml (1)

300-304: Clarify fallback logic and verify error message alignment.

The comment flags this behavior as needing investigation, which is concerning for a consolidated fix. Two clarifications needed:

  1. Is the fallback to default identity when a nonexistent identity is requested the intended behavior introduced by the consolidation, or a side effect?
  2. Does the error message "no credentials found" accurately describe what happened? When identity resolution falls back, should the error be more explicit?

Verify the implementation of retrieveCredentialWithFallback() and GetDefaultIdentity() to confirm this behavior is intentional and properly tested.

tests/cli_test.go (2)

396-400: LGTM! Timestamp normalization stabilizes auth whoami snapshots.

The regex correctly matches timestamp patterns and the fixed replacement ensures consistent snapshot comparisons across test runs.


409-413: LGTM! Credential store normalization handles platform differences correctly.

The regex matches known keyring backends and the placeholder ensures snapshot consistency across different platforms.

osterman and others added 2 commits October 29, 2025 08:43
Changed credential_store sanitization from '<KEYRING_BACKEND>' to 'keyring'
for better readability and consistency.

The angle-bracket syntax looks like HTML/template code and doesn't represent
any real value. Using the keyword 'keyring' is simpler and more accurate
since all backends (system-keyring, noop, file) are keyring implementations.

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

Co-Authored-By: Claude <[email protected]>
Changed from 'keyring' to 'keyring-placeholder' to make it clear the
value has been sanitized while still resembling actual keyring backend
names (system-keyring, noop, file).

Benefits:
- Clearly indicates sanitization (the "-placeholder" suffix)
- Resembles actual values without being confused for a real backend
- Avoids template-like syntax (angle brackets)
- More explicit than generic "keyring"

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

Co-Authored-By: Claude <[email protected]>
Fixed linting issue where inline comment at line 100 was missing
a terminating period. All comment sentences must end with periods
per godot linter rules.

Changed:
- "Don't fail config loading if this step fails, just log it"
+ "Don't fail config loading if this step fails, just log it."

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

Co-Authored-By: Claude <[email protected]>
…tity selection

- Add TTY check using term.IsTTYSupportForStdin() to fail fast in CI/non-TTY environments
- Change InitCliConfig parameter from true to false to skip unnecessary stack processing
- Align error wrapping with coding guidelines using fmt.Errorf("%w: %w") pattern
- Improves error handling and prevents hanging in headless environments
- Add t.Parallel() to case sensitivity test for faster execution
- Add CLI docs link to blog post for better discoverability
- Clarify flag vs env var precedence (--identity overrides ATMOS_IDENTITY)
- Add language markers (text) to PRD code blocks for proper markdown linting
The regex was requiring both hours and minutes (\d+h\d+m), but Go's duration formatting
omits minutes when they are zero (650194h instead of 650194h0m). Updated regex to make
minutes optional with (?:\d+m)? to handle both formats consistently.
Changed from /cli/commands/auth/atmos_auth (non-existent) to /cli/commands/auth/usage
which is the correct URL for the main auth documentation page.
Add mandatory guidelines for verifying documentation URLs before adding links:
- How to find correct URLs using find and grep
- Common mistakes (using command names vs file names)
- Importance of checking slug frontmatter
- Step-by-step verification approach

Prevents broken links like /cli/commands/auth/atmos_auth (non-existent) when
the correct URL is /cli/commands/auth/usage.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
docs/prd/credential-retrieval-consolidation.md (2)

282-282: Heading formatting (MD036).

Line 282 uses emphasis (**Recommended Approach**) where a proper markdown heading (## Recommended Approach) would be more idiomatic.

-**Recommended Approach**
+## Recommended Approach

47-47: Minor wording: "exact same" is redundant.

Consider simplifying for conciseness:

-...failed (e.g., `atmos terraform plan`) with the exact same credentials.
+...failed (e.g., `atmos terraform plan`) with identical credentials.
📜 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 23997c8 and 8f340cc.

📒 Files selected for processing (5)
  • cmd/terraform_utils.go (2 hunks)
  • docs/prd/credential-retrieval-consolidation.md (1 hunks)
  • pkg/auth/manager_case_test.go (1 hunks)
  • tests/cli_test.go (1 hunks)
  • website/blog/2025-10-29-auth-ux-improvements.mdx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/auth/manager_case_test.go
  • website/blog/2025-10-29-auth-ux-improvements.mdx
🧰 Additional context used
📓 Path-based instructions (7)
**/*_test.go

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

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

**/*_test.go: Prefer unit tests with mocks over integration tests; use interfaces + DI; table-driven tests for coverage; target >80% coverage.
Tests must exercise production code paths; avoid duplicating implementation logic in tests.
Use t.Skipf("reason") with clear context when skipping tests; CLI tests may auto-build temp binaries.

Files:

  • tests/cli_test.go
**/*.go

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

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

**/*.go: Define interfaces for major functionality and use dependency injection to enable testability.
Generate mocks using go.uber.org/mock/mockgen via //go:generate directives (never manual mocks).
Prefer functional Options pattern instead of functions with many parameters for configuration.
Use context.Context only for cancellation, deadlines/timeouts, and request-scoped values; never for configuration or dependencies; context should be the first parameter.
All comments must end with periods (godot linter).
Preserve helpful existing comments; update them to match code when refactoring; only remove when incorrect, redundant, or code removed.
Organize imports in three groups (stdlib, third-party, Atmos) separated by blank lines and sorted alphabetically; maintain aliases cfg, log, u, errUtils.
Add defer perf.Track(atmosConfig, "pkg.FuncName")() (or nil if no atmosConfig) plus a blank line at the start of all public functions for performance tracking.
Use static errors from errors/errors.go; wrap with fmt.Errorf("%w") for chaining; use errors.Join for independent errors; avoid dynamic errors and string comparison; use errors.Is for checks.
Never use //revive:disable:file-length-limit to bypass file size limits; keep files small and focused (<600 lines).
Environment variables must be bound with viper.BindEnv using ATMOS_ prefix (e.g., viper.BindEnv("ATMOS_VAR", "ATMOS_VAR", "FALLBACK")).
Ensure cross-platform compatibility: use filepath.Join instead of hardcoded path separators and prefer SDKs over shelling out to binaries.

Files:

  • tests/cli_test.go
  • cmd/terraform_utils.go
tests/**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*_test.go: Integration tests, when necessary, must live under tests/.
Use test precondition helpers from tests/test_preconditions.go so tests skip gracefully when prerequisites are missing.

Files:

  • tests/cli_test.go
cmd/**/*.go

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

cmd/**/*.go: Use Cobra's recommended command structure with a root command and subcommands
Implement each CLI command in a separate file under cmd/
Use Viper for managing configuration, environment variables, and flags in the CLI
Keep separation of concerns between CLI interface (cmd) and business logic
Use kebab-case for command-line flags
Provide comprehensive help text for all commands and flags
Include examples in Cobra command help
Use Viper for configuration management; support files, env vars, and flags with precedence flags > env > config > defaults
Follow single responsibility; separate command interface from business logic
Provide meaningful user feedback and include progress indicators for long-running operations
Provide clear error messages to users and troubleshooting hints where appropriate

cmd/**/*.go: New CLI commands must use the command registry pattern via CommandProvider and register through the command registry (see cmd/internal/registry.go).
Commands should embed *_usage.md via //go:embed and render with utils.PrintfMarkdown().
UI (prompts/status) must write to stderr; data outputs to stdout; logging only for system events (never for UI).
For non-standard execution paths, capture telemetry with telemetry.CaptureCmd(); normal paths are auto-enabled via RootCmd.ExecuteC(). Never capture user data.

Files:

  • cmd/terraform_utils.go
**/!(*_test).go

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

Document all exported functions, types, and methods with Go doc comments

Files:

  • cmd/terraform_utils.go
{cmd,pkg/ui}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use theme colors from pkg/ui/theme/colors.go for UI elements.

Files:

  • cmd/terraform_utils.go
docs/prd/[a-z0-9-]*.md

📄 CodeRabbit inference engine (CLAUDE.md)

All PRDs must be placed under docs/prd/ with kebab-case filenames.

Files:

  • docs/prd/credential-retrieval-consolidation.md
🧠 Learnings (2)
📚 Learning: 2025-09-13T18:06:07.674Z
Learnt from: samtholiya
PR: cloudposse/atmos#1466
File: toolchain/list.go:39-42
Timestamp: 2025-09-13T18:06:07.674Z
Learning: In the cloudposse/atmos repository, for UI messages in the toolchain package, use utils.PrintfMessageToTUI instead of log.Error or fmt.Fprintln(os.Stderr, ...). Import pkg/utils with alias "u" to follow the established pattern.

Applied to files:

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

Applied to files:

  • cmd/terraform_utils.go
🧬 Code graph analysis (1)
cmd/terraform_utils.go (5)
cmd/auth.go (1)
  • IdentityFlagSelectValue (12-12)
internal/tui/templates/term/term_writer.go (1)
  • IsTTYSupportForStdin (133-135)
errors/error_funcs.go (1)
  • CheckErrorPrintAndExit (75-98)
errors/errors.go (3)
  • ErrDefaultIdentity (382-382)
  • ErrInitializeCLIConfig (341-341)
  • ErrFailedToInitializeAuthManager (391-391)
pkg/config/config.go (1)
  • InitCliConfig (25-62)
🪛 LanguageTool
docs/prd/credential-retrieval-consolidation.md

[typographical] ~21-~21: Consider using typographic quotation marks here.
Context: ...dling:** Properly propagates errors vs. "not found" conditions ### Refactored Call Sites ...

(EN_QUOTES)


[style] ~47-~47: ‘exact same’ might be wordy. Consider a shorter alternative.
Context: ...(e.g., atmos terraform plan) with the exact same credentials. ## Original Architecture ...

(EN_WORDINESS_PREMIUM_EXACT_SAME)


[typographical] ~124-~124: Consider using a typographic opening quote here.
Context: ...tion** without a shared abstraction for "retrieve credential from any available s...

(EN_QUOTES)


[typographical] ~124-~124: Consider using a typographic close quote here.
Context: ...ve credential from any available source." ## Why This Manifested in Terraform Co...

(EN_QUOTES)


[typographical] ~156-~156: Consider using typographic quotation marks here.
Context: ...ion** Three separate implementations of "retrieve credential" logic with inconsistent behavior. ### ...

(EN_QUOTES)


[typographical] ~159-~159: Consider using a typographic opening quote here.
Context: ...ngle method/interface that encapsulates "retrieve credential from any available s...

(EN_QUOTES)


[typographical] ~159-~159: Consider using a typographic close quote here.
Context: ...y available source with proper fallback." ### 3. Implicit Assumptions `retri...

(EN_QUOTES)


[typographical] ~166-~166: Consider using a typographic opening quote here.
Context: ...s, headless systems, no D-Bus) - We use "noop" keyring in these environments ###...

(EN_QUOTES)


[typographical] ~166-~166: Consider using a typographic close quote here.
Context: ...adless systems, no D-Bus) - We use "noop" keyring in these environments ### 4. *...

(EN_QUOTES)

🪛 markdownlint-cli2 (0.18.1)
docs/prd/credential-retrieval-consolidation.md

282-282: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Review Dependency Licenses
  • GitHub Check: Build (windows)
  • GitHub Check: Build (linux)
  • GitHub Check: Build (macos)
  • GitHub Check: Run pre-commit hooks
  • GitHub Check: website-deploy-preview
  • GitHub Check: Analyze (go)
  • GitHub Check: Lint (golangci)
  • GitHub Check: autofix
  • GitHub Check: Summary
🔇 Additional comments (5)
docs/prd/credential-retrieval-consolidation.md (1)

1-454: Comprehensive and well-structured PRD. Content is accurate and informative.

The document effectively explains the problem, analyzes root causes, documents the implemented solution, and outlines testing strategy. The implementation summary correctly describes loadCredentialsWithFallback and the three refactored call sites. Excellent historical context and architectural rationale.

tests/cli_test.go (1)

396-413: Solid snapshot normalizations.

These three additions normalize time-sensitive and platform-specific values to keep snapshot tests stable:

  • Last Updated timestamps (lines 396-400)
  • Mock credential expiration durations (lines 402-407)
  • Platform-specific credential store backends (lines 409-413)

Each follows the existing normalization pattern in this function, with clear comments explaining the purpose. The duration normalization is intentionally scoped to mock identities, which makes sense for test stability without over-normalizing real output.

cmd/terraform_utils.go (3)

11-15: Imports look tidy and on‑spec.

Atmos imports are grouped and alphabetized; aliases (errUtils, cfg, log) follow house style. All good.


112-116: Identity precedence preserved — nice.

Only override when flag is non‑empty; preserves ATMOS_IDENTITY from env. LGTM.


96-97: Bug: fmt.Errorf has multiple %w (invalid) — will panic at runtime.

Replace double‑%w formatting with errors.Join (or a single wrap). This preserves sentinel checks via errors.Is and aligns with earlier usage in this file. As per coding guidelines.

Apply:

-            errUtils.CheckErrorPrintAndExit(fmt.Errorf("%w: %w", errUtils.ErrInitializeCLIConfig, err), "", "")
+            errUtils.CheckErrorPrintAndExit(errors.Join(errUtils.ErrInitializeCLIConfig, err), "", "")

-            errUtils.CheckErrorPrintAndExit(fmt.Errorf("%w: %w", errUtils.ErrFailedToInitializeAuthManager, err), "", "")
+            errUtils.CheckErrorPrintAndExit(errors.Join(errUtils.ErrFailedToInitializeAuthManager, err), "", "")

-            errUtils.CheckErrorPrintAndExit(fmt.Errorf("%w: %w", errUtils.ErrDefaultIdentity, err), "", "")
+            errUtils.CheckErrorPrintAndExit(errors.Join(errUtils.ErrDefaultIdentity, err), "", "")

Also applies to: 102-103, 108-109

⛔ Skipped due to learnings
Learnt from: CR
PR: cloudposse/atmos#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-24T14:51:19.355Z
Learning: Applies to **/*.go : Use static errors from errors/errors.go; wrap with fmt.Errorf("%w") for chaining; use errors.Join for independent errors; avoid dynamic errors and string comparison; use errors.Is for checks.
Learnt from: Benbentwo
PR: cloudposse/atmos#1475
File: pkg/auth/identities/aws/user.go:141-145
Timestamp: 2025-09-10T22:38:42.212Z
Learning: ErrWrappingFormat is correctly defined as "%w: %w" in the errors package and is used throughout the codebase to wrap two error types together. The usage fmt.Errorf(errUtils.ErrWrappingFormat, errUtils.ErrAuthAwsFileManagerFailed, err) is the correct pattern when both arguments are error types.
Learnt from: osterman
PR: cloudposse/atmos#1695
File: pkg/auth/manager.go:169-171
Timestamp: 2025-10-22T14:55:44.014Z
Learning: Go 1.20+ supports multiple %w verbs in fmt.Errorf, which returns an error implementing Unwrap() []error. This is valid and does not panic. Atmos uses Go 1.24.8 and configures errorlint with errorf-multi: true to validate this pattern.
Learnt from: Benbentwo
PR: cloudposse/atmos#1475
File: pkg/auth/identities/aws/user.go:141-145
Timestamp: 2025-09-10T22:38:42.212Z
Learning: The user confirmed that the errors package has an error string wrapping format, contradicting the previous learning about ErrWrappingFormat being invalid. The current usage of fmt.Errorf(errUtils.ErrWrappingFormat, errUtils.ErrAuthAwsFileManagerFailed, err) appears to be the correct pattern.
Learnt from: CR
PR: cloudposse/atmos#0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-09-23T02:30:42.362Z
Learning: Applies to **/*.go : Wrap errors with context using fmt.Errorf("context: %w", err)

Changed 'retrieveCredentialWithFallback' to 'loadCredentialsWithFallback' in two
locations (lines 244, 284) to match the actual implementation and other references
throughout the document (lines 5, 15, 178, 181).

The implemented function is loadCredentialsWithFallback, which better describes the
behavior (load = fetch + process) compared to retrieve (just fetch).
… selection

The interactive selector (huh.NewForm) requires:
- stdin TTY for user input
- stdout TTY for TUI rendering

Updated condition to check both term.IsTTYSupportForStdin() and
term.IsTTYSupportForStdout() to prevent hanging in environments where
only one is available (e.g., piped stdout).
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 (3)
website/blog/2025-10-29-auth-ux-improvements.mdx (3)

30-48: Document case-insensitive matching as potential breaking change.

Case-insensitive identity name matching is a semantic change that could affect existing workflows or scripts that relied on case-sensitive matching. Consider adding a note flagging this as a behavior change and any migration considerations for users with overlapping identity names that differ only in case.

Example addition:

+**Note**: This behavior changed in this version. If you have identity names that differ only in case (e.g., `dev-admin` and `Dev-Admin`), Atmos will now treat them as the same identity with the first match returned. Ensure your `atmos.yaml` uses consistent casing to avoid confusion.

50-76: Add clarity on status indicator scope and refresh.

The post introduces status indicators (✓, ⚠, ✗) in auth list showing credential expiry, but doesn't explain:

  • How "expiring soon" is defined (threshold)
  • Whether status is real-time or cached
  • When indicators update or if they require command re-run

These details help users understand the reliability of the status display.

Example addition after line 75:

+Status is refreshed each time you run `atmos auth list`. Credentials are considered "expiring soon" if they will expire within 15 minutes.

77-86: Clarify legacy path migration scope.

The legacy path warning only applies to ~/.aws/atmos/ path, but the blog doesn't explain which commands trigger this warning or whether it appears once per session globally or per identity. Also, the suggested migration command is atmos auth login, but it's not clear if this applies to all identities or requires explicit identity specification.

Consider clarifying:

-If you're using the legacy `~/.aws/atmos/` credential path, Atmos will now show the migration warning only once per execution instead of repeatedly:
+If you're using the legacy `~/.aws/atmos/` credential path (from older Atmos versions), the next time you run an `auth` or `terraform` command, you'll see a one-time migration warning per session:
📜 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 8f340cc and e68c68e.

📒 Files selected for processing (2)
  • CLAUDE.md (1 hunks)
  • website/blog/2025-10-29-auth-ux-improvements.mdx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
website/**

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

website/**: Update website documentation in website/ when adding features
Ensure consistency between CLI help text and website documentation
Follow the website's documentation structure and style
Keep website code in website/ and follow its architecture/style; test changes locally
Keep CLI and website documentation in sync; document new features with examples and use cases

Files:

  • website/blog/2025-10-29-auth-ux-improvements.mdx
🧠 Learnings (4)
📚 Learning: 2025-10-24T14:51:19.355Z
Learnt from: CR
PR: cloudposse/atmos#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-24T14:51:19.355Z
Learning: Applies to website/docs/cli/commands/**/*.md : All commands/flags require Docusaurus docs under website/docs/cli/commands/.

Applied to files:

  • CLAUDE.md
📚 Learning: 2025-10-24T14:51:19.355Z
Learnt from: CR
PR: cloudposse/atmos#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-24T14:51:19.355Z
Learning: All command additions must include Docusaurus docs and website build steps as part of the workflow (website build should succeed).

Applied to files:

  • CLAUDE.md
📚 Learning: 2025-10-24T14:51:19.355Z
Learnt from: CR
PR: cloudposse/atmos#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-24T14:51:19.355Z
Learning: Applies to website/blog/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-*.md : PRs labeled minor or major must include a blog post with filename date prefix and feature name; include <!--truncate--> and proper tags.

Applied to files:

  • CLAUDE.md
📚 Learning: 2025-09-23T02:30:42.362Z
Learnt from: CR
PR: cloudposse/atmos#0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-09-23T02:30:42.362Z
Learning: Applies to website/** : Keep CLI and website documentation in sync; document new features with examples and use cases

Applied to files:

  • CLAUDE.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Build (linux)
  • GitHub Check: Build (macos)
  • GitHub Check: Build (windows)
  • GitHub Check: Run pre-commit hooks
  • GitHub Check: Review Dependency Licenses
  • GitHub Check: website-deploy-preview
  • GitHub Check: autofix
  • GitHub Check: Analyze (go)
  • GitHub Check: Lint (golangci)
  • GitHub Check: Summary
🔇 Additional comments (3)
CLAUDE.md (1)

343-350: CLAUDE.md blog post guidance overstates CI enforcement—remove .mdx requirement or clarify it's aspirational, not enforced.

CI only validates that a blog post file exists in website/blog/ matching .(md|mdx)$ for PRs labeled minor/major targeting main. It doesn't enforce .mdx extension specifically, YAML frontmatter structure, <!--truncate--> placement, or tag format. The existing repo has a mix of .md and .mdx files, all passing CI. Either align guidance with actual CI checks or note these are style preferences, not CI-enforced requirements.

Likely an incorrect or invalid review comment.

website/blog/2025-10-29-auth-ux-improvements.mdx (2)

13-28: Clarify whether --identity without a value is a documented feature or needs implementation.

The blog post shows atmos terraform plan mycomponent -s mystack --identity (without a value) triggering an interactive selector, but Atmos CLI documentation shows --identity as a value-taking flag. Either this is a new feature being added in this PR that needs code verification, or the example should specify a flag value. Confirm that interactive selection without a value is actually implemented in the underlying code changes, and verify the claim that this brings terraform commands "up to parity with other auth commands."


28-28: Documentation reference is accurate and current.

The path /cli/commands/auth/usage exists and the file comprehensively covers all features mentioned in the blog post: interactive identity selection, terraform integration, and authentication precedence rules via the --identity flag. The usage documentation explicitly documents interactive prompts when multiple or no default identities are configured, and includes terraform examples using atmos auth exec -- terraform plan and direct terraform commands.

…espace is set

When ignore_trailing_whitespace is enabled for a test, snapshots are now generated
without trailing whitespace. This prevents cross-platform snapshot failures caused by
terminal width differences (lipgloss padding varies between Mac/Linux).

Changes:
- Update snapshot generation to strip trailing whitespace if ignore_trailing_whitespace is set
- Apply fix to both TTY and non-TTY snapshot generation paths
- Enable ignore_trailing_whitespace for 'atmos auth whoami --identity nonexistent' test
- Regenerate snapshot with trailing whitespace removed

This fixes CI failures where Mac generates snapshots with lipgloss padding but Linux
environments produce output without that padding.
Replaced incorrect example using /cli/commands/auth/usage with a concrete,
step-by-step example using auth-user-configure.mdx demonstrating:
- How to find the doc file with find command
- How to check slug in frontmatter with grep
- How to verify the URL with existing links

Shows expected output for each step and clarifies that MDX filenames
(without extension) typically match the URL path unless overridden by slug.
@aknysh aknysh merged commit f9bcb1f into main Oct 29, 2025
55 checks passed
@aknysh aknysh deleted the fix-terraform-auth-context branch October 29, 2025 16:50
@github-actions
Copy link

These changes were released in v1.196.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/xl Extra large size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants