Skip to content

Support --profile flag for custom commands with autocomplete#2190

Open
osterman wants to merge 4 commits intomainfrom
osterman/check-profile-autocomplete
Open

Support --profile flag for custom commands with autocomplete#2190
osterman wants to merge 4 commits intomainfrom
osterman/check-profile-autocomplete

Conversation

@osterman
Copy link
Member

@osterman osterman commented Mar 13, 2026

what

  • Added early parsing of --profile flag (before Cobra parses) to ensure profiles are applied during initial config load, so custom commands receive a properly profiled atmosConfig
  • Extended StringSliceFlag to support custom completion functions
  • Implemented profile autocomplete that lists available profiles from the profile manager
  • Updated FlagRegistry.SetCompletionFunc() to support both StringFlag and StringSliceFlag types

why

Previously, custom commands could accept --profile but the flag was never applied because atmosConfig was loaded at boot time (before Cobra parsed flags). Custom commands captured this pre-profile config in their closure. Built-in commands worked because they called GetConfigAndStacksInfo() which re-extracted --profile during command execution.

This fix follows the same pattern as --chdir: parse the flag early from os.Args and pass it to InitCliConfig, ensuring all commands (built-in and custom) get properly profiled configuration.

The autocomplete feature provides users with shell suggestions of available profiles when typing atmos commands.

references

Resolves the gap where custom commands couldn't use the --profile flag effectively.

Summary by CodeRabbit

  • New Features

    • CLI supports profiles via global --profile and ATMOS_PROFILE; profiles are applied at startup and available for shell completion.
    • Improved completion: profile listing and multi-value flag completion now supported.
    • Early config selection can be provided via command-line or environment variables.
  • Tests

    • Added tests for flag completion behavior and profile/config selection wiring.

…complete

## What Changed

- Added early parsing of --profile flag (before Cobra parses) to ensure profiles
  are applied during initial config load, so custom commands get a properly
  profiled atmosConfig
- Exported ParseProfilesFromOsArgs() and ParseProfilesFromEnvString() functions
  for use during early boot
- Added profile autocomplete by implementing profileFlagCompletion() that uses
  the profile manager to list available profiles
- Extended StringSliceFlag to support custom completion functions (via
  CompletionFunc field)
- Updated FlagRegistry.SetCompletionFunc() to support both StringFlag and
  StringSliceFlag types

## Why

Previously, custom commands could accept --profile but the flag was never
applied because:
1. atmosConfig was loaded at boot time (before Cobra parses flags)
2. Custom commands captured this pre-profile config in their closure
3. Built-in commands work because they call GetConfigAndStacksInfo() which
   re-extracts --profile during command execution

This fix follows the same pattern as --chdir: parse the flag early from os.Args
and pass it to InitCliConfig, ensuring all commands (built-in and custom) get
a properly profiled configuration.

The autocomplete feature provides users with shell suggestions of available
profiles when typing atmos commands.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@osterman osterman requested a review from a team as a code owner March 13, 2026 23:42
@github-actions github-actions bot added the size/m Medium size PR label Mar 13, 2026
@github-actions
Copy link

github-actions bot commented Mar 13, 2026

Dependency Review

✅ No vulnerabilities or license issues found.

Scanned Files

None

@osterman osterman added the patch A minor, backward compatible change label Mar 13, 2026
@mergify mergify bot added the triage Needs triage label Mar 13, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 14, 2026

📝 Walkthrough

Walkthrough

Integrates Atmos profile handling into CLI startup: profiles parsed from args/env before config load, passed into InitCliConfig; adds exported profile parsing helpers; extends flag registry and StringSliceFlag to support shell completion for the global --profile flag.

Changes

Cohort / File(s) Summary
CLI root / profile wiring
cmd/root.go
Parse profiles from os.Args and ATMOS_PROFILE before Cobra parsing, include profiles in InitCliConfig payload, register global --profile completion and wire profileFlagCompletion.
Profile parsing API & tests
pkg/config/load.go, pkg/config/load_config_selection_test.go, pkg/config/load_profile_test.go, pkg/config/load_flags_test.go, pkg/config/load_test.go
Renamed and exported parsing helpers (ParseProfilesFromOsArgs, ParseProfilesFromEnvString), added ConfigSelection type and helpers for early config selection, suppressed flag output during parsing, and updated/added tests for new APIs.
Flag registry & types
pkg/flags/registry.go, pkg/flags/registry_test.go, pkg/flags/types.go
SetCompletionFunc now applies completion to StringFlag and StringSliceFlag; StringSliceFlag gains CompletionFunc field and returns it via GetCompletionFunc. New tests validate completion wiring.
Misc / manifest
manifest_file, go.mod
Manifest/test adjustments included; minor comment and test import updates.

