Skip to content

Add interactive profile picker to auth logout - #4616

Closed
mihaimitrea-db wants to merge 25 commits into
mainfrom
mihaimitrea-db/stack/auth_logout_profile_picker
Closed

Add interactive profile picker to auth logout#4616
mihaimitrea-db wants to merge 25 commits into
mainfrom
mihaimitrea-db/stack/auth_logout_profile_picker

Conversation

@mihaimitrea-db

@mihaimitrea-dbmihaimitrea-db commented Feb 27, 2026

Copy link
Copy Markdown
Contributor

🥞 Stacked PR

Use this link to review incremental changes.


When --profile is not specified in an interactive terminal, show a searchable prompt listing all configured profiles. Profiles are sorted alphabetically and displayed with their host or account ID. The picker supports fuzzy search by name, host, or account ID.

Changes

  • When --profile is omitted in an interactive terminal, show a searchable prompt listing all configured profiles sorted alphabetically. Profiles display their host or account ID, and support fuzzy search by name, host, or account ID.
  • Expand the command's long help text to document all four interaction modes (explicit --profile, interactive picker, non-interactive error, --force).

Tests

  • Existing unit tests from #4613 continue to pass (profile picker is only triggered in interactive mode, which tests mock as non-interactive).
  • Manual testing with multiple profiles to verify picker display, search, and selection.

@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Feb 27, 2026

Copy link
Copy Markdown
Collaborator

Commit: e5a1d88

Run: 22909885914

