From cd9fe628593ae575dbad7aa395922aedad4ee998 Mon Sep 17 00:00:00 2001 From: simon Date: Wed, 25 Feb 2026 16:09:14 +0100 Subject: [PATCH 1/3] Preserve profile fields on re-login instead of wiping all keys SaveToProfile previously deleted all keys in a profile section before writing, destroying fields like cluster_id, warehouse_id, scopes, and custom keys on every `auth login` or `configure` invocation. Switch to merge semantics: existing keys not mentioned in the new config are preserved. Add a clearKeys variadic parameter so callers can explicitly remove incompatible keys on auth-type transitions (e.g., OAuth login clears PAT/M2M/Azure/GCP credentials; PAT configure clears auth_type and OAuth metadata). Handle boolean zero-value for experimental_is_unified_host by clearing it explicitly when false. Scopes are preserved across re-logins in both auth login and inline login (auth token): when --scopes is not passed, the existing profile's scopes are read back and used for the OAuth challenge, keeping the profile truthful and the user's preference intact. Non-auth config properties like azure_environment are preserved. Clear serverless_compute_id when cluster_id is set (via flag or env). Clear experimental_is_unified_host and databricks_cli_path during PAT configure. --- .../login/preserve-fields/out.databrickscfg | 7 + .../auth/login/preserve-fields/out.test.toml | 5 + .../cmd/auth/login/preserve-fields/output.txt | 21 +++ .../cmd/auth/login/preserve-fields/script | 28 ++++ .../cmd/auth/login/preserve-fields/test.toml | 3 + cmd/auth/login.go | 77 +++++++++-- cmd/auth/token.go | 24 +++- cmd/configure/configure.go | 55 +++++++- cmd/configure/configure_test.go | 128 ++++++++++++++++++ libs/databrickscfg/ops.go | 15 +- libs/databrickscfg/ops_test.go | 121 ++++++++++++++--- libs/databrickscfg/profile/file.go | 1 + libs/databrickscfg/profile/profile.go | 1 + 13 files changed, 445 insertions(+), 41 deletions(-) create mode 100644 acceptance/cmd/auth/login/preserve-fields/out.databrickscfg create mode 100644 acceptance/cmd/auth/login/preserve-fields/out.test.toml create mode 100644 acceptance/cmd/auth/login/preserve-fields/output.txt create mode 100644 acceptance/cmd/auth/login/preserve-fields/script create mode 100644 acceptance/cmd/auth/login/preserve-fields/test.toml diff --git a/acceptance/cmd/auth/login/preserve-fields/out.databrickscfg b/acceptance/cmd/auth/login/preserve-fields/out.databrickscfg new file mode 100644 index 0000000000..3658c4d33b --- /dev/null +++ b/acceptance/cmd/auth/login/preserve-fields/out.databrickscfg @@ -0,0 +1,7 @@ +[DEFAULT] +host = [DATABRICKS_URL] +cluster_id = existing-cluster-123 +warehouse_id = warehouse-456 +azure_environment = USGOVERNMENT +custom_key = my-custom-value +auth_type = databricks-cli diff --git a/acceptance/cmd/auth/login/preserve-fields/out.test.toml b/acceptance/cmd/auth/login/preserve-fields/out.test.toml new file mode 100644 index 0000000000..d560f1de04 --- /dev/null +++ b/acceptance/cmd/auth/login/preserve-fields/out.test.toml @@ -0,0 +1,5 @@ +Local = true +Cloud = false + +[EnvMatrix] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/cmd/auth/login/preserve-fields/output.txt b/acceptance/cmd/auth/login/preserve-fields/output.txt new file mode 100644 index 0000000000..625efd0bf2 --- /dev/null +++ b/acceptance/cmd/auth/login/preserve-fields/output.txt @@ -0,0 +1,21 @@ + +=== Initial profile +[DEFAULT] +host = [DATABRICKS_URL] +cluster_id = existing-cluster-123 +warehouse_id = warehouse-456 +azure_environment = USGOVERNMENT +custom_key = my-custom-value + +=== Run auth login (no --configure-cluster or --configure-serverless) +>>> [CLI] auth login --host [DATABRICKS_URL] --profile DEFAULT +Profile DEFAULT was successfully saved + +=== Profile after auth login — all non-auth fields should be preserved +[DEFAULT] +host = [DATABRICKS_URL] +cluster_id = existing-cluster-123 +warehouse_id = warehouse-456 +azure_environment = USGOVERNMENT +custom_key = my-custom-value +auth_type = databricks-cli diff --git a/acceptance/cmd/auth/login/preserve-fields/script b/acceptance/cmd/auth/login/preserve-fields/script new file mode 100644 index 0000000000..a82101c892 --- /dev/null +++ b/acceptance/cmd/auth/login/preserve-fields/script @@ -0,0 +1,28 @@ +sethome "./home" + +# Create an initial profile with cluster_id, warehouse_id, azure_environment, +# and a custom key that is not a recognized SDK config attribute. +cat > "./home/.databrickscfg" < 0 { + persistentAuthOpts = append(persistentAuthOpts, u2m.WithScopes(scopesList)) + } + persistentAuth, err := u2m.NewPersistentAuth(ctx, persistentAuthOpts...) if err != nil { return "", nil, err } @@ -443,6 +456,10 @@ func runInlineLogin(ctx context.Context, profiler profile.Profiler) (string, *pr return "", nil, err } + clearKeys := oauthLoginClearKeys() + if !loginArgs.IsUnifiedHost { + clearKeys = append(clearKeys, "experimental_is_unified_host") + } err = databrickscfg.SaveToProfile(ctx, &config.Config{ Profile: profileName, Host: loginArgs.Host, @@ -451,7 +468,8 @@ func runInlineLogin(ctx context.Context, profiler profile.Profiler) (string, *pr WorkspaceID: loginArgs.WorkspaceID, Experimental_IsUnifiedHost: loginArgs.IsUnifiedHost, ConfigFile: os.Getenv("DATABRICKS_CONFIG_FILE"), - }) + Scopes: scopesList, + }, clearKeys...) if err != nil { return "", nil, err } diff --git a/cmd/configure/configure.go b/cmd/configure/configure.go index 4f36bf7c72..dd308473db 100644 --- a/cmd/configure/configure.go +++ b/cmd/configure/configure.go @@ -12,6 +12,43 @@ import ( "github.com/spf13/cobra" ) +// patConfigureClearKeys lists profile keys that should be explicitly removed +// when saving a PAT-based profile via `databricks configure`. This prevents +// stale auth credentials from other methods (OAuth, Azure, GCP, etc.) from +// remaining in the profile and causing multi-auth validation failures. +var patConfigureClearKeys = []string{ + // OAuth metadata + "auth_type", + "scopes", + // Basic auth + "username", + "password", + // M2M OAuth + "client_id", + "client_secret", + // Google + "google_service_account", + "google_credentials", + // Azure (azure_environment is NOT cleared — it's a non-auth + // config property describing the Azure cloud, not a credential) + "azure_workspace_resource_id", + "azure_use_msi", + "azure_client_secret", + "azure_client_id", + "azure_tenant_id", + "azure_login_app_id", + // Metadata service + "metadata_service_url", + // GitHub Actions OIDC + "actions_id_token_request_url", + "actions_id_token_request_token", + // OIDC file/env + "databricks_id_token_filepath", + "oidc_token_env", + // OAuth CLI path + "databricks_cli_path", +} + func configureInteractive(cmd *cobra.Command, flags *configureFlags, cfg *config.Config) error { ctx := cmd.Context() @@ -141,14 +178,28 @@ The host must be specified with the --host flag or the DATABRICKS_HOST environme // This is relevant for OAuth only. cfg.DatabricksCliPath = "" - // Save profile to config file. + // Save profile to config file. PAT-based configure clears all + // non-PAT auth credentials and OAuth metadata to prevent + // multi-auth conflicts in the profile. + clearKeys := append([]string{}, patConfigureClearKeys...) + + // Cluster and serverless are mutually exclusive. Clear serverless + // when a cluster is being set (via flag or env var). + if cfg.ClusterID != "" { + clearKeys = append(clearKeys, "serverless_compute_id") + } + + // Clear stale unified-host metadata — PAT profiles don't use it, + // and leaving it can change HostType() routing. + clearKeys = append(clearKeys, "experimental_is_unified_host") + return databrickscfg.SaveToProfile(ctx, &config.Config{ Profile: cfg.Profile, Host: cfg.Host, Token: cfg.Token, ClusterID: cfg.ClusterID, ConfigFile: cfg.ConfigFile, - }) + }, clearKeys...) } return cmd diff --git a/cmd/configure/configure_test.go b/cmd/configure/configure_test.go index 309c65363c..5c62d50c6f 100644 --- a/cmd/configure/configure_test.go +++ b/cmd/configure/configure_test.go @@ -181,6 +181,134 @@ func TestEnvVarsConfigureNoArgsNoInteractive(t *testing.T) { assertKeyValueInSection(t, defaultSection, "token", "secret") } +func TestConfigureClearsOAuthAuthType(t *testing.T) { + ctx := context.Background() + tempHomeDir := setup(t) + cfgPath := filepath.Join(tempHomeDir, ".databrickscfg") + + // Pre-populate a profile with OAuth auth_type and scopes (as if `auth login` was run). + err := os.WriteFile(cfgPath, []byte(`[DEFAULT] +host = https://host +auth_type = databricks-cli +scopes = all-apis +`), 0o600) + assert.NoError(t, err) + + inp := getTempFileWithContent(t, tempHomeDir, "new-token\n") + defer inp.Close() + oldStdin := os.Stdin + t.Cleanup(func() { os.Stdin = oldStdin }) + os.Stdin = inp + + cmd := cmd.New(ctx) + cmd.SetArgs([]string{"configure", "--token", "--host", "https://host"}) + + err = root.Execute(ctx, cmd) + assert.NoError(t, err) + + cfg, err := ini.Load(cfgPath) + assert.NoError(t, err) + + section, err := cfg.GetSection("DEFAULT") + assert.NoError(t, err) + + assertKeyValueInSection(t, section, "host", "https://host") + assertKeyValueInSection(t, section, "token", "new-token") + + // auth_type and scopes should be cleared by PAT configure. + _, err = section.GetKey("auth_type") + assert.Error(t, err, "auth_type should have been cleared by configure") + _, err = section.GetKey("scopes") + assert.Error(t, err, "scopes should have been cleared by configure") +} + +func TestConfigureClearsUnifiedHostMetadata(t *testing.T) { + ctx := context.Background() + tempHomeDir := setup(t) + cfgPath := filepath.Join(tempHomeDir, ".databrickscfg") + + // Pre-populate a profile with unified-host metadata (as if OAuth login was run against a unified host). + err := os.WriteFile(cfgPath, []byte(`[DEFAULT] +host = https://api.databricks.com +experimental_is_unified_host = true +account_id = acc-123 +workspace_id = ws-456 +auth_type = databricks-cli +`), 0o600) + assert.NoError(t, err) + + inp := getTempFileWithContent(t, tempHomeDir, "new-token\n") + defer inp.Close() + oldStdin := os.Stdin + t.Cleanup(func() { os.Stdin = oldStdin }) + os.Stdin = inp + + cmd := cmd.New(ctx) + cmd.SetArgs([]string{"configure", "--token", "--host", "https://api.databricks.com"}) + + err = root.Execute(ctx, cmd) + assert.NoError(t, err) + + cfg, err := ini.Load(cfgPath) + assert.NoError(t, err) + + section, err := cfg.GetSection("DEFAULT") + assert.NoError(t, err) + + assertKeyValueInSection(t, section, "host", "https://api.databricks.com") + assertKeyValueInSection(t, section, "token", "new-token") + + // experimental_is_unified_host should be cleared by PAT configure. + _, err = section.GetKey("experimental_is_unified_host") + assert.Error(t, err, "experimental_is_unified_host should have been cleared") + + // account_id and workspace_id are identity fields — they should be preserved. + assertKeyValueInSection(t, section, "account_id", "acc-123") + assertKeyValueInSection(t, section, "workspace_id", "ws-456") +} + +func TestConfigureClearsServerlessWhenClusterFromEnv(t *testing.T) { + ctx := context.Background() + tempHomeDir := setup(t) + cfgPath := filepath.Join(tempHomeDir, ".databrickscfg") + + // Pre-populate a profile with serverless_compute_id. + err := os.WriteFile(cfgPath, []byte(`[DEFAULT] +host = https://host +serverless_compute_id = auto +`), 0o600) + assert.NoError(t, err) + + // Set cluster ID via env var (not via --configure-cluster flag). + t.Setenv("DATABRICKS_CLUSTER_ID", "env-cluster-789") + + inp := getTempFileWithContent(t, tempHomeDir, "new-token\n") + defer inp.Close() + oldStdin := os.Stdin + t.Cleanup(func() { os.Stdin = oldStdin }) + os.Stdin = inp + + cmd := cmd.New(ctx) + cmd.SetArgs([]string{"configure", "--token", "--host", "https://host"}) + + err = root.Execute(ctx, cmd) + assert.NoError(t, err) + + cfg, err := ini.Load(cfgPath) + assert.NoError(t, err) + + section, err := cfg.GetSection("DEFAULT") + assert.NoError(t, err) + + assertKeyValueInSection(t, section, "host", "https://host") + assertKeyValueInSection(t, section, "token", "new-token") + assertKeyValueInSection(t, section, "cluster_id", "env-cluster-789") + + // serverless_compute_id should be cleared since cluster_id is set. + _, err = section.GetKey("serverless_compute_id") + assert.Error(t, err, "serverless_compute_id should have been cleared when cluster_id is set") +} + func TestCustomProfileConfigureNoInteractive(t *testing.T) { ctx := context.Background() tempHomeDir := setup(t) diff --git a/libs/databrickscfg/ops.go b/libs/databrickscfg/ops.go index 32c53b0004..54a4c55245 100644 --- a/libs/databrickscfg/ops.go +++ b/libs/databrickscfg/ops.go @@ -80,7 +80,12 @@ func matchOrCreateSection(ctx context.Context, configFile *config.File, cfg *con return section, nil } -func SaveToProfile(ctx context.Context, cfg *config.Config) error { +// SaveToProfile merges the provided config into a .databrickscfg profile. +// Non-zero fields in cfg overwrite existing values. Existing keys not +// mentioned in cfg are preserved. Keys listed in clearKeys are explicitly +// removed (use this for mutually exclusive fields like cluster_id vs +// serverless_compute_id, or to drop stale auth credentials on auth-type switch). +func SaveToProfile(ctx context.Context, cfg *config.Config, clearKeys ...string) error { configFile, err := loadOrCreateConfigFile(cfg.ConfigFile) if err != nil { return err @@ -95,11 +100,13 @@ func SaveToProfile(ctx context.Context, cfg *config.Config) error { cfg.Profile = "" cfg.ConfigFile = "" - // clear old keys in case we're overriding the section - for _, oldKey := range section.KeyStrings() { - section.DeleteKey(oldKey) + // Explicitly remove keys the caller wants cleared. + for _, key := range clearKeys { + section.DeleteKey(key) } + // Write non-zero fields from the new config. Iterates ConfigAttributes + // in declaration order for deterministic key ordering on new profiles. for _, attr := range config.ConfigAttributes { if attr.IsZero(cfg) { continue diff --git a/libs/databrickscfg/ops_test.go b/libs/databrickscfg/ops_test.go index ed81da2d10..50e342a97f 100644 --- a/libs/databrickscfg/ops_test.go +++ b/libs/databrickscfg/ops_test.go @@ -178,48 +178,131 @@ token = xyz `, string(contents)) } -func TestSaveToProfile_ClearingPreviousProfile(t *testing.T) { +func TestSaveToProfile_MergePreservesExistingKeys(t *testing.T) { ctx := context.Background() path := filepath.Join(t.TempDir(), "databrickscfg") + // First save: profile with host and token. err := SaveToProfile(ctx, &config.Config{ ConfigFile: path, Profile: "abc", Host: "https://foo", Token: "xyz", }) - assert.NoError(t, err) + require.NoError(t, err) + // Second save: add auth_type but don't mention token. + // Token should be preserved by merge semantics. err = SaveToProfile(ctx, &config.Config{ ConfigFile: path, - Profile: "bcd", - Host: "https://bar", - Token: "zyx", + Host: "https://foo", + AuthType: "databricks-cli", }) - assert.NoError(t, err) - assert.FileExists(t, path+".bak") + require.NoError(t, err) - err = SaveToProfile(ctx, &config.Config{ + file, err := loadOrCreateConfigFile(path) + require.NoError(t, err) + + abc, err := file.GetSection("abc") + require.NoError(t, err) + raw := abc.KeysHash() + assert.Len(t, raw, 3) + assert.Equal(t, "https://foo", raw["host"]) + assert.Equal(t, "databricks-cli", raw["auth_type"]) + assert.Equal(t, "xyz", raw["token"]) +} + +func TestSaveToProfile_ClearKeysRemovesSpecifiedKeys(t *testing.T) { + ctx := context.Background() + path := filepath.Join(t.TempDir(), "databrickscfg") + + // First save: profile with host, token, and cluster_id. + err := SaveToProfile(ctx, &config.Config{ ConfigFile: path, + Profile: "abc", Host: "https://foo", - AuthType: "databricks-cli", + Token: "xyz", + ClusterID: "cluster-123", }) - assert.NoError(t, err) + require.NoError(t, err) + + // Second save: switch to OAuth, clear token and cluster_id. + err = SaveToProfile(ctx, &config.Config{ + ConfigFile: path, + Host: "https://foo", + AuthType: "databricks-cli", + ServerlessComputeID: "auto", + }, "token", "cluster_id") + require.NoError(t, err) file, err := loadOrCreateConfigFile(path) - assert.NoError(t, err) + require.NoError(t, err) - assert.Len(t, file.Sections(), 3) - assert.True(t, file.HasSection("DEFAULT")) - assert.True(t, file.HasSection("bcd")) - assert.True(t, file.HasSection("bcd")) + abc, err := file.GetSection("abc") + require.NoError(t, err) + raw := abc.KeysHash() + assert.Equal(t, "https://foo", raw["host"]) + assert.Equal(t, "databricks-cli", raw["auth_type"]) + assert.Equal(t, "auto", raw["serverless_compute_id"]) + assert.Empty(t, raw["token"], "token should have been cleared") + assert.Empty(t, raw["cluster_id"], "cluster_id should have been cleared") + assert.Len(t, raw, 3) +} - dlft, err := file.GetSection("DEFAULT") - assert.NoError(t, err) - assert.Empty(t, dlft.KeysHash()) +func TestSaveToProfile_OverwritesExistingValues(t *testing.T) { + ctx := context.Background() + path := filepath.Join(t.TempDir(), "databrickscfg") + + // First save: profile with host. + err := SaveToProfile(ctx, &config.Config{ + ConfigFile: path, + Profile: "abc", + Host: "https://old-host", + }) + require.NoError(t, err) + + // Second save: update host value. + err = SaveToProfile(ctx, &config.Config{ + ConfigFile: path, + Profile: "abc", + Host: "https://new-host", + }) + require.NoError(t, err) + + file, err := loadOrCreateConfigFile(path) + require.NoError(t, err) abc, err := file.GetSection("abc") - assert.NoError(t, err) + require.NoError(t, err) + raw := abc.KeysHash() + assert.Equal(t, "https://new-host", raw["host"]) +} + +func TestSaveToProfile_ClearKeysOnNonExistentKeyIsNoop(t *testing.T) { + ctx := context.Background() + path := filepath.Join(t.TempDir(), "databrickscfg") + + err := SaveToProfile(ctx, &config.Config{ + ConfigFile: path, + Profile: "abc", + Host: "https://foo", + }) + require.NoError(t, err) + + // Clear a key that doesn't exist — should not error. + err = SaveToProfile(ctx, &config.Config{ + ConfigFile: path, + Profile: "abc", + Host: "https://foo", + AuthType: "databricks-cli", + }, "token", "nonexistent_key") + require.NoError(t, err) + + file, err := loadOrCreateConfigFile(path) + require.NoError(t, err) + + abc, err := file.GetSection("abc") + require.NoError(t, err) raw := abc.KeysHash() assert.Len(t, raw, 2) assert.Equal(t, "https://foo", raw["host"]) diff --git a/libs/databrickscfg/profile/file.go b/libs/databrickscfg/profile/file.go index b078e67d3a..3d062e11af 100644 --- a/libs/databrickscfg/profile/file.go +++ b/libs/databrickscfg/profile/file.go @@ -87,6 +87,7 @@ func (f FileProfilerImpl) LoadProfiles(ctx context.Context, fn ProfileMatchFunct ClusterID: all["cluster_id"], ServerlessComputeID: all["serverless_compute_id"], HasClientCredentials: all["client_id"] != "" && all["client_secret"] != "", + Scopes: all["scopes"], } if fn(profile) { profiles = append(profiles, profile) diff --git a/libs/databrickscfg/profile/profile.go b/libs/databrickscfg/profile/profile.go index 9cdf24f82b..7bf8784ad2 100644 --- a/libs/databrickscfg/profile/profile.go +++ b/libs/databrickscfg/profile/profile.go @@ -18,6 +18,7 @@ type Profile struct { ClusterID string ServerlessComputeID string HasClientCredentials bool + Scopes string } func (p Profile) Cloud() string { From 22e37288c35b144b79028b3602103cdf75b9e641 Mon Sep 17 00:00:00 2001 From: simon Date: Thu, 26 Feb 2026 16:35:58 +0100 Subject: [PATCH 2/3] Address review comments: dynamic auth keys, table tests, acceptance tests - Derive auth credential clear-keys from config.ConfigAttributes instead of hardcoding them, so new SDK auth fields are picked up automatically. - Convert SaveToProfile unit tests to table-driven format. - Replace configure unit tests with acceptance tests under acceptance/cmd/configure/. --- .../clears-oauth-on-pat/out.databrickscfg | 5 + .../clears-oauth-on-pat/out.test.toml | 5 + .../configure/clears-oauth-on-pat/output.txt | 19 ++ .../cmd/configure/clears-oauth-on-pat/script | 24 ++ .../configure/clears-oauth-on-pat/test.toml | 3 + .../out.databrickscfg | 4 + .../out.test.toml | 5 + .../output.txt | 14 + .../script | 22 ++ .../test.toml | 3 + cmd/auth/login.go | 36 +-- cmd/configure/configure.go | 38 +-- cmd/configure/configure_test.go | 128 --------- libs/databrickscfg/ops.go | 14 + libs/databrickscfg/ops_test.go | 248 +++++++----------- 15 files changed, 223 insertions(+), 345 deletions(-) create mode 100644 acceptance/cmd/configure/clears-oauth-on-pat/out.databrickscfg create mode 100644 acceptance/cmd/configure/clears-oauth-on-pat/out.test.toml create mode 100644 acceptance/cmd/configure/clears-oauth-on-pat/output.txt create mode 100644 acceptance/cmd/configure/clears-oauth-on-pat/script create mode 100644 acceptance/cmd/configure/clears-oauth-on-pat/test.toml create mode 100644 acceptance/cmd/configure/clears-serverless-when-cluster-from-env/out.databrickscfg create mode 100644 acceptance/cmd/configure/clears-serverless-when-cluster-from-env/out.test.toml create mode 100644 acceptance/cmd/configure/clears-serverless-when-cluster-from-env/output.txt create mode 100644 acceptance/cmd/configure/clears-serverless-when-cluster-from-env/script create mode 100644 acceptance/cmd/configure/clears-serverless-when-cluster-from-env/test.toml diff --git a/acceptance/cmd/configure/clears-oauth-on-pat/out.databrickscfg b/acceptance/cmd/configure/clears-oauth-on-pat/out.databrickscfg new file mode 100644 index 0000000000..e6618735de --- /dev/null +++ b/acceptance/cmd/configure/clears-oauth-on-pat/out.databrickscfg @@ -0,0 +1,5 @@ +[DEFAULT] +host = https://host +account_id = acc-123 +workspace_id = ws-456 +token = [DATABRICKS_TOKEN] diff --git a/acceptance/cmd/configure/clears-oauth-on-pat/out.test.toml b/acceptance/cmd/configure/clears-oauth-on-pat/out.test.toml new file mode 100644 index 0000000000..d560f1de04 --- /dev/null +++ b/acceptance/cmd/configure/clears-oauth-on-pat/out.test.toml @@ -0,0 +1,5 @@ +Local = true +Cloud = false + +[EnvMatrix] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/cmd/configure/clears-oauth-on-pat/output.txt b/acceptance/cmd/configure/clears-oauth-on-pat/output.txt new file mode 100644 index 0000000000..6142be378e --- /dev/null +++ b/acceptance/cmd/configure/clears-oauth-on-pat/output.txt @@ -0,0 +1,19 @@ + +=== Initial profile (OAuth with unified host) +[DEFAULT] +host = https://host +auth_type = databricks-cli +scopes = all-apis +experimental_is_unified_host = true +account_id = acc-123 +workspace_id = ws-456 + +=== Run configure with PAT token +>>> [CLI] configure --token --host https://host + +=== Profile after PAT configure +[DEFAULT] +host = https://host +account_id = acc-123 +workspace_id = ws-456 +token = [DATABRICKS_TOKEN] diff --git a/acceptance/cmd/configure/clears-oauth-on-pat/script b/acceptance/cmd/configure/clears-oauth-on-pat/script new file mode 100644 index 0000000000..587e0d0a55 --- /dev/null +++ b/acceptance/cmd/configure/clears-oauth-on-pat/script @@ -0,0 +1,24 @@ +sethome "./home" + +# Pre-populate a profile with OAuth metadata and unified host fields +# (as if `auth login` was previously run against a unified host). +cat > "./home/.databrickscfg" <>> [CLI] configure --token --host https://host + +=== Profile after configure (serverless should be cleared) +[DEFAULT] +host = https://host +cluster_id = env-cluster-789 +token = [DATABRICKS_TOKEN] diff --git a/acceptance/cmd/configure/clears-serverless-when-cluster-from-env/script b/acceptance/cmd/configure/clears-serverless-when-cluster-from-env/script new file mode 100644 index 0000000000..b9d8573f78 --- /dev/null +++ b/acceptance/cmd/configure/clears-serverless-when-cluster-from-env/script @@ -0,0 +1,22 @@ +sethome "./home" + +# Pre-populate a profile with serverless_compute_id. +cat > "./home/.databrickscfg" < Date: Fri, 27 Feb 2026 13:10:22 +0100 Subject: [PATCH 3/3] Remove unnecessary out.databrickscfg files from acceptance tests --- .../cmd/auth/login/preserve-fields/out.databrickscfg | 7 ------- acceptance/cmd/auth/login/preserve-fields/script | 3 --- .../cmd/configure/clears-oauth-on-pat/out.databrickscfg | 5 ----- acceptance/cmd/configure/clears-oauth-on-pat/script | 2 -- .../out.databrickscfg | 4 ---- .../clears-serverless-when-cluster-from-env/script | 2 -- 6 files changed, 23 deletions(-) delete mode 100644 acceptance/cmd/auth/login/preserve-fields/out.databrickscfg delete mode 100644 acceptance/cmd/configure/clears-oauth-on-pat/out.databrickscfg delete mode 100644 acceptance/cmd/configure/clears-serverless-when-cluster-from-env/out.databrickscfg diff --git a/acceptance/cmd/auth/login/preserve-fields/out.databrickscfg b/acceptance/cmd/auth/login/preserve-fields/out.databrickscfg deleted file mode 100644 index 3658c4d33b..0000000000 --- a/acceptance/cmd/auth/login/preserve-fields/out.databrickscfg +++ /dev/null @@ -1,7 +0,0 @@ -[DEFAULT] -host = [DATABRICKS_URL] -cluster_id = existing-cluster-123 -warehouse_id = warehouse-456 -azure_environment = USGOVERNMENT -custom_key = my-custom-value -auth_type = databricks-cli diff --git a/acceptance/cmd/auth/login/preserve-fields/script b/acceptance/cmd/auth/login/preserve-fields/script index a82101c892..6b34f7db60 100644 --- a/acceptance/cmd/auth/login/preserve-fields/script +++ b/acceptance/cmd/auth/login/preserve-fields/script @@ -23,6 +23,3 @@ trace $CLI auth login --host $DATABRICKS_HOST --profile DEFAULT title "Profile after auth login — all non-auth fields should be preserved\n" cat "./home/.databrickscfg" - -# Track the .databrickscfg file that was created to surface changes. -mv "./home/.databrickscfg" "./out.databrickscfg" diff --git a/acceptance/cmd/configure/clears-oauth-on-pat/out.databrickscfg b/acceptance/cmd/configure/clears-oauth-on-pat/out.databrickscfg deleted file mode 100644 index e6618735de..0000000000 --- a/acceptance/cmd/configure/clears-oauth-on-pat/out.databrickscfg +++ /dev/null @@ -1,5 +0,0 @@ -[DEFAULT] -host = https://host -account_id = acc-123 -workspace_id = ws-456 -token = [DATABRICKS_TOKEN] diff --git a/acceptance/cmd/configure/clears-oauth-on-pat/script b/acceptance/cmd/configure/clears-oauth-on-pat/script index 587e0d0a55..30f7b7fed1 100644 --- a/acceptance/cmd/configure/clears-oauth-on-pat/script +++ b/acceptance/cmd/configure/clears-oauth-on-pat/script @@ -20,5 +20,3 @@ echo "new-token" | trace $CLI configure --token --host https://host title "Profile after PAT configure\n" cat "./home/.databrickscfg" - -mv "./home/.databrickscfg" "./out.databrickscfg" diff --git a/acceptance/cmd/configure/clears-serverless-when-cluster-from-env/out.databrickscfg b/acceptance/cmd/configure/clears-serverless-when-cluster-from-env/out.databrickscfg deleted file mode 100644 index 1fb91390b4..0000000000 --- a/acceptance/cmd/configure/clears-serverless-when-cluster-from-env/out.databrickscfg +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] -host = https://host -cluster_id = env-cluster-789 -token = [DATABRICKS_TOKEN] diff --git a/acceptance/cmd/configure/clears-serverless-when-cluster-from-env/script b/acceptance/cmd/configure/clears-serverless-when-cluster-from-env/script index b9d8573f78..2e0482d39f 100644 --- a/acceptance/cmd/configure/clears-serverless-when-cluster-from-env/script +++ b/acceptance/cmd/configure/clears-serverless-when-cluster-from-env/script @@ -18,5 +18,3 @@ echo "new-token" | trace $CLI configure --token --host https://host title "Profile after configure (serverless should be cleared)\n" cat "./home/.databrickscfg" - -mv "./home/.databrickscfg" "./out.databrickscfg"