Sequence Diagram

sequenceDiagram
    participant CLI as CLI Runtime
    participant Root as cmd/root.go
    participant Parser as pkg/config
    participant Registry as pkg/flags
    participant ProfileMgr as ProfileManager

    CLI->>Root: start (args, env)
    Root->>Parser: ParseProfilesFromOsArgs(os.Args) / ParseProfilesFromEnvString(env)
    Parser-->>Root: profiles[]
    Root->>Parser: InitCliConfig(with ProfilesFromArg, AtmosBasePath, config selections)
    Parser-->>Root: config initialized

    CLI->>Registry: request shell completion for --profile
    Registry->>ProfileMgr: list profiles (using initialized config)
    ProfileMgr-->>Registry: profile names
    Registry-->>CLI: completion suggestions
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

minor

Suggested reviewers

  • aknysh
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 directly addresses the main feature: adding --profile flag support with autocomplete for custom commands, which aligns with the PR's core objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch osterman/check-profile-autocomplete
📝 Coding Plan
  • Generate coding plan for human review comments

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

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
pkg/flags/registry_test.go (1)

148-153: Prefer asserting through the Flag API, not concrete struct fields.

These assertions currently couple to implementation details (flag.CompletionFunc). Using registry.Get(...).GetCompletionFunc() would keep the test behavior-focused and more refactor-safe.

As per coding guidelines "Test Quality (MANDATORY): Test behavior, not implementation."

Also applies to: 165-170

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/flags/registry_test.go` around lines 148 - 153, The test is asserting on
the concrete StringFlag.CompletionFunc field; instead use the Flag API accessor
to avoid coupling to implementation details—replace uses of
registry.Get("stack").(*StringFlag).CompletionFunc with
registry.Get("stack").GetCompletionFunc() (or the equivalent interface method)
and call that to obtain (values, directive) for assertions, applying the same
change for the other occurrence around the second block (currently lines
165-170).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/root.go`:
- Around line 1866-1867: The completion config is being initialized with an
empty schema.ConfigAndStacksInfo which ignores parsed global flags; before
calling cfg.InitCliConfig(schema.ConfigAndStacksInfo{}, false) populate a
ConfigAndStacksInfo instance by calling flags.ParseGlobalFlags(cmd, v) (or
equivalent) so the global flag values are applied, then pass that populated
struct to cfg.InitCliConfig; update the call around atmosCfg, err :=
cfg.InitCliConfig(...) to use the parsed/filled ConfigAndStacksInfo instead of
the empty literal.

---

Nitpick comments:
In `@pkg/flags/registry_test.go`:
- Around line 148-153: The test is asserting on the concrete
StringFlag.CompletionFunc field; instead use the Flag API accessor to avoid
coupling to implementation details—replace uses of
registry.Get("stack").(*StringFlag).CompletionFunc with
registry.Get("stack").GetCompletionFunc() (or the equivalent interface method)
and call that to obtain (values, directive) for assertions, applying the same
change for the other occurrence around the second block (currently lines
165-170).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9dfd34d3-4eeb-4824-98a2-2b5cbc6335d8

📥 Commits

Reviewing files that changed from the base of the PR and between 76a269b and 3188dbb.

📒 Files selected for processing (8)
  • cmd/root.go
  • pkg/config/load.go
  • pkg/config/load_flags_test.go
  • pkg/config/load_profile_test.go
  • pkg/config/load_test.go
  • pkg/flags/registry.go
  • pkg/flags/registry_test.go
  • pkg/flags/types.go

