Skip to content
Open
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
af9cf3d
Fix markdown code fence in Nerd Fonts installation instructions (#1917)
osterman Dec 28, 2025
f2384ad
Address PR review comments for auth registry refactor
osterman Dec 29, 2025
83e8adf
Fix NoOptDefVal preprocessing for identity flag in auth commands
osterman Dec 29, 2025
df871c1
Add documentation for --identity flag placement best practices
osterman Dec 29, 2025
03d98f1
Address PR review comments: constants and security annotations
osterman Dec 29, 2025
50dca0a
Add --identity flag documentation to global flags reference
osterman Dec 29, 2025
3f3ea78
Refactor preprocessing architecture with preprocessArgs orchestrator
osterman Dec 30, 2025
d7ff75a
Address PR review comments
osterman Jan 2, 2026
9e12803
Add unit tests for cmd/auth package to improve coverage
osterman Jan 2, 2026
3aeeb0a
Fix cross-platform compatibility for exec tests
osterman Jan 6, 2026
c66f3e6
Merge origin/main into osterman/auth-registry-refactor
osterman Jan 10, 2026
b352c69
Merge origin/main into osterman/auth-registry-refactor
osterman Jan 13, 2026
5546d2b
test: Add tests for --profile flag in auth commands (fixes #1973)
osterman Jan 15, 2026
f45fdc9
docs: Reorganize terraform usage examples into distinct sections
osterman Jan 16, 2026
02a76d7
test: Regenerate auth command golden snapshots
osterman Jan 16, 2026
95e491a
Merge remote-tracking branch 'origin/main' into osterman/auth-registr…
osterman Jan 16, 2026
ed6c41e
fix: Windows test failures and website build
osterman Jan 16, 2026
2d79d7e
fix: Add IsExperimental method to AuthCommandProvider
osterman Jan 17, 2026
caccbca
[autofix.ci] apply automated fixes
autofix-ci[bot] Jan 17, 2026
ec40c5a
[autofix.ci] apply automated fixes (attempt 2/3)
autofix-ci[bot] Jan 17, 2026
d56631c
[autofix.ci] apply automated fixes (attempt 3/3)
autofix-ci[bot] Jan 17, 2026
b2cf1c2
Merge branch 'main' into osterman/auth-registry-refactor
osterman Jan 17, 2026
2e097bb
[autofix.ci] apply automated fixes
autofix-ci[bot] Jan 17, 2026
cfb8fc8
fix: Address CodeRabbit review comments for auth-registry-refactor
osterman Jan 18, 2026
ce3ae23
Merge branch 'main' into osterman/auth-registry-refactor
osterman Jan 20, 2026
3bc2495
chore: Regenerate golden snapshots for experimental auth command
osterman Jan 20, 2026
e6a589c
Merge branch 'main' into osterman/auth-registry-refactor
osterman Jan 24, 2026
3a95d93
Merge branch 'main' into osterman/auth-registry-refactor
osterman Mar 10, 2026
69f1ab0
fix: Address CodeRabbit review findings in auth commands
osterman Mar 10, 2026
d8c0c60
fix: Migrate auth commands from legacy u.Print* to ui/data layer and …
osterman Mar 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 0 additions & 50 deletions cmd/auth.go

This file was deleted.

140 changes: 140 additions & 0 deletions cmd/auth/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package auth

import (
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/cloudposse/atmos/cmd/auth/user"
"github.com/cloudposse/atmos/cmd/internal"
cfg "github.com/cloudposse/atmos/pkg/config"
"github.com/cloudposse/atmos/pkg/flags"
"github.com/cloudposse/atmos/pkg/flags/compat"
"github.com/cloudposse/atmos/pkg/perf"
)

const (
// IdentityFlagName is the name of the identity flag (from pkg/config/const.go).
IdentityFlagName = cfg.IdentityFlagName
// IdentityFlagSelectValue is the sentinel value for interactive selection (from pkg/config/const.go).
IdentityFlagSelectValue = cfg.IdentityFlagSelectValue
)

// authParser handles persistent flags for auth command.
var authParser *flags.StandardParser

// authCmd groups authentication-related subcommands.
var authCmd = &cobra.Command{
Use: "auth",
Short: "Authenticate with cloud providers and identity services.",
Long: "Obtain, refresh, and configure credentials from external identity providers such as AWS SSO, Vault, or OIDC. Provides the necessary authentication context for tools like Terraform and Helm to interact with cloud infrastructure.",
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: false},
ValidArgsFunction: ComponentsArgCompletion,
}

func init() {
defer perf.Track(nil, "auth.init")()

// Create parser with persistent identity flag.
authParser = flags.NewStandardParser(
flags.WithStringFlag(IdentityFlagName, "i", "", "Specify the target identity to assume. Use without value to interactively select."),
flags.WithNoOptDefVal(IdentityFlagName, IdentityFlagSelectValue),
flags.WithEnvVars(IdentityFlagName, "ATMOS_IDENTITY"),
)

// Register as persistent flags (inherited by subcommands).
authParser.RegisterPersistentFlags(authCmd)

// Bind to Viper for environment variable support.
if err := authParser.BindToViper(viper.GetViper()); err != nil {
panic(err)
}

// Add identity completion.
AddIdentityCompletion(authCmd)

// Add user subcommand (nested command structure).
authCmd.AddCommand(user.AuthUserCmd)

// Register this command with the registry.
internal.Register(&AuthCommandProvider{})
}
Comment on lines +34 to +60
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for IDENTITY env var usage
rg -i "IDENTITY" --type go -A 2 -B 2 | head -100

# Also search for any documentation about this env var
fd -e md -e txt | xargs rg -i "IDENTITY.*env" 2>/dev/null | head -50

Repository: cloudposse/atmos

Length of output: 14180


🏁 Script executed:

#!/bin/bash
# Check if IDENTITY is referenced in similar flag patterns
rg "WithEnvVars.*," --type go -A 1 -B 1 | head -80

Repository: cloudposse/atmos

Length of output: 5327


🏁 Script executed:

#!/bin/bash
# Check git history for when IDENTITY was added
git log --oneline --all -- cmd/auth/auth.go | head -20

# Look for any migration or changelog entries
fd -e md | xargs rg -l "IDENTITY\|migration\|backward" | head -10

Repository: cloudposse/atmos

Length of output: 103


🏁 Script executed:

#!/bin/bash
# Search for direct usage of IDENTITY env var
rg '"IDENTITY"|`IDENTITY`' --type go | grep -v "test\|comment\|WithEnvVars" | head -20

Repository: cloudposse/atmos

Length of output: 221


🏁 Script executed:

#!/bin/bash
# Also check if IDENTITY is referenced in any documentation as a supported feature
rg 'IDENTITY.*env' --type md | head -30

Repository: cloudposse/atmos

Length of output: 2949


Remove undocumented IDENTITY env var fallback across three locations.

Guidelines require ATMOS_ prefix for Atmos-specific configuration variables. IDENTITY is not a standard/canonical environment variable like SHELL or GITHUB_TOKEN, so it should not appear as a fallback. Documentation only references ATMOS_IDENTITY.

Update these three locations to remove the IDENTITY fallback:

  • cmd/auth/auth.go:40
  • cmd/terraform/flags.go (identity flag)
  • pkg/flags/global_registry.go (global identity definition)
Changes needed
-		flags.WithEnvVars(IdentityFlagName, "ATMOS_IDENTITY", "IDENTITY"),
+		flags.WithEnvVars(IdentityFlagName, "ATMOS_IDENTITY"),
🤖 Prompt for AI Agents
In `@cmd/auth/auth.go` around lines 34 - 60, Remove the undocumented fallback
"IDENTITY" env var from the identity flag definitions and keep only the ATMOS_
prefixed var; specifically, in the init function where flags.NewStandardParser
is called for IdentityFlagName (the flags.WithEnvVars call in cmd/auth/auth.go),
remove "IDENTITY" from the env var list so it only uses "ATMOS_IDENTITY"; apply
the same change to the identity flag definition in cmd/terraform/flags.go (the
IdentityFlagName / NewStandardParser or WithEnvVars usage) and to the global
identity definition in pkg/flags/global_registry.go (where the global identity
env var list is defined) so none of these include "IDENTITY" and only reference
"ATMOS_IDENTITY".


// AuthCommandProvider implements the CommandProvider interface.
type AuthCommandProvider struct{}

// GetCommand returns the auth command.
func (a *AuthCommandProvider) GetCommand() *cobra.Command {
return authCmd
}

// GetName returns the command name.
func (a *AuthCommandProvider) GetName() string {
return "auth"
}

// GetGroup returns the command group for help organization.
func (a *AuthCommandProvider) GetGroup() string {
return "Pro Features"
}

// GetFlagsBuilder returns the flags builder for this command.
func (a *AuthCommandProvider) GetFlagsBuilder() flags.Builder {
return authParser
}

// GetPositionalArgsBuilder returns the positional args builder for this command.
func (a *AuthCommandProvider) GetPositionalArgsBuilder() *flags.PositionalArgsBuilder {
return nil
}

// GetCompatibilityFlags returns compatibility flags for this command.
func (a *AuthCommandProvider) GetCompatibilityFlags() map[string]compat.CompatibilityFlag {
return nil
}

// GetAliases returns command aliases.
func (a *AuthCommandProvider) GetAliases() []internal.CommandAlias {
return nil
}

// IsExperimental returns whether this command is experimental.
// Auth commands are currently experimental as part of Pro Features.
func (a *AuthCommandProvider) IsExperimental() bool {
return true
}

// GetIdentityFromFlags retrieves the identity flag value, handling the NoOptDefVal quirk.
// The identity flag uses NoOptDefVal="__SELECT__" which causes Viper to always return "__SELECT__"
// after parsing, even when the flag wasn't provided. This function checks the actual flag state
// to return the correct value.
//
// Returns:
// - Empty string if --identity was not provided (use config/env default).
// - IdentityFlagSelectValue if --identity was provided without a value (user wants selection).
// - The actual value if --identity=value was provided.
func GetIdentityFromFlags(cmd *cobra.Command) string {
defer perf.Track(nil, "auth.GetIdentityFromFlags")()

flag := cmd.Flags().Lookup(IdentityFlagName)
if flag == nil {
// Check persistent flags if not found in local flags.
flag = cmd.InheritedFlags().Lookup(IdentityFlagName)
}

if flag == nil || !flag.Changed {
// Flag was not explicitly set on command line.
// Return empty string to signal "use default from config/env".
return ""
}

// Flag was explicitly set - return its value.
// This could be:
// - The actual value (--identity=prod or --identity prod).
// - IdentityFlagSelectValue (--identity without value).
return flag.Value.String()
}

// GetAuthCmd returns the auth command for subcommand registration.
func GetAuthCmd() *cobra.Command {
return authCmd
}
135 changes: 135 additions & 0 deletions cmd/auth/auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package auth

import (
"testing"

"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
)

func TestAuthCommandProvider(t *testing.T) {
provider := &AuthCommandProvider{}

assert.Equal(t, "auth", provider.GetName())
assert.Equal(t, "Pro Features", provider.GetGroup())
assert.NotNil(t, provider.GetCommand())
assert.NotNil(t, provider.GetFlagsBuilder())
}

func TestGetIdentityFromFlags(t *testing.T) {
tests := []struct {
name string
setupCmd func(*cobra.Command)
expected string
}{
{
name: "flag not set",
setupCmd: func(cmd *cobra.Command) {
cmd.Flags().String(IdentityFlagName, "", "identity")
},
expected: "",
},
{
name: "flag set with value",
setupCmd: func(cmd *cobra.Command) {
cmd.Flags().String(IdentityFlagName, "", "identity")
_ = cmd.Flags().Set(IdentityFlagName, "prod-admin")
},
expected: "prod-admin",
},
{
name: "flag set without value (NoOptDefVal)",
setupCmd: func(cmd *cobra.Command) {
cmd.Flags().String(IdentityFlagName, "", "identity")
// Simulate NoOptDefVal behavior by setting the select value.
_ = cmd.Flags().Set(IdentityFlagName, IdentityFlagSelectValue)
},
expected: IdentityFlagSelectValue,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := &cobra.Command{Use: "test"}
tt.setupCmd(cmd)

result := GetIdentityFromFlags(cmd)
assert.Equal(t, tt.expected, result)
})
}
}

