Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f30ebe8
Add `databricks auth switch` command for setting the default profile
simonfaltum Mar 4, 2026
e75348f
Fix hasNoProfiles on fresh machines and GetDefaultProfile side effects
simonfaltum Mar 4, 2026
a7f5031
Reject positional args and show current default in interactive picker
simonfaltum Mar 4, 2026
b2c5315
Show resolved default profile name in auth describe output
simonfaltum Mar 4, 2026
71b7c7d
Deduplicate tilde expansion, remove redundant file read and test asse…
simonfaltum Mar 4, 2026
1614826
Rename settings section to [__databricks-settings__]
simonfaltum Mar 4, 2026
ea4a9e0
Update acceptance test expected output for auto-default on first login
simonfaltum Mar 4, 2026
3f72a7f
Wire default_profile into workspace client resolution
simonfaltum Mar 6, 2026
51c780a
Merge branch 'main' into simonfaltum/auth-switch
simonfaltum Mar 6, 2026
b72f644
Replace context.Background() with t.Context() in tests
simonfaltum Mar 6, 2026
f78d6a4
Merge branch 'main' into simonfaltum/auth-switch
simonfaltum Mar 9, 2026
bbb07d3
Address review comments on auth switch PR
simonfaltum Mar 9, 2026
4129a82
Merge branch 'main' into simonfaltum/auth-switch
simonfaltum Mar 9, 2026
95ae4c3
Merge branch 'main' into simonfaltum/auth-switch
simonfaltum Mar 9, 2026
8e88cc2
Merge branch 'main' into simonfaltum/auth-switch
simonfaltum Mar 9, 2026
4876b8b
Merge branch 'main' into simonfaltum/auth-switch
simonfaltum Mar 10, 2026
75aa2dc
Fix default profile edge cases in auth switch
simonfaltum Mar 10, 2026
389921a
Address PR review comments for auth switch
simonfaltum Mar 10, 2026
3d0ee11
Fix gofumpt: group consecutive const declarations
simonfaltum Mar 10, 2026
55d8373
Fix gofmt formatting in const block
simonfaltum Mar 10, 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
5 changes: 5 additions & 0 deletions acceptance/cmd/auth/describe/default-profile/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions acceptance/cmd/auth/describe/default-profile/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

=== Describe without --profile (should use default)

>>> [CLI] auth describe
Host: [DATABRICKS_URL]
User: [USERNAME]
Authenticated with: pat
-----
Current configuration:
✓ host: [DATABRICKS_URL] (from DATABRICKS_HOST environment variable)
✓ token: ******** (from DATABRICKS_TOKEN environment variable)
✓ profile: my-workspace
✓ databricks_cli_path: [CLI]
✓ auth_type: pat
✓ rate_limit: [NUMID] (from DATABRICKS_RATE_LIMIT environment variable)
20 changes: 20 additions & 0 deletions acceptance/cmd/auth/describe/default-profile/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
sethome "./home"

# Create a config with two profiles and an explicit default.
cat > "./home/.databrickscfg" <<EOF
[DEFAULT]

[my-workspace]
host = $DATABRICKS_HOST
token = $DATABRICKS_TOKEN

[other-workspace]
host = https://other.cloud.databricks.com
token = other-token

[__settings__]
default_profile = my-workspace
EOF

title "Describe without --profile (should use default)\n"
trace $CLI auth describe
3 changes: 3 additions & 0 deletions acceptance/cmd/auth/describe/default-profile/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Ignore = [
"home"
]
3 changes: 3 additions & 0 deletions acceptance/cmd/auth/login/nominal/out.databrickscfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
[test]
host = [DATABRICKS_URL]
auth_type = databricks-cli

[__settings__]
default_profile = test
4 changes: 2 additions & 2 deletions acceptance/cmd/auth/login/nominal/output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
Profile test was successfully saved

>>> [CLI] auth profiles
Name Host Valid
test [DATABRICKS_URL] YES
Name Host Valid
test (Default) [DATABRICKS_URL] YES
3 changes: 3 additions & 0 deletions acceptance/cmd/auth/login/with-scopes/out.databrickscfg
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@
host = [DATABRICKS_URL]
scopes = jobs,pipelines,clusters
auth_type = databricks-cli

[__settings__]
default_profile = scoped-test
15 changes: 15 additions & 0 deletions acceptance/cmd/auth/switch/nominal/out.databrickscfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
; The profile defined in the DEFAULT section is to be used as a fallback when no profile is explicitly specified.
[DEFAULT]

[profile-a]
host = https://profile-a.cloud.databricks.com
token = token-a
auth_type = pat

[profile-b]
host = https://profile-b.cloud.databricks.com
token = token-b
auth_type = pat

[__settings__]
default_profile = profile-b
5 changes: 5 additions & 0 deletions acceptance/cmd/auth/switch/nominal/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 72 additions & 0 deletions acceptance/cmd/auth/switch/nominal/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@

=== Initial config
; The profile defined in the DEFAULT section is to be used as a fallback when no profile is explicitly specified.
[DEFAULT]

