diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index f26e1ea1afc..09c99068f8b 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -6,6 +6,7 @@ ### CLI * Show a once-per-day notice after a command when a newer CLI release is available, with a link to the release and the upgrade command for the detected install method. Suppressed for non-interactive/CI runs, JSON output, the Databricks Runtime, and development builds, and can be disabled with `DATABRICKS_CLI_DISABLE_UPDATE_CHECK` ([#5470](https://github.com/databricks/cli/pull/5470)). +* `databricks auth login --skip-workspace` (without `--host`) now lands the user on the account selector at `login.databricks.com` instead of the workspace selector, skipping a step that is wasted for account-only logins. ### Bundles * Remove API enum values and types that are still in development from the `databricks-bundles` Python package; these were never accepted by the backend ([#5484](https://github.com/databricks/cli/pull/5484)). diff --git a/cmd/auth/login.go b/cmd/auth/login.go index f9f2531ac74..f4b68929c5c 100644 --- a/cmd/auth/login.go +++ b/cmd/auth/login.go @@ -269,6 +269,7 @@ a new profile is created. profileName: profileName, timeout: loginTimeout, scopes: scopes, + skipWorkspace: skipWorkspace, existingProfile: existingProfile, browserFunc: getBrowserFunc(cmd), tokenStore: tokenStore, @@ -635,6 +636,7 @@ type discoveryLoginInputs struct { profileName string timeout time.Duration scopes string + skipWorkspace bool existingProfile *profile.Profile browserFunc func(string) error tokenStore storage.Store @@ -664,6 +666,12 @@ func discoveryLogin(ctx context.Context, in discoveryLoginInputs) error { if len(scopesList) > 0 { opts = append(opts, u2m.WithScopes(scopesList)) } + // --skip-workspace lands the user on the account selector at + // login.databricks.com instead of the workspace selector, which is a + // wasted step for account-only logins. + if in.skipWorkspace { + opts = append(opts, u2m.WithDiscoveryAccountTarget()) + } discoveryHost := env.Get(ctx, discoveryHostEnvVar) if discoveryHost != "" { opts = append(opts, u2m.WithDiscoveryHost(discoveryHost)) diff --git a/cmd/auth/login_test.go b/cmd/auth/login_test.go index a8eafb4be43..732be4d7e91 100644 --- a/cmd/auth/login_test.go +++ b/cmd/auth/login_test.go @@ -87,8 +87,9 @@ type fakeDiscoveryClient struct { introspection *auth.IntrospectionResult introspectionErr error // For assertions - introspectHost string - introspectToken string + introspectHost string + introspectToken string + persistentAuthOpts []u2m.PersistentAuthOption } func (f *fakeDiscoveryClient) NewOAuthArgument(profileName string) (*u2m.BasicDiscoveryOAuthArgument, error) { @@ -99,6 +100,7 @@ func (f *fakeDiscoveryClient) NewOAuthArgument(profileName string) (*u2m.BasicDi } func (f *fakeDiscoveryClient) NewPersistentAuth(ctx context.Context, opts ...u2m.PersistentAuthOption) (discoveryPersistentAuth, error) { + f.persistentAuthOpts = opts if f.persistentAuthErr != nil { return nil, f.persistentAuthErr } @@ -1259,6 +1261,52 @@ func TestDiscoveryLogin_OverridesHostFromEnv(t *testing.T) { assert.NotContains(t, stderr.String(), "Opening login.databricks.com in your browser...") } +func TestDiscoveryLogin_SkipWorkspaceAddsAccountTargetOption(t *testing.T) { + tmpDir := t.TempDir() + configPath := filepath.Join(tmpDir, ".databrickscfg") + require.NoError(t, os.WriteFile(configPath, []byte(""), 0o600)) + t.Setenv("DATABRICKS_CONFIG_FILE", configPath) + + newDiscoveryClient := func(t *testing.T) *fakeDiscoveryClient { + oauthArg, err := u2m.NewBasicDiscoveryOAuthArgument("DISCOVERY") + require.NoError(t, err) + oauthArg.SetDiscoveredHost("https://workspace.example.com") + return &fakeDiscoveryClient{ + oauthArg: oauthArg, + persistentAuth: &fakeDiscoveryPersistentAuth{ + token: &oauth2.Token{AccessToken: "test-token"}, + }, + introspectionErr: errors.New("not asserted here"), + } + } + + ctx, _ := cmdio.NewTestContextWithStdout(t.Context()) + + // Baseline: no --skip-workspace. + baselineDC := newDiscoveryClient(t) + require.NoError(t, discoveryLogin(ctx, discoveryLoginInputs{ + dc: baselineDC, + profileName: "DISCOVERY", + timeout: time.Second, + browserFunc: func(string) error { return nil }, + tokenStore: newTestStore(), + })) + + // With --skip-workspace: expect exactly one extra option. + skipDC := newDiscoveryClient(t) + require.NoError(t, discoveryLogin(ctx, discoveryLoginInputs{ + dc: skipDC, + profileName: "DISCOVERY", + timeout: time.Second, + skipWorkspace: true, + browserFunc: func(string) error { return nil }, + tokenStore: newTestStore(), + })) + + assert.Len(t, skipDC.persistentAuthOpts, len(baselineDC.persistentAuthOpts)+1, + "--skip-workspace should add exactly one extra PersistentAuthOption (WithDiscoveryAccountTarget)") +} + func TestLoginRejectsPositionalArgWithHostFlag(t *testing.T) { ctx := cmdio.MockDiscard(t.Context()) authArgs := &auth.AuthArguments{Host: "https://example.com"}