diff --git a/acceptance/localenv/bundle-multiple-defaults/databricks.yml b/acceptance/localenv/bundle-multiple-defaults/databricks.yml new file mode 100644 index 0000000000..46757827b5 --- /dev/null +++ b/acceptance/localenv/bundle-multiple-defaults/databricks.yml @@ -0,0 +1,11 @@ +bundle: + name: multiple-defaults + +# Two targets both marked default is an invalid bundle. setup-local consults the +# bundle only as an optional source of cluster_id, so this error must not surface; +# the command should still reach its own E_NO_TARGET. +targets: + dev: + default: true + dev2: + default: true diff --git a/acceptance/localenv/bundle-multiple-defaults/out.test.toml b/acceptance/localenv/bundle-multiple-defaults/out.test.toml new file mode 100644 index 0000000000..0938e67898 --- /dev/null +++ b/acceptance/localenv/bundle-multiple-defaults/out.test.toml @@ -0,0 +1,2 @@ +Cloud = false +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] diff --git a/acceptance/localenv/bundle-multiple-defaults/output.txt b/acceptance/localenv/bundle-multiple-defaults/output.txt new file mode 100644 index 0000000000..eba9aad391 --- /dev/null +++ b/acceptance/localenv/bundle-multiple-defaults/output.txt @@ -0,0 +1,7 @@ +preflight ok uv [UV_VERSION] +resolve error No compute target is selected. Select a cluster or serverless target, or pass --cluster-id / --cluster-name / --serverless-version / --job-task +fetch pending +merge pending +provision pending +validate pending +For more detail, re-run with --debug, or --output json to share a structured report. diff --git a/acceptance/localenv/bundle-multiple-defaults/script b/acceptance/localenv/bundle-multiple-defaults/script new file mode 100644 index 0000000000..712272f0e5 --- /dev/null +++ b/acceptance/localenv/bundle-multiple-defaults/script @@ -0,0 +1 @@ +musterr $CLI environments setup-local diff --git a/acceptance/localenv/bundle-multiple-defaults/test.toml b/acceptance/localenv/bundle-multiple-defaults/test.toml new file mode 100644 index 0000000000..270ce8c5b7 --- /dev/null +++ b/acceptance/localenv/bundle-multiple-defaults/test.toml @@ -0,0 +1,5 @@ +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] + +[[Repls]] +Old = 'uv uv \S+(?: \([^)]+\))?' +New = 'uv [UV_VERSION]' diff --git a/cmd/environments/sync.go b/cmd/environments/sync.go index 35256c8d14..2adc00eaa8 100644 --- a/cmd/environments/sync.go +++ b/cmd/environments/sync.go @@ -10,6 +10,8 @@ import ( "github.com/databricks/cli/cmd/root" "github.com/databricks/cli/libs/cmdctx" libslocalenv "github.com/databricks/cli/libs/localenv" + "github.com/databricks/cli/libs/log" + "github.com/databricks/cli/libs/logdiag" "github.com/spf13/cobra" ) @@ -32,7 +34,15 @@ env-owned sections are refreshed, user-owned content is preserved).`, // The target is selected via flags; reject stray positional args rather than // silently ignoring them. cmd.Args = cobra.NoArgs - cmd.PreRunE = root.MustWorkspaceClient + // This command resolves its own compute target and only consults the bundle as + // an optional source of bundle.cluster_id (see bundleTarget). Skip bundle-based + // auth configuration in the shared PreRunE so a malformed databricks.yml (e.g. + // two targets marked default) can't fail the command before it runs; the fallback + // bundle read in bundleTarget swallows such errors and falls through to E_NO_TARGET. + cmd.PreRunE = func(cmd *cobra.Command, args []string) error { + cmd.SetContext(root.SkipLoadBundle(cmd.Context())) + return root.MustWorkspaceClient(cmd, args) + } addComputeFlags(cmd) cmd.RunE = func(cmd *cobra.Command, args []string) error { return runPipeline(cmd) @@ -175,7 +185,24 @@ func runPipeline(cmd *cobra.Command) error { // // TODO: extend once bundle config exposes a serverless field at the bundle level. func bundleTarget(cmd *cobra.Command) libslocalenv.BundleTarget { + // Load the bundle in an isolated diagnostics context: the bundle is only an + // optional source of cluster_id here, so a malformed databricks.yml must not + // surface as a fatal command error. Any load error is logged for debugging and + // treated as "no bundle target", so the pipeline falls through to E_NO_TARGET + // (which tells the user to pass an explicit --cluster-id/--serverless-version/etc). + orig := cmd.Context() + ctx := logdiag.IsolatedContext(orig) + // Collect (buffer) diagnostics instead of rendering them: an isolated context + // still prints each diagnostic to stderr unless collection is on, and we want a + // bundle load error to be silent (debug-logged) on this optional fallback path. + logdiag.SetCollect(ctx, true) + cmd.SetContext(ctx) + defer cmd.SetContext(orig) b := root.TryConfigureBundle(cmd) + if logdiag.HasError(ctx) { + log.Debugf(ctx, "ignoring bundle for cluster_id fallback: %s", logdiag.GetFirstErrorSummary(ctx)) + return libslocalenv.BundleTarget{Selected: false} + } if b == nil { return libslocalenv.BundleTarget{Selected: false} }