Env❌​FAIL🔄​flaky💚​RECOVERED🙈​SKIP✅​pass🙈​skipTime
💚​aws linux8726878811:00
💚​aws windows8727078611:38
🔄​aws-ucws linux38736270324:32
🔄​aws-ucws windows27736670115:56
💚​azure linux2927178611:23
💚​azure windows292737847:51
❌​azure-ucws linux12936769920:14
🔄​azure-ucws windows41936969715:09
💚​gcp linux292677897:50
💚​gcp windows292697877:28
19 interesting tests: 7 SKIP, 6 RECOVERED, 5 flaky, 1 FAIL
Test Nameaws linuxaws windowsaws-ucws linuxaws-ucws windowsazure linuxazure windowsazure-ucws linuxazure-ucws windowsgcp linuxgcp windows
🔄​TestAccept💚​R💚​R💚​R💚​R💚​R💚​R🔄​f💚​R💚​R💚​R
🙈​TestAccept/bundle/resources/permissions🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🔄​TestAccept/bundle/resources/permissions/jobs/delete_one/cloud🙈​s🙈​s🔄​f✅​p🙈​s🙈​s✅​p🔄​f🙈​s🙈​s
🔄​TestAccept/bundle/resources/permissions/jobs/delete_one/cloud/DATABRICKS_BUNDLE_ENGINE=direct🔄​f✅​p✅​p🔄​f
💚​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions💚​R💚​R💚​R💚​R🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
💚​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=direct💚​R💚​R💚​R💚​R
💚​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=terraform💚​R💚​R💚​R💚​R
💚​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions💚​R💚​R💚​R💚​R🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
💚​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=direct💚​R💚​R💚​R💚​R
💚​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=terraform💚​R💚​R💚​R💚​R
🙈​TestAccept/bundle/resources/postgres_branches/basic🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_branches/recreate🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_branches/update_protected🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_branches/without_branch_id🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_endpoints/recreate🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/synced_database_tables/basic🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🔄​TestAccept/ssh/connect-serverless-gpu🙈​s🙈​s🔄​f🔄​f🙈​s🙈​s✅​p🔄​f🙈​s🙈​s
🔄​TestAccept/ssh/connection💚​R💚​R💚​R🔄​f💚​R💚​R🔄​f🔄​f💚​R💚​R
❌​TestFetchRepositoryInfoAPI_FromRepo✅​p✅​p✅​p✅​p✅​p✅​p❌​F✅​p✅​p✅​p
Top 50 slowest tests (at least 2 minutes):
durationenvtestname
6:48aws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
5:40aws-ucws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
5:33aws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
5:30azure-ucws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
5:26aws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
5:00aws-ucws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:51azure linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
4:45aws-ucws linuxTestAccept/ssh/connect-serverless-gpu
4:44aws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
4:34azure windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:34gcp linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:33azure linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:27gcp windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:27aws-ucws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
4:25aws-ucws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:05azure windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:53azure-ucws linuxTestAccept/bundle/resources/dashboards/generate_inplace/DATABRICKS_BUNDLE_ENGINE=terraform
3:38azure-ucws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:34azure-ucws windowsTestSparkJarTaskDeployAndRunOnVolumes/Databricks_Runtime_14.3_LTS
3:34azure-ucws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:23gcp linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:21gcp windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:11aws-ucws windowsTestAccept/bundle/deploy/files/no-snapshot-sync/DATABRICKS_BUNDLE_ENGINE=terraform
2:58azure-ucws linuxTestAccept/bundle/deploy/files/no-snapshot-sync/DATABRICKS_BUNDLE_ENGINE=terraform
2:52azure-ucws linuxTestAccept/bundle/resources/permissions/jobs/delete_one/cloud/DATABRICKS_BUNDLE_ENGINE=terraform
2:44aws-ucws windowsTestFilerWorkspaceFilesExtensionsReadDir
2:38aws-ucws linuxTestAccept/bundle/deployment/bind/job/generate-and-bind/DATABRICKS_BUNDLE_ENGINE=terraform
2:33aws-ucws linuxTestAccept/bundle/deploy/files/no-snapshot-sync/DATABRICKS_BUNDLE_ENGINE=terraform
2:32azure-ucws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:32azure-ucws linuxTestAccept/bundle/resources/model_serving_endpoints/basic/DATABRICKS_BUNDLE_ENGINE=terraform
2:30aws-ucws linuxTestAccept/bundle/resources/volumes/recreate/DATABRICKS_BUNDLE_ENGINE=terraform
2:28aws-ucws windowsTestAccept/bundle/resources/permissions/jobs/delete_one/cloud/DATABRICKS_BUNDLE_ENGINE=terraform
2:25aws-ucws windowsTestAccept/bundle/resources/volumes/recreate/DATABRICKS_BUNDLE_ENGINE=terraform
2:24azure-ucws windowsTestAccept/bundle/resources/volumes/recreate/DATABRICKS_BUNDLE_ENGINE=terraform
2:23aws-ucws windowsTestAccept/bundle/deployment/bind/job/generate-and-bind/DATABRICKS_BUNDLE_ENGINE=terraform
2:23aws-ucws windowsTestAccept/bundle/resources/model_serving_endpoints/basic/DATABRICKS_BUNDLE_ENGINE=terraform
2:22azure-ucws windowsTestAccept/bundle/resources/permissions/jobs/delete_one/cloud/DATABRICKS_BUNDLE_ENGINE=terraform
2:22aws-ucws windowsTestAccept/bundle/deploy/files/no-snapshot-sync/DATABRICKS_BUNDLE_ENGINE=direct
2:21azure-ucws linuxTestAccept/bundle/deployment/bind/job/generate-and-bind/DATABRICKS_BUNDLE_ENGINE=terraform
2:15azure-ucws windowsTestAccept/bundle/deploy/files/no-snapshot-sync/DATABRICKS_BUNDLE_ENGINE=terraform
2:15aws-ucws linuxTestAccept/bundle/resources/model_serving_endpoints/basic/DATABRICKS_BUNDLE_ENGINE=terraform
2:13aws-ucws linuxTestAccept/bundle/deployment/bind/job/generate-and-bind/DATABRICKS_BUNDLE_ENGINE=direct
2:12aws-ucws windowsTestAccept/bundle/deployment/bind/job/generate-and-bind/DATABRICKS_BUNDLE_ENGINE=direct
2:06azure-ucws windowsTestAccept/bundle/resources/model_serving_endpoints/basic/DATABRICKS_BUNDLE_ENGINE=direct
2:05azure-ucws windowsTestAccept/bundle/resources/model_serving_endpoints/basic/DATABRICKS_BUNDLE_ENGINE=terraform
2:04azure-ucws linuxTestAccept/bundle/deployment/bind/alert/DATABRICKS_BUNDLE_ENGINE=terraform
2:04aws-ucws linuxTestAccept/bundle/resources/permissions/jobs/delete_one/cloud/DATABRICKS_BUNDLE_ENGINE=terraform
2:04azure-ucws linuxTestAccept/bundle/deploy/files/no-snapshot-sync/DATABRICKS_BUNDLE_ENGINE=direct
2:01aws-ucws linuxTestAccept/bundle/destroy/jobs-and-pipeline/DATABRICKS_BUNDLE_ENGINE=direct
2:01azure-ucws windowsTestAccept/bundle/resources/jobs/check-metadata/DATABRICKS_BUNDLE_ENGINE=direct