[profile-a]
host = https://profile-a.cloud.databricks.com
token = token-a
auth_type = pat

[profile-b]
host = https://profile-b.cloud.databricks.com
token = token-b
auth_type = pat

=== Switch to profile-a

>>> [CLI] auth switch --profile profile-a
Default profile set to "profile-a".

=== Config after first switch
; The profile defined in the DEFAULT section is to be used as a fallback when no profile is explicitly specified.
[DEFAULT]

[profile-a]
host = https://profile-a.cloud.databricks.com
token = token-a
auth_type = pat

[profile-b]
host = https://profile-b.cloud.databricks.com
token = token-b
auth_type = pat

[__settings__]
default_profile = profile-a

=== Profiles after first switch

>>> [CLI] auth profiles --skip-validate
Name Host Valid
profile-a (Default) https://profile-a.cloud.databricks.com NO
profile-b https://profile-b.cloud.databricks.com NO

=== Switch to profile-b

>>> [CLI] auth switch --profile profile-b
Default profile set to "profile-b".

=== Config after second switch
; The profile defined in the DEFAULT section is to be used as a fallback when no profile is explicitly specified.
[DEFAULT]

[profile-a]
host = https://profile-a.cloud.databricks.com
token = token-a
auth_type = pat

[profile-b]
host = https://profile-b.cloud.databricks.com
token = token-b
auth_type = pat

[__settings__]
default_profile = profile-b

=== Profiles after second switch

>>> [CLI] auth profiles --skip-validate
Name Host Valid
profile-a https://profile-a.cloud.databricks.com NO
profile-b (Default) https://profile-b.cloud.databricks.com NO
41 changes: 41 additions & 0 deletions acceptance/cmd/auth/switch/nominal/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
sethome "./home"

# Create two profiles without a [__settings__] section.
cat > "./home/.databrickscfg" <<'EOF'
; The profile defined in the DEFAULT section is to be used as a fallback when no profile is explicitly specified.
[DEFAULT]

[profile-a]
host = https://profile-a.cloud.databricks.com
token = token-a
auth_type = pat

[profile-b]
host = https://profile-b.cloud.databricks.com
token = token-b
auth_type = pat
EOF

title "Initial config\n"
cat "./home/.databrickscfg"

title "Switch to profile-a\n"
trace $CLI auth switch --profile profile-a

title "Config after first switch\n"
cat "./home/.databrickscfg"

title "Profiles after first switch\n"
trace $CLI auth profiles --skip-validate

title "Switch to profile-b\n"
trace $CLI auth switch --profile profile-b

title "Config after second switch\n"
cat "./home/.databrickscfg"

title "Profiles after second switch\n"
trace $CLI auth profiles --skip-validate

# Track the final .databrickscfg to surface changes.
cp "./home/.databrickscfg" "./out.databrickscfg"
3 changes: 3 additions & 0 deletions acceptance/cmd/auth/switch/nominal/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Ignore = [
"home"
]
1 change: 1 addition & 0 deletions cmd/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ GCP: https://docs.gcp.databricks.com/dev-tools/auth/index.html`,
cmd.AddCommand(newProfilesCommand())
cmd.AddCommand(newTokenCommand(&authArguments))
cmd.AddCommand(newDescribeCommand())
cmd.AddCommand(newSwitchCommand())
return cmd
}

Expand Down
18 changes: 13 additions & 5 deletions cmd/auth/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
"github.com/databricks/cli/cmd/root"
"github.com/databricks/cli/libs/cmdctx"
"github.com/databricks/cli/libs/cmdio"
"github.com/databricks/cli/libs/databrickscfg"
"github.com/databricks/cli/libs/flags"
"github.com/databricks/cli/libs/log"
"github.com/databricks/databricks-sdk-go/config"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -177,13 +179,19 @@ func getAuthDetails(cmd *cobra.Command, cfg *config.Config, showSensitive bool)
}
}

