Skip to content

Add support for default_profile in [__settings__] section#1534

Open
simonfaltum wants to merge 7 commits intomainfrom
default-profile
Open

Add support for default_profile in [__settings__] section#1534
simonfaltum wants to merge 7 commits intomainfrom
default-profile

Conversation

@simonfaltum
Copy link
Member

Changes

When no profile is explicitly configured, the SDK now checks for a
[__settings__].default_profile key in .databrickscfg before falling
back to the DEFAULT section. This aligns the SDK with the CLI's
databricks auth switch command, which writes the user's chosen
default profile to this section.

Profile resolution order:

  1. Explicit profile (--profile flag, env var, or programmatic setting)
  2. [__settings__].default_profile (new)
  3. [DEFAULT] section (legacy fallback)

The [__settings__] section is never treated as a profile.

Backwards compatible: existing configs without [__settings__] behave identically.

Tests

Seven test scenarios covering:

  • default_profile resolves correctly
  • default_profile takes precedence over [DEFAULT]
  • Legacy fallback when no [__settings__]
  • Legacy fallback when default_profile is empty
  • [__settings__] is not a profile
  • Explicit --profile overrides default_profile
  • default_profile pointing to nonexistent section returns error

When no profile is explicitly configured, the SDK now checks for a
[__settings__].default_profile key in .databrickscfg before falling
back to the DEFAULT section. This aligns the SDK with the CLI's
'databricks auth switch' command, which writes the user's chosen
default profile to this section.

Profile resolution order:
1. Explicit profile (--profile flag, env var, or programmatic)
2. [__settings__].default_profile (new)
3. [DEFAULT] section (legacy fallback)

The [__settings__] section is never treated as a profile.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: simon <simon.faltum@databricks.com>
Signed-off-by: simon <simon.faltum@databricks.com>
- Reject default_profile = __settings__ (self-reference falls through
  to DEFAULT instead of trying to load __settings__ as a profile)
- Add test for the self-reference guard with dedicated fixture
- Differentiate TestConfigFile_DefaultProfileResolves from the
  precedence test by using a fixture without a [DEFAULT] section
- Strengthen TestConfigFile_SettingsSectionNotAProfile to verify
  __settings__ is present in SectionStrings (documenting that callers
  must filter it) and that resolveDefaultProfile still rejects it
Persist the resolved profile name and reject the reserved settings section as a profile target so OAuth cache keys and CLI auth keep using the selected profile.
@simonfaltum simonfaltum marked this pull request as ready for review March 11, 2026 16:23
@github-actions
Copy link

If integration tests don't run automatically, an authorized user can run them manually by following the instructions below:

Trigger:
go/deco-tests-run/sdk-go

Inputs:

  • PR number: 1534
  • Commit SHA: 53fb991fabe0b437b79653f54cb86ffb6c91ec2e

Checks will be approved automatically on success.

@simonfaltum simonfaltum deployed to test-trigger-is March 15, 2026 14:45 — with GitHub Actions Active
@renaudhartert-db
Copy link
Contributor

I think the logic in Configure is getting a bit hard to follow with the two booleans (hasExplicitProfile, hasDefaultProfileSetting). The condition !hasExplicitProfile && !hasDefaultProfileSetting is really encoding a three-way state: explicit, from settings, or default fallback.

Also, unless I'm missing something, the branch where profile == settingsSection and both booleans are false is unreachable -- resolveDefaultProfile only returns "__settings__" with hasDefaultProfileSetting = true, and the fallback is always "DEFAULT". So the profile == settingsSection guard and the !hasExplicitProfile && !hasDefaultProfileSetting check can't interact in practice, but it takes a moment to convince yourself of that.

What would you think of introducing a small typed enum for the profile source, and moving all the resolution logic into a single function?

type profileSource int

const (
    profileSourceDefault  profileSource = iota // legacy [DEFAULT] fallback
    profileSourceSettings                      // from [__settings__].default_profile
    profileSourceExplicit                      // --profile flag / env / programmatic
)

func resolveProfile(cfg *Config, f *File) (string, profileSource) {
    if cfg.Profile != "" {
        return cfg.Profile, profileSourceExplicit
    }
    section, err := f.GetSection(settingsSection)
    if err == nil {
        key, err := section.GetKey("default_profile")
        if err == nil {
            v := strings.TrimSpace(key.String())
            if v != "" {
                return v, profileSourceSettings
            }
        }
    }
    return "DEFAULT", profileSourceDefault
}

Then Configure doesn't need to care about how the profile was resolved:

profile, source := resolveProfile(cfg, configFile)

if profile == settingsSection {
    return fmt.Errorf("%s has no %s profile configured", configFile.Path(), profile)
}

profileValues := configFile.Section(profile)
if len(profileValues.Keys()) == 0 {
    if source == profileSourceDefault {
        logger.Debugf(context.Background(), "%s has no %s profile configured", configFile.Path(), profile)
        return nil
    }
    return fmt.Errorf("%s has no %s profile configured", configFile.Path(), profile)
}

err := cfg.loadProfileFromSection(profileValues)
if err != nil {
    return fmt.Errorf("%s %s profile: %w", configFile.Path(), profile, err)
}
if source == profileSourceSettings {
    cfg.Profile = profile
}
return nil

source == profileSourceDefault reads more naturally than !hasExplicitProfile && !hasDefaultProfileSetting, and adding a fourth source in the future wouldn't require another boolean.

Sign up for free to 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.

2 participants