@simonfaltumsimonfaltum left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good! I left some comments

Comment threadcmd/auth/logout.go
Comment threadcmd/auth/logout_test.go Outdated
Comment threadcmd/auth/logout.go Outdated
Comment threadlibs/databrickscfg/ops.go Outdated
Comment threadlibs/databrickscfg/ops.go Outdated
Comment threadcmd/auth/logout.go
Comment threadcmd/auth/logout.go Outdated
Comment threadcmd/auth/logout.go Outdated
Comment threadcmd/auth/logout.go Outdated
@mihaimitrea-dbmihaimitrea-db self-assigned this Mar 2, 2026
@mihaimitrea-db
mihaimitrea-dbforce-pushed the mihaimitrea-db/stack/auth_logout_profile_picker branch 2 times, most recently from f374e5a to c903ed8CompareMarch 2, 2026 11:42
@mihaimitrea-db
mihaimitrea-dbforce-pushed the mihaimitrea-db/stack/auth_logout_profile_picker branch from c903ed8 to 889b7caCompareMarch 2, 2026 12:11
@mihaimitrea-db
mihaimitrea-dbforce-pushed the mihaimitrea-db/stack/auth_logout_profile_picker branch from 889b7ca to 7a39778CompareMarch 2, 2026 12:45
@mihaimitrea-db
mihaimitrea-dbforce-pushed the mihaimitrea-db/stack/auth_logout_profile_picker branch from 7a39778 to 67eff3fCompareMarch 2, 2026 13:27
@mihaimitrea-dbmihaimitrea-db linked an issue Mar 2, 2026 that may be closed by this pull request
@mihaimitrea-db
mihaimitrea-db marked this pull request as ready for review March 2, 2026 14:07
Comment threadcmd/auth/logout.go Outdated
Comment on lines +201 to +203
slices.SortFunc(allProfiles, func(a, b profile.Profile) int {
return strings.Compare(strings.ToLower(a.Name), strings.ToLower(b.Name))
})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

do we always sort profiles alphabetically? I'd think we just want to show them in the order they were added (or alternatively last used or most used but we don't log that anywhere..)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

My personal preference is to have lists sorted, but you are right, no other picker in the codebase does this so for consistency let's go wth the order in which they were added.

Comment threadcmd/auth/logout.go
// promptForLogoutProfile shows an interactive profile picker for logout.
// Account profiles are displayed as "name (account: id)", workspace profiles
// as "name (host)".
func promptForLogoutProfile(ctx context.Context, profiler profile.Profiler) (string, error) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this is good work but did you look through the codebase for similar patterns?
I think the closest existing pattern is promptForProfileSelection in cmd/auth/token.go (line ~368), which already handles an "all profiles" picker with search by name/host and the StartInSearchMode: len(profiles) > 5 pattern.
I think we are re-implementing much of this logic, which maybe is justified but it might be worth it to make a component we could re-use broadly as selecting profile interactively is something we will have to do multiple times. I think in auth token it lets you create a new profile also, which is probably not something we want to do in logout 😃

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I tried fixing that in the next PR on this stack. Check that out: #4647