osterman and others added 2 commits March 13, 2026 22:57
The pflag.FlagSet created in ParseProfilesFromOsArgs() defaulted to
writing usage output to os.Stderr, causing "Usage of profile-parser"
to appear on every CLI invocation and breaking all snapshot tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Parse global flags (--base-path, --config, --config-path, --profile)
before initializing config in profileFlagCompletion, matching the
pattern used by other completion functions (e.g., ansible).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/root.go`:
- Around line 1403-1421: The InitCliConfig call is being passed only
ProfilesFromArg, so early config-selection flags (--base-path, --config,
--config-path) are ignored when building custom commands; update the pre-Cobra
parsing to extract those flags (using the same early-parsing pattern as
ParseProfilesFromOsArgs/ParseProfilesFromEnvString) and populate the
corresponding fields on the schema.ConfigAndStacksInfo passed into
cfg.InitCliConfig (e.g., set BasePath, Config, ConfigPath or whatever selection
fields exist on schema.ConfigAndStacksInfo) so InitCliConfig/LoadConfig sees the
intended selection before Cobra loads commands.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 59e8f018-ac3e-476f-b5c3-f7b39be3f7e2

📥 Commits

Reviewing files that changed from the base of the PR and between 3188dbb and 7163935.

📒 Files selected for processing (2)
  • cmd/root.go
  • pkg/config/load.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/config/load.go

@codecov
Copy link

codecov bot commented Mar 14, 2026

Codecov Report

❌ Patch coverage is 71.79487% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.29%. Comparing base (dd9fa5b) to head (d106181).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
cmd/root.go 35.29% 21 Missing and 1 partial ⚠️

❌ Your patch check has failed because the patch coverage (71.79%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2190      +/-   ##
==========================================
+ Coverage   77.24%   77.29%   +0.05%     
==========================================
  Files         959      960       +1     
  Lines       90782    91158     +376     
==========================================
+ Hits        70124    70465     +341     
- Misses      16574    16609      +35     
  Partials     4084     4084              
Flag Coverage Δ
unittests 77.29% <71.79%> (+0.05%) ⬆️

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

Files with missing lines Coverage Δ
pkg/config/load.go 81.96% <100.00%> (+0.97%) ⬆️
pkg/flags/registry.go 67.70% <100.00%> (+1.57%) ⬆️
pkg/flags/types.go 100.00% <100.00%> (ø)
cmd/root.go 66.22% <35.29%> (-1.36%) ⬇️

... and 23 files with indirect coverage changes

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

…ig call

The early InitCliConfig call only seeded ProfilesFromArg, so config-selection
flags were ignored when building custom commands and aliases. Now all three
config-selection flags are parsed early from os.Args (with env var fallbacks)
and passed into InitCliConfig, ensuring `atmos --config ... --profile ... <cmd>`
resolves the correct configuration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/root.go`:
- Around line 1415-1417: The current logic replaces configSel with
cfg.ConfigSelectionFromEnv() only when all selectors are empty; instead fetch
envSel := cfg.ConfigSelectionFromEnv() and for each field (configSel.BasePath,
configSel.Config, configSel.ConfigPath, etc.) if that field is empty/zero then
assign the corresponding value from envSel, so environment variables are applied
per-field (flags keep precedence). Use the existing ConfigSelectionFromEnv() and
update only missing fields on configSel to preserve flags > env > file >
defaults ordering.

In `@pkg/config/load_config_selection_test.go`:
- Around line 169-177: The subtests in load_config_selection_test.go can inherit
host environment variables causing flaky failures; before applying each test
case's tt.envVars in the t.Run block, explicitly clear ATMOS_BASE_PATH,
ATMOS_CONFIG, and ATMOS_CONFIG_PATH (use t.Setenv(key, "") or equivalent) so
ConfigSelectionFromEnv() reads only the intended values for each subtest; update
the loop around ConfigSelectionFromEnv() / assert.Equal checks to first reset
those three env vars, then apply tt.envVars, then call ConfigSelectionFromEnv().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ec0926a0-5c5c-4d5a-ab32-17e1b60ac73d

📥 Commits

Reviewing files that changed from the base of the PR and between 7163935 and d106181.

📒 Files selected for processing (3)
  • cmd/root.go
  • pkg/config/load.go
  • pkg/config/load_config_selection_test.go

