From 4869557012a273362ef05b1734ffe7877d1ac04a Mon Sep 17 00:00:00 2001 From: Tejas Kochar Date: Sun, 18 Jan 2026 14:11:56 +0000 Subject: [PATCH 1/9] Refactor to use config struct only when configure-clusters flag is set --- cmd/auth/login.go | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/cmd/auth/login.go b/cmd/auth/login.go index 733c404e33..8d288c8bd8 100644 --- a/cmd/auth/login.go +++ b/cmd/auth/login.go @@ -152,18 +152,6 @@ depends on the existing profiles you have set in your configuration file } defer persistentAuth.Close() - // We need the config without the profile before it's used to initialise new workspace client below. - // Otherwise it will complain about non existing profile because it was not yet saved. - cfg := config.Config{ - Host: authArguments.Host, - AccountID: authArguments.AccountID, - AuthType: "databricks-cli", - } - databricksCfgFile := os.Getenv("DATABRICKS_CONFIG_FILE") - if databricksCfgFile != "" { - cfg.ConfigFile = databricksCfgFile - } - ctx, cancel := context.WithTimeout(ctx, loginTimeout) defer cancel() @@ -171,21 +159,27 @@ depends on the existing profiles you have set in your configuration file return err } + var clusterID, serverlessComputeID string switch { case configureCluster: - w, err := databricks.NewWorkspaceClient((*databricks.Config)(&cfg)) + // Create a workspace client with direct credentials (not from a profile) + // because the profile hasn't been saved yet. + w, err := databricks.NewWorkspaceClient(&databricks.Config{ + Host: authArguments.Host, + AccountID: authArguments.AccountID, + AuthType: "databricks-cli", + ConfigFile: os.Getenv("DATABRICKS_CONFIG_FILE"), + }) if err != nil { return err } - clusterID, err := cfgpickers.AskForCluster(ctx, w, + clusterID, err = cfgpickers.AskForCluster(ctx, w, cfgpickers.WithDatabricksConnect(minimalDbConnectVersion)) if err != nil { return err } - cfg.ClusterID = clusterID case configureServerless: - cfg.ClusterID = "" - cfg.ServerlessComputeID = "auto" + serverlessComputeID = "auto" default: // Respect the existing profile if it exists, even if it has // both cluster and serverless configured. Tools relying on @@ -195,20 +189,20 @@ depends on the existing profiles you have set in your configuration file // to clean up the profile under the assumption that serverless // is the preferred option. if existingProfile != nil { - cfg.ClusterID = existingProfile.ClusterID - cfg.ServerlessComputeID = existingProfile.ServerlessComputeID + clusterID = existingProfile.ClusterID + serverlessComputeID = existingProfile.ServerlessComputeID } } if profileName != "" { err = databrickscfg.SaveToProfile(ctx, &config.Config{ Profile: profileName, - Host: cfg.Host, - AuthType: cfg.AuthType, - AccountID: cfg.AccountID, - ClusterID: cfg.ClusterID, - ConfigFile: cfg.ConfigFile, - ServerlessComputeID: cfg.ServerlessComputeID, + Host: authArguments.Host, + AuthType: "databricks-cli", + AccountID: authArguments.AccountID, + ClusterID: clusterID, + ConfigFile: os.Getenv("DATABRICKS_CONFIG_FILE"), + ServerlessComputeID: serverlessComputeID, }) if err != nil { return err From 8b22f350e51ff47ed47b048b8431daf2588513c0 Mon Sep 17 00:00:00 2001 From: Tejas Kochar Date: Mon, 19 Jan 2026 13:40:56 +0000 Subject: [PATCH 2/9] add comment and remove unnecessary config field --- cmd/auth/login.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cmd/auth/login.go b/cmd/auth/login.go index 8d288c8bd8..82d0d1b7c7 100644 --- a/cmd/auth/login.go +++ b/cmd/auth/login.go @@ -158,6 +158,8 @@ depends on the existing profiles you have set in your configuration file if err = persistentAuth.Challenge(); err != nil { return err } + // An OAuth token has been successfully obtained at this point. + // We now attempt to save the current configuration as a profile in the config file. var clusterID, serverlessComputeID string switch { @@ -165,10 +167,9 @@ depends on the existing profiles you have set in your configuration file // Create a workspace client with direct credentials (not from a profile) // because the profile hasn't been saved yet. w, err := databricks.NewWorkspaceClient(&databricks.Config{ - Host: authArguments.Host, - AccountID: authArguments.AccountID, - AuthType: "databricks-cli", - ConfigFile: os.Getenv("DATABRICKS_CONFIG_FILE"), + Host: authArguments.Host, + AccountID: authArguments.AccountID, + AuthType: "databricks-cli", }) if err != nil { return err From 76a63178bd8528e0c2666d32c3bbf81dd473b9b6 Mon Sep 17 00:00:00 2001 From: Tejas Kochar Date: Mon, 19 Jan 2026 14:05:02 +0000 Subject: [PATCH 3/9] add auth login tests to ensure refactoring does not break something --- .../custom-config-file/out.databrickscfg | 6 +++++ .../login/custom-config-file/out.test.toml | 5 ++++ .../auth/login/custom-config-file/output.txt | 15 +++++++++++ .../cmd/auth/login/custom-config-file/script | 25 ++++++++++++++++++ .../auth/login/custom-config-file/test.toml | 3 +++ .../out.databrickscfg | 6 +++++ .../host-arg-overrides-profile/out.test.toml | 5 ++++ .../host-arg-overrides-profile/output.txt | 16 ++++++++++++ .../login/host-arg-overrides-profile/script | 26 +++++++++++++++++++ .../host-arg-overrides-profile/test.toml | 4 +++ .../login/host-from-profile/out.databrickscfg | 6 +++++ .../login/host-from-profile/out.test.toml | 5 ++++ .../auth/login/host-from-profile/output.txt | 16 ++++++++++++ .../cmd/auth/login/host-from-profile/script | 25 ++++++++++++++++++ .../auth/login/host-from-profile/test.toml | 4 +++ 15 files changed, 167 insertions(+) create mode 100644 acceptance/cmd/auth/login/custom-config-file/out.databrickscfg create mode 100644 acceptance/cmd/auth/login/custom-config-file/out.test.toml create mode 100644 acceptance/cmd/auth/login/custom-config-file/output.txt create mode 100644 acceptance/cmd/auth/login/custom-config-file/script create mode 100644 acceptance/cmd/auth/login/custom-config-file/test.toml create mode 100644 acceptance/cmd/auth/login/host-arg-overrides-profile/out.databrickscfg create mode 100644 acceptance/cmd/auth/login/host-arg-overrides-profile/out.test.toml create mode 100644 acceptance/cmd/auth/login/host-arg-overrides-profile/output.txt create mode 100644 acceptance/cmd/auth/login/host-arg-overrides-profile/script create mode 100644 acceptance/cmd/auth/login/host-arg-overrides-profile/test.toml create mode 100644 acceptance/cmd/auth/login/host-from-profile/out.databrickscfg create mode 100644 acceptance/cmd/auth/login/host-from-profile/out.test.toml create mode 100644 acceptance/cmd/auth/login/host-from-profile/output.txt create mode 100644 acceptance/cmd/auth/login/host-from-profile/script create mode 100644 acceptance/cmd/auth/login/host-from-profile/test.toml diff --git a/acceptance/cmd/auth/login/custom-config-file/out.databrickscfg b/acceptance/cmd/auth/login/custom-config-file/out.databrickscfg new file mode 100644 index 0000000000..e228e10bc6 --- /dev/null +++ b/acceptance/cmd/auth/login/custom-config-file/out.databrickscfg @@ -0,0 +1,6 @@ +; The profile defined in the DEFAULT section is to be used as a fallback when no profile is explicitly specified. +[DEFAULT] + +[custom-test] +host = [DATABRICKS_URL] +auth_type = databricks-cli diff --git a/acceptance/cmd/auth/login/custom-config-file/out.test.toml b/acceptance/cmd/auth/login/custom-config-file/out.test.toml new file mode 100644 index 0000000000..d560f1de04 --- /dev/null +++ b/acceptance/cmd/auth/login/custom-config-file/out.test.toml @@ -0,0 +1,5 @@ +Local = true +Cloud = false + +[EnvMatrix] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/cmd/auth/login/custom-config-file/output.txt b/acceptance/cmd/auth/login/custom-config-file/output.txt new file mode 100644 index 0000000000..7f48b023df --- /dev/null +++ b/acceptance/cmd/auth/login/custom-config-file/output.txt @@ -0,0 +1,15 @@ + +=== Login with DATABRICKS_CONFIG_FILE set +>>> [CLI] auth login --host [DATABRICKS_URL] --profile custom-test +Profile custom-test was successfully saved + +=== Default config file should NOT exist +OK: Default .databrickscfg does not exist + +=== Custom config file should exist +; The profile defined in the DEFAULT section is to be used as a fallback when no profile is explicitly specified. +[DEFAULT] + +[custom-test] +host = [DATABRICKS_URL] +auth_type = databricks-cli diff --git a/acceptance/cmd/auth/login/custom-config-file/script b/acceptance/cmd/auth/login/custom-config-file/script new file mode 100644 index 0000000000..5a111ec108 --- /dev/null +++ b/acceptance/cmd/auth/login/custom-config-file/script @@ -0,0 +1,25 @@ +sethome "./home" + +# Use a fake browser that performs a GET on the authorization URL +# and follows the redirect back to localhost. +export BROWSER="browser.py" + +# Set custom config file location via env var +export DATABRICKS_CONFIG_FILE="./home/custom.databrickscfg" + +title "Login with DATABRICKS_CONFIG_FILE set" +trace $CLI auth login --host $DATABRICKS_HOST --profile custom-test + +title "Default config file should NOT exist\n" +if [ -f "./home/.databrickscfg" ]; then + echo "ERROR: Default .databrickscfg exists but should not" + cat "./home/.databrickscfg" +else + echo "OK: Default .databrickscfg does not exist" +fi + +title "Custom config file should exist\n" +cat "./home/custom.databrickscfg" + +# Track the custom config file to surface changes +mv "./home/custom.databrickscfg" "./out.databrickscfg" diff --git a/acceptance/cmd/auth/login/custom-config-file/test.toml b/acceptance/cmd/auth/login/custom-config-file/test.toml new file mode 100644 index 0000000000..36c0e7e237 --- /dev/null +++ b/acceptance/cmd/auth/login/custom-config-file/test.toml @@ -0,0 +1,3 @@ +Ignore = [ + "home" +] diff --git a/acceptance/cmd/auth/login/host-arg-overrides-profile/out.databrickscfg b/acceptance/cmd/auth/login/host-arg-overrides-profile/out.databrickscfg new file mode 100644 index 0000000000..0b20cb5f03 --- /dev/null +++ b/acceptance/cmd/auth/login/host-arg-overrides-profile/out.databrickscfg @@ -0,0 +1,6 @@ +; The profile defined in the DEFAULT section is to be used as a fallback when no profile is explicitly specified. +[DEFAULT] + +[override-test] +host = [DATABRICKS_URL] +auth_type = databricks-cli diff --git a/acceptance/cmd/auth/login/host-arg-overrides-profile/out.test.toml b/acceptance/cmd/auth/login/host-arg-overrides-profile/out.test.toml new file mode 100644 index 0000000000..d560f1de04 --- /dev/null +++ b/acceptance/cmd/auth/login/host-arg-overrides-profile/out.test.toml @@ -0,0 +1,5 @@ +Local = true +Cloud = false + +[EnvMatrix] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/cmd/auth/login/host-arg-overrides-profile/output.txt b/acceptance/cmd/auth/login/host-arg-overrides-profile/output.txt new file mode 100644 index 0000000000..b13e876dd0 --- /dev/null +++ b/acceptance/cmd/auth/login/host-arg-overrides-profile/output.txt @@ -0,0 +1,16 @@ + +=== Initial profile with old host +[override-test] +host = https://old-host.cloud.databricks.com + +=== Login with positional host argument (should override profile) +>>> [CLI] auth login [DATABRICKS_URL] --profile override-test +Profile override-test was successfully saved + +=== Profile after login (host should be updated) +; The profile defined in the DEFAULT section is to be used as a fallback when no profile is explicitly specified. +[DEFAULT] + +[override-test] +host = [DATABRICKS_URL] +auth_type = databricks-cli diff --git a/acceptance/cmd/auth/login/host-arg-overrides-profile/script b/acceptance/cmd/auth/login/host-arg-overrides-profile/script new file mode 100644 index 0000000000..4353a3bb22 --- /dev/null +++ b/acceptance/cmd/auth/login/host-arg-overrides-profile/script @@ -0,0 +1,26 @@ +sethome "./home" + +# Create an existing profile with a DIFFERENT host +cat > "./home/.databrickscfg" <>> [CLI] auth login --profile existing-profile +Profile existing-profile was successfully saved + +=== Profile after login +; The profile defined in the DEFAULT section is to be used as a fallback when no profile is explicitly specified. +[DEFAULT] + +[existing-profile] +host = [DATABRICKS_URL] +auth_type = databricks-cli diff --git a/acceptance/cmd/auth/login/host-from-profile/script b/acceptance/cmd/auth/login/host-from-profile/script new file mode 100644 index 0000000000..dd452ca03e --- /dev/null +++ b/acceptance/cmd/auth/login/host-from-profile/script @@ -0,0 +1,25 @@ +sethome "./home" + +# Create an existing profile with a host +cat > "./home/.databrickscfg" < Date: Mon, 19 Jan 2026 14:22:24 +0000 Subject: [PATCH 4/9] strengthen assertion --- .../auth/login/custom-config-file/output.txt | 10 ++++++++-- .../cmd/auth/login/custom-config-file/script | 18 ++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/acceptance/cmd/auth/login/custom-config-file/output.txt b/acceptance/cmd/auth/login/custom-config-file/output.txt index 7f48b023df..79e67f48b2 100644 --- a/acceptance/cmd/auth/login/custom-config-file/output.txt +++ b/acceptance/cmd/auth/login/custom-config-file/output.txt @@ -1,12 +1,18 @@ -=== Login with DATABRICKS_CONFIG_FILE set +=== Initial custom config file (with old host) +[custom-test] +host = https://old-host.cloud.databricks.com +auth_type = pat +token = old-token-123 + +=== Login with new host (should override old host) >>> [CLI] auth login --host [DATABRICKS_URL] --profile custom-test Profile custom-test was successfully saved === Default config file should NOT exist OK: Default .databrickscfg does not exist -=== Custom config file should exist +=== Custom config file after login (old host should be replaced) ; The profile defined in the DEFAULT section is to be used as a fallback when no profile is explicitly specified. [DEFAULT] diff --git a/acceptance/cmd/auth/login/custom-config-file/script b/acceptance/cmd/auth/login/custom-config-file/script index 5a111ec108..8be849974e 100644 --- a/acceptance/cmd/auth/login/custom-config-file/script +++ b/acceptance/cmd/auth/login/custom-config-file/script @@ -7,7 +7,21 @@ export BROWSER="browser.py" # Set custom config file location via env var export DATABRICKS_CONFIG_FILE="./home/custom.databrickscfg" -title "Login with DATABRICKS_CONFIG_FILE set" +# Create an existing custom config file with a DIFFERENT host. +# The login command should use the host from --host argument, NOT from this file. +# If the wrong host (from the config file) were used, the OAuth flow would fail +# because the mock server only responds for $DATABRICKS_HOST. +cat > "./home/custom.databrickscfg" < Date: Mon, 19 Jan 2026 14:53:31 +0000 Subject: [PATCH 5/9] fix lint --- acceptance/cmd/auth/login/host-arg-overrides-profile/script | 1 - acceptance/cmd/auth/login/host-arg-overrides-profile/test.toml | 1 - acceptance/cmd/auth/login/host-from-profile/script | 1 - acceptance/cmd/auth/login/host-from-profile/test.toml | 1 - 4 files changed, 4 deletions(-) diff --git a/acceptance/cmd/auth/login/host-arg-overrides-profile/script b/acceptance/cmd/auth/login/host-arg-overrides-profile/script index 4353a3bb22..8d3cc68ae4 100644 --- a/acceptance/cmd/auth/login/host-arg-overrides-profile/script +++ b/acceptance/cmd/auth/login/host-arg-overrides-profile/script @@ -23,4 +23,3 @@ cat "./home/.databrickscfg" # Track the .databrickscfg file that was created to surface changes. mv "./home/.databrickscfg" "./out.databrickscfg" - diff --git a/acceptance/cmd/auth/login/host-arg-overrides-profile/test.toml b/acceptance/cmd/auth/login/host-arg-overrides-profile/test.toml index 1db87ba21b..36c0e7e237 100644 --- a/acceptance/cmd/auth/login/host-arg-overrides-profile/test.toml +++ b/acceptance/cmd/auth/login/host-arg-overrides-profile/test.toml @@ -1,4 +1,3 @@ Ignore = [ "home" ] - diff --git a/acceptance/cmd/auth/login/host-from-profile/script b/acceptance/cmd/auth/login/host-from-profile/script index dd452ca03e..9f44e4a705 100644 --- a/acceptance/cmd/auth/login/host-from-profile/script +++ b/acceptance/cmd/auth/login/host-from-profile/script @@ -22,4 +22,3 @@ cat "./home/.databrickscfg" # Track the .databrickscfg file that was created to surface changes. mv "./home/.databrickscfg" "./out.databrickscfg" - diff --git a/acceptance/cmd/auth/login/host-from-profile/test.toml b/acceptance/cmd/auth/login/host-from-profile/test.toml index 1db87ba21b..36c0e7e237 100644 --- a/acceptance/cmd/auth/login/host-from-profile/test.toml +++ b/acceptance/cmd/auth/login/host-from-profile/test.toml @@ -1,4 +1,3 @@ Ignore = [ "home" ] - From 6be5c6781df019d80987653e07be36319f68eb24 Mon Sep 17 00:00:00 2001 From: Tejas Kochar <65645667+tejaskochar-db@users.noreply.github.com> Date: Tue, 20 Jan 2026 08:53:18 +0100 Subject: [PATCH 6/9] Update cmd/auth/login.go Improve comment Co-authored-by: Renaud Hartert --- cmd/auth/login.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/auth/login.go b/cmd/auth/login.go index 82d0d1b7c7..cc6af24610 100644 --- a/cmd/auth/login.go +++ b/cmd/auth/login.go @@ -158,8 +158,10 @@ depends on the existing profiles you have set in your configuration file if err = persistentAuth.Challenge(); err != nil { return err } - // An OAuth token has been successfully obtained at this point. - // We now attempt to save the current configuration as a profile in the config file. + // At this point, an OAuth token has been successfully minted and stored + // in the CLI cache. The rest of the command focuses on: + // 1. Configuring cluster and serverless; + // 2. Saving the profile. var clusterID, serverlessComputeID string switch { From 8bac2ee5277ea8abbf456c22869dff14a3b9c531 Mon Sep 17 00:00:00 2001 From: Tejas Kochar Date: Tue, 20 Jan 2026 09:10:28 +0000 Subject: [PATCH 7/9] fetch minted token directly rather than spawning a child CLI process to fetch it --- cmd/auth/login.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/cmd/auth/login.go b/cmd/auth/login.go index cc6af24610..aece3698a6 100644 --- a/cmd/auth/login.go +++ b/cmd/auth/login.go @@ -158,20 +158,23 @@ depends on the existing profiles you have set in your configuration file if err = persistentAuth.Challenge(); err != nil { return err } - // At this point, an OAuth token has been successfully minted and stored + // At this point, an OAuth token has been successfully minted and stored // in the CLI cache. The rest of the command focuses on: // 1. Configuring cluster and serverless; - // 2. Saving the profile. + // 2. Saving the profile. var clusterID, serverlessComputeID string switch { case configureCluster: - // Create a workspace client with direct credentials (not from a profile) - // because the profile hasn't been saved yet. + // Get the token we just minted directly instead of spawning a child CLI process. + token, err := persistentAuth.Token() + if err != nil { + return err + } w, err := databricks.NewWorkspaceClient(&databricks.Config{ Host: authArguments.Host, AccountID: authArguments.AccountID, - AuthType: "databricks-cli", + Token: token.AccessToken, }) if err != nil { return err From ee574ea6344790cbb0d2d056f8c4f93eda068d6c Mon Sep 17 00:00:00 2001 From: Tejas Kochar Date: Tue, 20 Jan 2026 09:37:19 +0000 Subject: [PATCH 8/9] fetch minted token directly instead of spwaning child CLI process - only it works this time --- cmd/auth/login.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/cmd/auth/login.go b/cmd/auth/login.go index aece3698a6..a809609870 100644 --- a/cmd/auth/login.go +++ b/cmd/auth/login.go @@ -19,9 +19,11 @@ import ( "github.com/databricks/cli/libs/exec" "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/config" + sdkauth "github.com/databricks/databricks-sdk-go/config/experimental/auth" "github.com/databricks/databricks-sdk-go/credentials/u2m" browserpkg "github.com/pkg/browser" "github.com/spf13/cobra" + "golang.org/x/oauth2" ) func promptForProfile(ctx context.Context, defaultValue string) (string, error) { @@ -166,15 +168,16 @@ depends on the existing profiles you have set in your configuration file var clusterID, serverlessComputeID string switch { case configureCluster: - // Get the token we just minted directly instead of spawning a child CLI process. - token, err := persistentAuth.Token() - if err != nil { - return err - } + // Create a workspace client to list clusters for interactive selection. + // We use a custom CredentialsStrategy that wraps the token we just minted, + // avoiding the need to spawn a child CLI process (which AuthType "databricks-cli" does). + tokenSource := sdkauth.TokenSourceFn(func(ctx context.Context) (*oauth2.Token, error) { + return persistentAuth.Token() + }) w, err := databricks.NewWorkspaceClient(&databricks.Config{ - Host: authArguments.Host, - AccountID: authArguments.AccountID, - Token: token.AccessToken, + Host: authArguments.Host, + AccountID: authArguments.AccountID, + Credentials: config.NewTokenSourceStrategy("login-token", tokenSource), }) if err != nil { return err From 56999f3105584e66d74c95369c10f1b0e1a48c6a Mon Sep 17 00:00:00 2001 From: Tejas Kochar Date: Wed, 21 Jan 2026 06:04:23 +0000 Subject: [PATCH 9/9] Use inbuilt adapter for making persistent auth a token source --- cmd/auth/login.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cmd/auth/login.go b/cmd/auth/login.go index a809609870..4173deb0d6 100644 --- a/cmd/auth/login.go +++ b/cmd/auth/login.go @@ -19,11 +19,10 @@ import ( "github.com/databricks/cli/libs/exec" "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/config" - sdkauth "github.com/databricks/databricks-sdk-go/config/experimental/auth" + "github.com/databricks/databricks-sdk-go/config/experimental/auth/authconv" "github.com/databricks/databricks-sdk-go/credentials/u2m" browserpkg "github.com/pkg/browser" "github.com/spf13/cobra" - "golang.org/x/oauth2" ) func promptForProfile(ctx context.Context, defaultValue string) (string, error) { @@ -171,13 +170,10 @@ depends on the existing profiles you have set in your configuration file // Create a workspace client to list clusters for interactive selection. // We use a custom CredentialsStrategy that wraps the token we just minted, // avoiding the need to spawn a child CLI process (which AuthType "databricks-cli" does). - tokenSource := sdkauth.TokenSourceFn(func(ctx context.Context) (*oauth2.Token, error) { - return persistentAuth.Token() - }) w, err := databricks.NewWorkspaceClient(&databricks.Config{ Host: authArguments.Host, AccountID: authArguments.AccountID, - Credentials: config.NewTokenSourceStrategy("login-token", tokenSource), + Credentials: config.NewTokenSourceStrategy("login-token", authconv.AuthTokenSource(persistentAuth)), }) if err != nil { return err