@mihaimitrea-db
mihaimitrea-dbforce-pushed the mihaimitrea-db/stack/auth_logout_profile_picker branch from a87d5a2 to bcf6e70CompareMarch 3, 2026 12:30
@mihaimitrea-db
mihaimitrea-dbforce-pushed the mihaimitrea-db/stack/auth_logout_profile_picker branch from bcf6e70 to e536cc8CompareMarch 3, 2026 12:37
Merge shared-host token deletion verification into one main parametrized test by addding the hostBasedKey and isSharedKey parameters to each case. This replaces the TestLogoutTokenCacheCleanup test with an assertion: host-based keys are preserved when another profile shares the same host, and deleted otherwise.
Rewrite the test to use inline config seeds and explicit expected state. Add cases for deleting the last non-default profile, deleting a unified host profile with multiple keys, and deleting the DEFAULT section.
- Use profiler.GetPath() to resolve config path instead of hardcoding platform-specific defaults for the help text.
- Read DATABRICKS_CONFIG_FILE via env.Get(ctx, ...) instead of os.Getenv to respect context-level env overrides.
- Add abort message when user declines the confirmation prompt.
- Guard DeleteProfile against non-existent profiles to avoid creating unnecessary backup files.
- Add TestDeleteProfile_NotFound for the error path.
Cover four scenarios: profile ordering and comments are preserved after deletion, deleting the last non-default profile leaves an empty DEFAULT section, deleting the DEFAULT profile itself clears its keys and restores the default comment, and error paths for non-existent profiles and missing --profile in non-interactive mode.
By default, Authentication related commands. For more information regarding how
authentication for the Databricks CLI and SDKs work please refer to the documentation
linked below.
AWS: https://docs.databricks.com/dev-tools/auth/index.html
Azure: https://learn.microsoft.com/azure/databricks/dev-tools/auth
GCP: https://docs.gcp.databricks.com/dev-tools/auth/index.html
Usage:
databricks auth [command]
Available Commands:
describe Describes the credentials and the source of those credentials, being used by the CLI to authenticate
env Get env
login Log into a Databricks workspace or account
profiles Lists profiles from ~/.databrickscfg
token Get authentication token
Flags:
--account-id string Databricks Account ID
--experimental-is-unified-host Flag to indicate if the host is a unified host
-h, --help help for auth
--host string Databricks Host
--workspace-id string Databricks Workspace ID
Global Flags:
--debug enable debug logging
-o, --output type output type: text or json (default text)
-p, --profile string ~/.databrickscfg profile
-t, --target string bundle target to use (if applicable)
Use "databricks auth [command] --help" for more information about a command. now only clears cached OAuth
tokens without removing the profile from ~/.databrickscfg. Pass
--delete to also remove the profile entry from the config file.
Existing acceptance tests that verify profile deletion now use --delete
since profile removal is opt-in. Two new acceptance tests verify token-only
logout: one for a unique host (both cache entries cleared) and one for a
shared host (host-keyed token preserved).
Replace manual strings.TrimRight(host, /) with the SDK's
config.Config.CanonicalHostName(), which handles scheme
normalization, trailing slashes, and empty hosts consistently
with how the SDK itself computes token cache keys.
- Make Long description static to avoid calling logger and GetPath at
command construction time before the logger is initialized.
- Remove empty test.toml files from acceptance tests.
- Add \n to error-case titles so errors appear on a separate line.
- Use .tokens | keys in jq queries for token cache to reduce verbosity.
- Switch test profiles from PAT to auth_type=databricks-cli (U2M) so
token cache tests exercise a realistic OAuth logout flow.
- Add AuthType field to profile.Profile to detect non-U2M profiles;
skip token cache cleanup and adjust success message accordingly.
- Add delete-m2m-profiles acceptance test covering PAT profile logout
with and without --delete.
- Fix DeleteProfile to clear DEFAULT section keys instead of
deleting and recreating it, preserving its position in the file.
- Rename isU2MProfile to isCreatedByLogin to accurately reflect
that the check is specific to profiles created by .
- Tighten success and error messages: drop "Successfully", add
actionable suggestions (e.g. "Use --delete to also remove it"),
and include retry guidance on partial failures.
- Return errors instead of logging on DeleteProfile failure so
callers see a non-zero exit code.
- Fix token-only acceptance test: use OIDC-style cache key
(host/oidc/accounts/<id>) for account profiles so both token
cache entries are correctly cleaned up.
@mihaimitrea-db
mihaimitrea-dbforce-pushed the mihaimitrea-db/stack/auth_logout_profile_picker branch from 4bd2e44 to d8fe900CompareMarch 9, 2026 12:41
When --profile is not specified in an interactive terminal, show a
searchable prompt listing all configured profiles. Profiles are sorted
alphabetically and displayed with their host or account ID. The picker
supports fuzzy search by name, host, or account ID.
Document the four interaction modes (explicit profile, interactive
picker, non-interactive error, and --force) in the command's long
help text.
@mihaimitrea-db
mihaimitrea-dbforce-pushed the mihaimitrea-db/stack/auth_logout_profile_picker branch from d8fe900 to e5a1d88CompareMarch 10, 2026 15:16
github-merge-queueBot pushed a commit that referenced this pull request Mar 12, 2026
…ckers (#4647)
## 🥞 Stacked PR
Use this
[link](https://github.com/databricks/cli/pull/4647/files/e5a1d8842ee9a458d4ffb3cea672a0f88077356f..51f14b5c7ea409cffdc78a3025a70de5c674fa9a)
to review incremental changes.
- [stack/auth_logout](#4613)
[[Files changed](https://github.com/databricks/cli/pull/4613/files)]
-
[stack/auth_logout_profile_picker](#4616)
[[Files
changed](https://github.com/databricks/cli/pull/4616/files/c9e8d79e276d6c33faa1e859ca20cc5136f9efb7..e5a1d8842ee9a458d4ffb3cea672a0f88077356f)]
-
[**stack/auth_logout_deduplication**](#4647)
[[Files
changed](https://github.com/databricks/cli/pull/4647/files/e5a1d8842ee9a458d4ffb3cea672a0f88077356f..51f14b5c7ea409cffdc78a3025a70de5c674fa9a)]
---------
Four places built nearly identical `promptui.Select` prompts for
interactive profile selection (`auth logout`, `auth token`,
`cmd/root/auth.go`, `cmd/root/bundle.go`). This PR extracts a reusable
`profile.SelectProfile` function that accepts a declarative
`SelectConfig` with label, profiles, and template strings, replacing all
four implementations.
## Changes
- Add `profile.SelectProfile` in `libs/databrickscfg/profile/select.go`
— a shared interactive profile picker that accepts a `SelectConfig`
(label, profiles, template strings) and returns the selected profile
name.
- Replace the four inline `promptui.Select` implementations in
`cmd/auth/logout.go`, `cmd/auth/token.go`, `cmd/root/auth.go`, and
`cmd/root/bundle.go` with calls to `SelectProfile`.
- Add `AccountID` to `Profiles.SearchCaseInsensitive` so all pickers
support searching by account ID, not just name and host.
- Extract `writeConfigFile` helper in `libs/databrickscfg/ops.go` to
consolidate the repeated default-comment / backup / save sequence shared
by `SaveToProfile` and `DeleteProfile`.
## Why
The four profile pickers each duplicated the same prompt setup, searcher
wiring, and result extraction. This made it easy for behavior to diverge
(e.g., only the logout picker searched by account ID). A single shared
helper keeps the UX consistent and reduces the surface area for future
changes.
## Tests
- Existing unit and acceptance tests for `auth logout`, `auth token`,
workspace/account profile selection, and bundle profile resolution
continue to pass — the refactor is behavior-preserving.
- The `SelectProfile` helper is exercised indirectly through all
existing callers.
---------
Co-authored-by: simon <simon.faltum@databricks.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Add logout command

3 participants

@mihaimitrea-db@eng-dev-ecosystem-bot@simonfaltum