func TestAuthCommand_Structure(t *testing.T) {
assert.Equal(t, "auth", authCmd.Use)
assert.NotEmpty(t, authCmd.Short)
assert.NotEmpty(t, authCmd.Long)

// Auth command should have subcommands.
subcommands := authCmd.Commands()
assert.Greater(t, len(subcommands), 0)

// Get subcommand names.
subcommandNames := make([]string, len(subcommands))
for i, cmd := range subcommands {
subcommandNames[i] = cmd.Name()
}

// Verify expected subcommands exist.
assert.Contains(t, subcommandNames, "login")
assert.Contains(t, subcommandNames, "logout")
assert.Contains(t, subcommandNames, "whoami")
assert.Contains(t, subcommandNames, "list")
assert.Contains(t, subcommandNames, "env")
assert.Contains(t, subcommandNames, "shell")
assert.Contains(t, subcommandNames, "exec")
assert.Contains(t, subcommandNames, "console")
assert.Contains(t, subcommandNames, "validate")
}

func TestAuthCommand_IdentityFlag(t *testing.T) {
// Check identity flag is registered as persistent flag.
identityFlag := authCmd.PersistentFlags().Lookup(IdentityFlagName)
assert.NotNil(t, identityFlag)
assert.Equal(t, "i", identityFlag.Shorthand)
}