// If profile is not set explicitly, default to "default"
// If profile is not set explicitly, show which profile is being used.
if _, ok := details.Configuration["profile"]; !ok {
profile := cfg.Profile
if profile == "" {
profile = "default"
displayProfile := cfg.Profile
if displayProfile == "" {
displayProfile = "default"
resolved, err := databrickscfg.GetConfiguredDefaultProfile(cmd.Context(), cfg.ConfigFile)
if err != nil {
log.Warnf(cmd.Context(), "Failed to read default profile setting: %v", err)
} else if resolved != "" {
displayProfile = fmt.Sprintf("default (%s)", resolved)
}
}
details.Configuration["profile"] = &config.AttrConfig{Value: profile, Source: config.Source{Type: config.SourceDynamicConfig}}
details.Configuration["profile"] = &config.AttrConfig{Value: displayProfile, Source: config.Source{Type: config.SourceDynamicConfig}}
}

// Unset source for databricks_cli_path because it can't be overridden anyway
Expand Down
17 changes: 15 additions & 2 deletions cmd/auth/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"io"
"os"
"runtime"
"strings"
"time"
Expand All @@ -17,6 +16,7 @@ import (
"github.com/databricks/cli/libs/databrickscfg/profile"
"github.com/databricks/cli/libs/env"
"github.com/databricks/cli/libs/exec"
"github.com/databricks/cli/libs/log"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/config"
"github.com/databricks/databricks-sdk-go/config/experimental/auth/authconv"
Expand Down Expand Up @@ -241,6 +241,13 @@ depends on the existing profiles you have set in your configuration file
}

if profileName != "" {
configFile := env.Get(ctx, "DATABRICKS_CONFIG_FILE")

// Check if this will be the only profile in the file.
// If so, we'll auto-set it as the default after saving.
allProfiles, loadErr := profile.DefaultProfiler.LoadProfiles(ctx, profile.MatchAllProfiles)
isOnlyProfile := errors.Is(loadErr, profile.ErrNoConfiguration) || (loadErr == nil && len(allProfiles) == 0)

err := databrickscfg.SaveToProfile(ctx, &config.Config{
Profile: profileName,
Host: authArguments.Host,
Expand All @@ -249,14 +256,20 @@ depends on the existing profiles you have set in your configuration file
WorkspaceID: authArguments.WorkspaceID,
Experimental_IsUnifiedHost: authArguments.IsUnifiedHost,
ClusterID: clusterID,
ConfigFile: os.Getenv("DATABRICKS_CONFIG_FILE"),
ConfigFile: configFile,
ServerlessComputeID: serverlessComputeID,
Scopes: scopesList,
}, clearKeys...)
if err != nil {
return err
}

if isOnlyProfile {
if err := databrickscfg.SetDefaultProfile(ctx, profileName, configFile); err != nil {
log.Debugf(ctx, "Failed to auto-set default profile: %v", err)
}
}

cmdio.LogString(ctx, fmt.Sprintf("Profile %s was successfully saved", profileName))
}

Expand Down
8 changes: 7 additions & 1 deletion cmd/auth/profiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"time"

"github.com/databricks/cli/libs/cmdio"
"github.com/databricks/cli/libs/databrickscfg"
"github.com/databricks/cli/libs/databrickscfg/profile"
"github.com/databricks/cli/libs/log"
"github.com/databricks/databricks-sdk-go"
Expand All @@ -26,6 +27,7 @@ type profileMetadata struct {
Cloud string `json:"cloud"`
AuthType string `json:"auth_type"`
Valid bool `json:"valid"`
Default bool `json:"default,omitempty"`
}

func (c *profileMetadata) IsEmpty() bool {
Expand Down Expand Up @@ -92,7 +94,7 @@ func newProfilesCommand() *cobra.Command {
Annotations: map[string]string{
"template": cmdio.Heredoc(`
{{header "Name"}} {{header "Host"}} {{header "Valid"}}
{{range .Profiles}}{{.Name | green}} {{.Host|cyan}} {{bool .Valid}}
{{range .Profiles}}{{.Name | green}}{{if .Default}} (Default){{end}} {{.Host|cyan}} {{bool .Valid}}
{{end}}`),
},
}
Expand All @@ -111,6 +113,9 @@ func newProfilesCommand() *cobra.Command {
} else if err != nil {
return fmt.Errorf("cannot parse config file: %w", err)
}

defaultProfile := databrickscfg.GetConfiguredDefaultProfileFrom(iniFile)

var wg sync.WaitGroup
for _, v := range iniFile.Sections() {
hash := v.KeysHash()
Expand All @@ -119,6 +124,7 @@ func newProfilesCommand() *cobra.Command {
Host: hash["host"],
AccountID: hash["account_id"],
WorkspaceID: hash["workspace_id"],
Default: v.Name() == defaultProfile,
}
if profile.IsEmpty() {
continue
Expand Down
31 changes: 31 additions & 0 deletions cmd/auth/profiles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,34 @@ func TestProfiles(t *testing.T) {
assert.Equal(t, "aws", profile.Cloud)
assert.Equal(t, "pat", profile.AuthType)
}

func TestProfilesDefaultMarker(t *testing.T) {
ctx := t.Context()
dir := t.TempDir()
configFile := filepath.Join(dir, ".databrickscfg")

// Create two profiles.
for _, name := range []string{"profile-a", "profile-b"} {
err := databrickscfg.SaveToProfile(ctx, &config.Config{
ConfigFile: configFile,
Profile: name,
Host: "https://" + name + ".cloud.databricks.com",
Token: "token",
})
require.NoError(t, err)
}

// Set profile-a as the default.
err := databrickscfg.SetDefaultProfile(ctx, "profile-a", configFile)
require.NoError(t, err)

t.Setenv("HOME", dir)
if runtime.GOOS == "windows" {
t.Setenv("USERPROFILE", dir)
}

// Read back the default profile and verify.
defaultProfile, err := databrickscfg.GetDefaultProfile(ctx, configFile)
require.NoError(t, err)
assert.Equal(t, "profile-a", defaultProfile)
}
Loading