@@ -14,50 +14,31 @@ import (
1414
1515var ResolveProfileFromHost = profileFromHostLoader {}
1616
17- // ResolveNonAuthFromEnv reads configuration from environment variables, except
18- // for the host and any authentication credential. It runs before the config
19- // file loader when the user has explicitly selected a profile (via the
20- // --profile flag or workspace.profile), so that the profile takes precedence
21- // over auth environment variables.
22- //
23- // The SDK's default loader order is environment first, then config file, and a
24- // loader never overwrites a field that is already set. As a result auth env
25- // vars (DATABRICKS_HOST, DATABRICKS_TOKEN, ...) shadow the selected profile.
26- // Skipping them here lets the subsequent config-file loader populate host and
27- // auth from the profile instead. A trailing config.ConfigAttributes loader can
28- // still fill auth fields the profile leaves empty (e.g. a host-only profile
29- // combined with DATABRICKS_TOKEN). See
30- // https://github.com/databricks/cli/issues/5096.
17+ // ResolveNonAuthFromEnv reads config from environment variables, except for the
18+ // host and any auth credential. It runs before the config file loader when a
19+ // profile is explicitly selected, so the profile wins over auth env vars: the
20+ // SDK's default chain reads env before the config file and never overwrites a
21+ // set field, which would otherwise let env vars shadow the profile (#5096).
3122var ResolveNonAuthFromEnv = nonAuthEnvLoader {}
3223
33- // ProfileAuthLoaders is the SDK loader chain to use when the user has
34- // explicitly selected a profile (via the --profile flag or a bundle's
35- // workspace.profile). It is the single source of truth for that precedence
36- // rule; call sites should reference it rather than restating the rationale.
37- //
38- // The selected profile must determine the host, routing identifiers, and
39- // authentication, taking precedence over the matching environment variables
40- // (DATABRICKS_HOST, DATABRICKS_TOKEN, ...). The SDK's default chain reads the
41- // environment before the config file and never overwrites an already-set
42- // field, so the env vars would otherwise shadow the profile (issue #5096).
24+ // ProfileAuthLoaders is the loader chain to use when a profile is explicitly
25+ // selected (--profile or a bundle's workspace.profile), and the single source
26+ // of truth for that precedence rule. The profile must win for host, routing and
27+ // auth over the matching env vars (DATABRICKS_HOST, DATABRICKS_TOKEN, ...),
28+ // which the SDK's default env-first chain would otherwise let shadow it (#5096).
4329//
44- // Note: this intentionally only governs an explicitly selected profile. A
45- // profile picked up from DATABRICKS_CONFIG_PROFILE keeps the SDK's default
46- // env-first precedence, because reordering precedence between two environment
47- // signals (DATABRICKS_CONFIG_PROFILE vs DATABRICKS_HOST) is the SDK's domain;
48- // we only override when the profile is named out-of-band (the --profile flag
49- // or a bundle's workspace.profile).
30+ // This only governs an explicitly selected profile. One picked up from
31+ // DATABRICKS_CONFIG_PROFILE keeps env-first precedence: reordering two
32+ // environment signals (DATABRICKS_CONFIG_PROFILE vs DATABRICKS_HOST) is the
33+ // SDK's domain; we only override when the profile is named out-of-band.
5034//
51- // The order matters:
52- // 1. ResolveNonAuthFromEnv loads non-auth, non-routing attributes from the
53- // environment (e.g. cluster_id), preserving env-wins precedence for those.
54- // 2. ConfigFile loads the selected profile, populating host, routing and auth.
55- // 3. ConfigAttributes loads any remaining attributes from the environment,
56- // filling fields the profile did not provide (e.g. a host-only profile
57- // combined with DATABRICKS_TOKEN). It never overwrites a value the profile
58- // already set, so the profile still wins for #5096. This gap-fill is a
59- // deliberate, tested contract (host-only profiles are a common CI pattern
60- // where the credential is injected via the environment).
35+ // Order:
36+ // 1. ResolveNonAuthFromEnv: non-auth, non-routing env attrs (e.g. cluster_id),
37+ // keeping env-wins precedence for those.
38+ // 2. ConfigFile: the selected profile (host, routing, auth).
39+ // 3. ConfigAttributes: gap-fills only fields the profile left empty (e.g. a
40+ // host-only profile + DATABRICKS_TOKEN, a common CI pattern); it never
41+ // overwrites a profile value, so the profile still wins.
6142var ProfileAuthLoaders = []config.Loader {
6243 ResolveNonAuthFromEnv ,
6344 config .ConfigFile ,
@@ -110,38 +91,25 @@ func findMatchingProfile(configFile *config.File, matcher func(*ini.Section) boo
11091 return matching [0 ], nil
11192}
11293
113- // nonAuthEnvSkipAttrs lists SDK config attribute names that nonAuthEnvLoader
114- // must not read from the environment, beyond those caught by HasAuthAttribute.
115- //
116- // Criterion: an attribute belongs here if it identifies the target
117- // workspace/account (host, routing IDs) or selects/steers the authentication
118- // method, but the SDK does NOT tag it `auth:"..."` (so HasAuthAttribute can't
119- // catch it). The SDK collapses an `auth:"-"` tag to an empty auth tag (marking
120- // the field "internal"), which is why these auth-steering fields slip past
121- // HasAuthAttribute and must be listed explicitly. Leaving any of them to the
122- // environment would let the matching env var shadow the selected profile, the
123- // same bug as #5096. Skipping here only changes precedence: the trailing
124- // ConfigAttributes loader still fills any of these the profile leaves empty
125- // from the environment (the same gap-fill host and credentials get).
94+ // nonAuthEnvSkipAttrs lists SDK config attributes nonAuthEnvLoader must not read
95+ // from the environment, beyond those caught by HasAuthAttribute. They identify
96+ // the target workspace/account (host, routing IDs) or steer the auth method but
97+ // are tagged auth:"-" (collapsed to Internal), so HasAuthAttribute misses them;
98+ // leaving them to env would let an env var shadow the selected profile (#5096).
99+ // Skipping only changes precedence: the trailing ConfigAttributes loader still
100+ // gap-fills any the profile leaves empty.
126101//
127- // - host: has no `auth` struct tag at all.
128- // - workspace_id (DATABRICKS_WORKSPACE_ID): routing identifier; a profile
129- // that sets it must win, or a stray env var routes the profile's
130- // credentials to a different workspace.
131- // - account_id (DATABRICKS_ACCOUNT_ID): account routing identifier, same
132- // reasoning as workspace_id.
133- // - auth_type (DATABRICKS_AUTH_TYPE): forces a specific auth method.
134- // - discovery_url (DATABRICKS_DISCOVERY_URL): redirects OIDC discovery.
135- // - audience (DATABRICKS_TOKEN_AUDIENCE): selects the token audience for
136- // OIDC/workload-identity flows.
137- // - cloud (DATABRICKS_CLOUD): steers cloud-specific auth (Azure/GCP/AWS).
102+ // - host: no `auth` tag at all.
103+ // - workspace_id / account_id: routing identifiers; an env var must not route
104+ // the profile's credentials elsewhere.
105+ // - auth_type: forces a specific auth method.
106+ // - discovery_url: redirects OIDC discovery.
107+ // - audience: selects the OIDC/workload-identity token audience.
108+ // - cloud: steers cloud-specific auth (Azure/GCP/AWS).
138109//
139- // Non-auth env-backed attributes tagged `auth:"-"` (e.g. oauth_callback_port,
140- // debug_headers, rate_limit) are intentionally NOT skipped: they don't change
141- // which credentials authenticate the request or where it is routed, so
142- // env-wins precedence is fine. TestNonAuthEnvSkipAttrsCoverSDKInternalEnvAttrs
143- // guards that every auth-steering internal attribute stays classified across
144- // SDK bumps.
110+ // Non-auth attrs tagged auth:"-" (oauth_callback_port, debug_headers, ...) are
111+ // intentionally not skipped; TestNonAuthEnvSkipAttrsCoverSDKInternalEnvAttrs
112+ // guards that every auth-steering internal attribute stays classified.
145113var nonAuthEnvSkipAttrs = map [string ]bool {
146114 "host" : true ,
147115 "workspace_id" : true ,
0 commit comments