func TestIdentityFlagName(t *testing.T) {
assert.Equal(t, "identity", IdentityFlagName)
}

func TestIdentityFlagSelectValue(t *testing.T) {
assert.Equal(t, "__SELECT__", IdentityFlagSelectValue)
}

func TestAuthCommand_HasRunE(t *testing.T) {
// Auth parent command may or may not have RunE.
// Just verify the command is properly configured.
assert.Equal(t, "auth", authCmd.Use)
}

func TestAuthCommand_PersistentPreRunE(t *testing.T) {
// Auth command should have a PersistentPreRunE for flag setup.
// The actual function is set, so verify it's not nil.
// Note: In the actual implementation, this might be set to nil or a function.
// Just verify the command is properly initialized.
assert.NotNil(t, authCmd)
}

func TestGetIdentityFromFlags_NoFlag(t *testing.T) {
// Test with a command that doesn't have the identity flag at all.
cmd := &cobra.Command{Use: "test"}

// Should return empty string without panicking.
result := GetIdentityFromFlags(cmd)
assert.Equal(t, "", result)
}

func TestAddIdentityCompletion(t *testing.T) {
cmd := &cobra.Command{Use: "test"}
cmd.Flags().String(IdentityFlagName, "", "identity")

// Should not panic.
assert.NotPanics(t, func() {
AddIdentityCompletion(cmd)
})
}
Loading
Loading