Comment on lines +1415 to +1417
if configSel.BasePath == "" && len(configSel.Config) == 0 && len(configSel.ConfigPath) == 0 {
configSel = cfg.ConfigSelectionFromEnv()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Apply env fallback per field, not only when all selectors are empty.

At Line 1415, env fallback is skipped if any selector flag is present. Example: --base-path plus ATMOS_CONFIG_PATH ignores ATMOS_CONFIG_PATH, which breaks expected precedence behavior.

Proposed patch
 	configSel := cfg.ParseConfigSelectionFromOsArgs(os.Args[1:])
-	if configSel.BasePath == "" && len(configSel.Config) == 0 && len(configSel.ConfigPath) == 0 {
-		configSel = cfg.ConfigSelectionFromEnv()
-	}
+	envSel := cfg.ConfigSelectionFromEnv()
+	if configSel.BasePath == "" {
+		configSel.BasePath = envSel.BasePath
+	}
+	if len(configSel.Config) == 0 {
+		configSel.Config = envSel.Config
+	}
+	if len(configSel.ConfigPath) == 0 {
+		configSel.ConfigPath = envSel.ConfigPath
+	}

As per coding guidelines: "Support configuration via files, environment variables, and flags following the precedence order: flags > environment variables > config file > defaults".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/root.go` around lines 1415 - 1417, The current logic replaces configSel
with cfg.ConfigSelectionFromEnv() only when all selectors are empty; instead
fetch envSel := cfg.ConfigSelectionFromEnv() and for each field
(configSel.BasePath, configSel.Config, configSel.ConfigPath, etc.) if that field
is empty/zero then assign the corresponding value from envSel, so environment
variables are applied per-field (flags keep precedence). Use the existing
ConfigSelectionFromEnv() and update only missing fields on configSel to preserve
flags > env > file > defaults ordering.

Comment on lines +169 to +177
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for k, v := range tt.envVars {
t.Setenv(k, v)
}
sel := ConfigSelectionFromEnv()
assert.Equal(t, tt.basePath, sel.BasePath, "BasePath")
assert.Equal(t, tt.config, sel.Config, "Config")
assert.Equal(t, tt.configPath, sel.ConfigPath, "ConfigPath")
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clear target env vars per subtest to avoid ambient-env flakes.

At Line 170, cases like “no env vars” can inherit host env values. Reset ATMOS_BASE_PATH, ATMOS_CONFIG, and ATMOS_CONFIG_PATH to empty at subtest start.

Proposed patch
 for _, tt := range tests {
 	t.Run(tt.name, func(t *testing.T) {
+		t.Setenv("ATMOS_BASE_PATH", "")
+		t.Setenv("ATMOS_CONFIG", "")
+		t.Setenv("ATMOS_CONFIG_PATH", "")
+
 		for k, v := range tt.envVars {
 			t.Setenv(k, v)
 		}
 		sel := ConfigSelectionFromEnv()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for k, v := range tt.envVars {
t.Setenv(k, v)
}
sel := ConfigSelectionFromEnv()
assert.Equal(t, tt.basePath, sel.BasePath, "BasePath")
assert.Equal(t, tt.config, sel.Config, "Config")
assert.Equal(t, tt.configPath, sel.ConfigPath, "ConfigPath")
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Setenv("ATMOS_BASE_PATH", "")
t.Setenv("ATMOS_CONFIG", "")
t.Setenv("ATMOS_CONFIG_PATH", "")
for k, v := range tt.envVars {
t.Setenv(k, v)
}
sel := ConfigSelectionFromEnv()
assert.Equal(t, tt.basePath, sel.BasePath, "BasePath")
assert.Equal(t, tt.config, sel.Config, "Config")
assert.Equal(t, tt.configPath, sel.ConfigPath, "ConfigPath")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/config/load_config_selection_test.go` around lines 169 - 177, The
subtests in load_config_selection_test.go can inherit host environment variables
causing flaky failures; before applying each test case's tt.envVars in the t.Run
block, explicitly clear ATMOS_BASE_PATH, ATMOS_CONFIG, and ATMOS_CONFIG_PATH
(use t.Setenv(key, "") or equivalent) so ConfigSelectionFromEnv() reads only the
intended values for each subtest; update the loop around
ConfigSelectionFromEnv() / assert.Equal checks to first reset those three env
vars, then apply tt.envVars, then call ConfigSelectionFromEnv().

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/m Medium size PR triage Needs triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant