Skip to content

Commit a4a9e29

Browse files
aknyshclaude
andcommitted
feat: Add ATMOS_PROFILE environment variable support and fix linter errors
## Profile Environment Variable Support Added `ATMOS_PROFILE` environment variable support for setting active profiles: - Added env var fallback in `internal/exec/cli_utils.go` for commands using ProcessCommandLineArgs - Removed redundant env var handling from `cmd/root.go` (not used by most commands) - Supports comma-separated profiles: `ATMOS_PROFILE="ci,developer"` - Flag takes precedence over env var for consistent behavior - Updated configuration documentation ## Linter Fixes Fixed all lintroller and golangci-lint errors: - Added missing `defer perf.Track()` in `pkg/flags/flag_parser.go:NormalizeShorthandWithEquals()` - Fixed `Deprecated` field comment in `pkg/schema/schema.go` (removed colon to avoid deprecation marker) - Removed unnecessary `os.Chdir` save/restore in `cmd/root_helpers_test.go` (tests don't change directory) - Added `cmd/root_helpers_test.go` and `cmd/root_test.go` to lintroller exclusions for legitimate `os.Args` usage (these files test functions that directly read `os.Args`) ## Testing All changes tested and verified: - ✅ `ATMOS_PROFILE=developer` loads profile correctly - ✅ `--profile` flag works - ✅ Flag precedence over env var - ✅ Comma-separated profiles work - ✅ All unit tests pass - ✅ All linter checks pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 3e91960 commit a4a9e29

File tree

15 files changed

+44
-22
lines changed

15 files changed

+44
-22
lines changed

cmd/root_helpers_test.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -828,11 +828,6 @@ func TestRootCmd_RunE(t *testing.T) {
828828
// This test verifies that the root command's RunE function
829829
// properly handles configuration and prints the ATMOS logo.
830830

831-
// Save original state.
832-
originalWd, err := os.Getwd()
833-
assert.NoError(t, err)
834-
defer os.Chdir(originalWd)
835-
836831
// Use test fixtures.
837832
stacksPath := "../tests/fixtures/scenarios/complete"
838833
t.Setenv("ATMOS_CLI_CONFIG_PATH", stacksPath)

cmd/version/list.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ var listCmd = &cobra.Command{
152152
// Validate format.
153153
normalizedFormat := strings.ToLower(listFormat)
154154
if normalizedFormat != "table" && normalizedFormat != "json" && normalizedFormat != "yaml" {
155-
return fmt.Errorf("%w: %s (supported: table, json, yaml)", errUtils.ErrUnsupportedOutputFormat, listFormat)
155+
return fmt.Errorf("%w: %s (supported: table, json, yaml)", errUtils.ErrInvalidFormat, listFormat)
156156
}
157157

158158
// Parse since date if provided.
@@ -188,7 +188,7 @@ var listCmd = &cobra.Command{
188188
case "yaml":
189189
return formatReleaseListYAML(releases)
190190
default:
191-
return fmt.Errorf("%w: %s (supported: table, json, yaml)", errUtils.ErrUnsupportedOutputFormat, listFormat)
191+
return fmt.Errorf("%w: %s (supported: table, json, yaml)", errUtils.ErrInvalidFormat, listFormat)
192192
}
193193
},
194194
}

cmd/version/show.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ var showCmd = &cobra.Command{
166166
case "yaml":
167167
return formatReleaseDetailYAML(release)
168168
default:
169-
return fmt.Errorf("%w: %s (supported: table, json, yaml)", errUtils.ErrUnsupportedOutputFormat, showFormat)
169+
return fmt.Errorf("%w: %s (supported: table, json, yaml)", errUtils.ErrInvalidFormat, showFormat)
170170
}
171171
},
172172
}

cmd/version/show_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ func TestShowCommand_FormatValidation(t *testing.T) {
322322
case "yaml":
323323
return formatReleaseDetailYAML(release)
324324
default:
325-
return fmt.Errorf("%w: %s (supported: table, json, yaml)", errUtils.ErrUnsupportedOutputFormat, showFormat)
325+
return fmt.Errorf("%w: %s (supported: table, json, yaml)", errUtils.ErrInvalidFormat, showFormat)
326326
}
327327
}
328328

docs/prd/auth-default-settings.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ auth:
126126
auth:
127127
# NEW: Global settings for auth behavior
128128
settings:
129-
default_identity: github-oidc-identity
130129
session_duration: "12h"
131130
console_session_duration: "8h"
132131
keyring_type: "system"
@@ -135,10 +134,12 @@ auth:
135134
# ... same as above
136135
137136
identities:
137+
github-oidc-identity:
138+
default: true # Current implementation uses identity-level default
138139
# ... same as above
139140
```text
140141

141-
**Note:** Less structured than Option 1, but flatter hierarchy.
142+
**Note:** Less structured than Option 1, but flatter hierarchy. Default identity selection uses the existing `identity.default: true` field.
142143

143144
## Detailed Design (Option 1 - Recommended)
144145

@@ -670,15 +671,17 @@ ATMOS_DEFAULT_IDENTITY=github-oidc-identity
670671
```yaml
671672
profiles:
672673
ci:
673-
default_identity: github-oidc-identity
674+
auth:
675+
identities:
676+
github-oidc-identity:
677+
default: true
674678
```text
675679

676680
**Pros:**
677681
- Clear profile scope
678-
- Separate from auth config
682+
- Uses existing identity default mechanism
679683

680684
**Cons:**
681-
- Breaks auth config encapsulation
682685
- Profile needs to know about auth internals
683686
- Doesn't solve global session defaults
684687

docs/prd/describe-commands-identity-flag.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -541,11 +541,13 @@ if err := viper.BindEnv("identity", "ATMOS_IDENTITY", "IDENTITY"); err != nil {
541541

542542
### Default Identity
543543

544-
Consider supporting default identity in `atmos.yaml`:
544+
Default identity is supported via the `default` field on identities in `atmos.yaml`:
545545

546546
```yaml
547547
auth:
548-
default_identity: core-auto/terraform
548+
identities:
549+
core-auto/terraform:
550+
default: true
549551
```
550552
551553
### Identity Caching

errors/errors.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ var (
5252
ErrInvalidOffset = errors.New("offset must be >= 0")
5353
ErrDuplicateFlagRegistration = errors.New("duplicate flag registration")
5454
ErrInvalidSinceDate = errors.New("invalid date format for --since")
55-
ErrUnsupportedOutputFormat = errors.New("unsupported output format")
5655
ErrTerminalTooNarrow = errors.New("terminal too narrow")
5756
ErrSpinnerReturnedNilModel = errors.New("spinner returned nil model")
5857
ErrSpinnerUnexpectedModelType = errors.New("spinner returned unexpected model type")

internal/exec/cli_utils.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ var commonFlags = []string{
7272
cfg.ProfilerTypeFlag,
7373
cfg.HeatmapFlag,
7474
cfg.HeatmapModeFlag,
75+
cfg.AuthProfileFlag,
7576
}
7677

7778
// ProcessCommandLineArgs processes command-line args.
@@ -116,10 +117,24 @@ func ProcessCommandLineArgs(
116117
if err != nil {
117118
return configAndStacksInfo, err
118119
}
119-
configAndStacksInfo.ProfilesFromArg, err = cmd.Flags().GetStringSlice("profile")
120+
// Read profile flag and env var.
121+
// Check flag first, then fall back to ATMOS_PROFILE env var if flag not set.
122+
profiles, err := cmd.Flags().GetStringSlice("profile")
120123
if err != nil {
121124
return configAndStacksInfo, err
122125
}
126+
if len(profiles) == 0 {
127+
//nolint:forbidigo // Must use os.Getenv: profile is processed before Viper configuration loads.
128+
if envProfiles := os.Getenv("ATMOS_PROFILE"); envProfiles != "" {
129+
// Split comma-separated profiles from env var.
130+
profiles = strings.Split(envProfiles, ",")
131+
// Trim whitespace from each profile name.
132+
for i := range profiles {
133+
profiles[i] = strings.TrimSpace(profiles[i])
134+
}
135+
}
136+
}
137+
configAndStacksInfo.ProfilesFromArg = profiles
123138
finalAdditionalArgsAndFlags := argsAndFlagsInfo.AdditionalArgsAndFlags
124139
if len(additionalArgsAndFlags) > 0 {
125140
finalAdditionalArgsAndFlags = append(finalAdditionalArgsAndFlags, additionalArgsAndFlags...)

pkg/config/const.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,7 @@ const (
144144
ProfilerTypeFlag = "--profiler-type"
145145
HeatmapFlag = "--heatmap"
146146
HeatmapModeFlag = "--heatmap-mode"
147+
148+
// Auth profile flags.
149+
AuthProfileFlag = "--profile"
147150
)

pkg/config/load.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ func LoadConfig(configAndStacksInfo *schema.ConfigAndStacksInfo) (schema.AtmosCo
108108
return atmosConfig, err
109109
}
110110

111-
log.Info("Profiles loaded successfully",
111+
log.Debug("Profiles loaded successfully",
112112
"profiles", configAndStacksInfo.ProfilesFromArg,
113113
"count", len(configAndStacksInfo.ProfilesFromArg))
114114
}

0 commit comments

Comments
 (0)