From a8453f9b89da0fe63d7215d396241ee0c2f090b1 Mon Sep 17 00:00:00 2001 From: "@rugpanov" Date: Mon, 7 Sep 2026 10:41:09 +0200 Subject: [PATCH 1/2] Migrate setup-local invocation to orthogonal --no-constraints/--no-dbconnect flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit *Why* The `setup-local` CLI has replaced the single behavioral flag `--constraints-only` with two orthogonal negative flags — `--no-constraints` (skip writing the remote Python-version/dependency pins) and `--no-dbconnect` (skip adding databricks-connect). The extension's `SetupLocalInvocation` still modeled the old single `mode` enum and only knew how to emit `--constraints-only`. Migrating now lets a later setup-options picker map its tiers directly onto real, composable capabilities. *What* - Replace `SetupLocalInvocation.mode` with two orthogonal optional booleans, `skipConstraints` → `--no-constraints` and `skipDbconnect` → `--no-dbconnect`; drop the `--constraints-only` emission. `buildSetupLocalArgs` stays pure and deterministic (fixed flag order). - Update the two call sites (the real setup run and the drift dry-run) to the flag-free default; derive the attempt telemetry's `mode` dimension from `skipDbconnect` so that event is unchanged. - Rework the arg unit tests around the new flag mapping (each flag alone, both together, default emits neither, and never `--constraints-only`). No behavior change: both callers run the default (Full) setup, which emits no behavioral flags, exactly as before. The flags become reachable when the picker is introduced. The bundled `cli.version` bump lands separately once a CLI release carrying the new flags is available. *Verification* - `tsc --noEmit`: clean. - Unit suite: `buildSetupLocalArgs`, `PythonSetupCliClient`, and `PythonSetupEnvironmentSetup` (setup + telemetry) all green (1061 passing); remaining local failures are unrelated environment artifacts (stale bundled CLI binary version pin). - eslint + prettier: clean on all changed files. Co-authored-by: Isaac --- packages/databricks-vscode/src/extension.ts | 1 - .../PythonSetupEnvironmentSetup.ts | 7 ++- .../gateways/PythonSetupCliClient.test.ts | 1 - .../python-setup/utils/setupLocalArgs.test.ts | 51 ++++++++++++++----- .../src/python-setup/utils/setupLocalArgs.ts | 32 ++++++++---- 5 files changed, 66 insertions(+), 26 deletions(-) diff --git a/packages/databricks-vscode/src/extension.ts b/packages/databricks-vscode/src/extension.ts index 3b192da57..3d50a5b12 100644 --- a/packages/databricks-vscode/src/extension.ts +++ b/packages/databricks-vscode/src/extension.ts @@ -1170,7 +1170,6 @@ export async function activate( try { const result = await pythonSetupClient.run( { - mode: "default", dryRun: true, compute: resolution.compute, }, diff --git a/packages/databricks-vscode/src/python-setup/controllers/PythonSetupEnvironmentSetup.ts b/packages/databricks-vscode/src/python-setup/controllers/PythonSetupEnvironmentSetup.ts index 3718e8657..0d3dff3a8 100644 --- a/packages/databricks-vscode/src/python-setup/controllers/PythonSetupEnvironmentSetup.ts +++ b/packages/databricks-vscode/src/python-setup/controllers/PythonSetupEnvironmentSetup.ts @@ -369,7 +369,6 @@ export class PythonSetupEnvironmentSetup implements Disposable { const compute = resolved.compute; const invocation: SetupLocalInvocation = { - mode: "default", compute, }; @@ -635,7 +634,11 @@ export class PythonSetupEnvironmentSetup implements Disposable { targetType: compute.kind, serverlessVersion: compute.kind === "serverless" ? compute.version : undefined, - mode: invocation.mode, + // The attempt event keeps its default/constraints-only mode + // dimension; derive it from the orthogonal flag that drops + // databricks-connect (the CLI treats --no-dbconnect and the old + // --constraints-only as the same mode). + mode: invocation.skipDbconnect ? "constraints-only" : "default", isGreenfield, // A run against a project already marked ready this session is a // re-run (the ready row's Re-run button / row click); anything diff --git a/packages/databricks-vscode/src/python-setup/gateways/PythonSetupCliClient.test.ts b/packages/databricks-vscode/src/python-setup/gateways/PythonSetupCliClient.test.ts index ddaf8e477..03cc3d166 100644 --- a/packages/databricks-vscode/src/python-setup/gateways/PythonSetupCliClient.test.ts +++ b/packages/databricks-vscode/src/python-setup/gateways/PythonSetupCliClient.test.ts @@ -63,7 +63,6 @@ function fakeSpawn(script: { } const inv = { - mode: "default" as const, compute: {kind: "serverless" as const, version: "5"}, }; diff --git a/packages/databricks-vscode/src/python-setup/utils/setupLocalArgs.test.ts b/packages/databricks-vscode/src/python-setup/utils/setupLocalArgs.test.ts index 492d7010e..39e40d688 100644 --- a/packages/databricks-vscode/src/python-setup/utils/setupLocalArgs.test.ts +++ b/packages/databricks-vscode/src/python-setup/utils/setupLocalArgs.test.ts @@ -4,7 +4,6 @@ import {buildSetupLocalArgs, SetupLocalInvocation} from "./setupLocalArgs"; describe("buildSetupLocalArgs", () => { it("builds a default serverless invocation with JSON output", () => { const inv: SetupLocalInvocation = { - mode: "default", compute: {kind: "serverless", version: "5"}, }; expect(buildSetupLocalArgs(inv)).to.deep.equal([ @@ -19,7 +18,6 @@ describe("buildSetupLocalArgs", () => { it("uses --cluster-id for a cluster target", () => { const args = buildSetupLocalArgs({ - mode: "default", compute: {kind: "cluster", clusterId: "0710-abc"}, }); const i = args.indexOf("--cluster-id"); @@ -28,25 +26,56 @@ describe("buildSetupLocalArgs", () => { expect(args).to.not.include("--serverless-version"); }); - it("adds --constraints-only in constraints-only mode", () => { + it("adds --no-constraints when constraints are skipped", () => { const args = buildSetupLocalArgs({ - mode: "constraints-only", + skipConstraints: true, compute: {kind: "serverless", version: "5"}, }); - expect(args).to.include("--constraints-only"); + expect(args).to.include("--no-constraints"); + expect(args).to.not.include("--no-dbconnect"); }); - it("omits --constraints-only in default mode", () => { + it("adds --no-dbconnect when databricks-connect is skipped", () => { const args = buildSetupLocalArgs({ - mode: "default", + skipDbconnect: true, compute: {kind: "serverless", version: "5"}, }); + expect(args).to.include("--no-dbconnect"); + expect(args).to.not.include("--no-constraints"); + }); + + it("adds both negative flags when both are skipped, --no-constraints first", () => { + const args = buildSetupLocalArgs({ + skipConstraints: true, + skipDbconnect: true, + compute: {kind: "serverless", version: "5"}, + }); + const c = args.indexOf("--no-constraints"); + const d = args.indexOf("--no-dbconnect"); + expect(c).to.be.greaterThan(-1); + expect(d).to.be.greaterThan(-1); + expect(c).to.be.lessThan(d); + }); + + it("emits no behavioral flags for the default (full) invocation", () => { + const args = buildSetupLocalArgs({ + compute: {kind: "serverless", version: "5"}, + }); + expect(args).to.not.include("--no-constraints"); + expect(args).to.not.include("--no-dbconnect"); + }); + + it("never emits the deprecated --constraints-only flag", () => { + const args = buildSetupLocalArgs({ + skipConstraints: true, + skipDbconnect: true, + compute: {kind: "cluster", clusterId: "c"}, + }); expect(args).to.not.include("--constraints-only"); }); it("never passes --profile (auth is forwarded via the environment)", () => { const args = buildSetupLocalArgs({ - mode: "default", compute: {kind: "serverless", version: "5"}, }); expect(args).to.not.include("--profile"); @@ -54,7 +83,6 @@ describe("buildSetupLocalArgs", () => { it("passes the hidden --constraint-source-url when provided", () => { const args = buildSetupLocalArgs({ - mode: "default", compute: {kind: "serverless", version: "5"}, constraintSourceUrl: "http://localhost:8077", }); @@ -65,7 +93,8 @@ describe("buildSetupLocalArgs", () => { it("always ends with --output json", () => { const args = buildSetupLocalArgs({ - mode: "constraints-only", + skipConstraints: true, + skipDbconnect: true, compute: {kind: "cluster", clusterId: "c"}, constraintSourceUrl: "u", }); @@ -74,7 +103,6 @@ describe("buildSetupLocalArgs", () => { it("adds --dry-run when the invocation is a dry run", () => { const args = buildSetupLocalArgs({ - mode: "default", compute: {kind: "serverless", version: "5"}, dryRun: true, }); @@ -85,7 +113,6 @@ describe("buildSetupLocalArgs", () => { it("omits --dry-run by default", () => { const args = buildSetupLocalArgs({ - mode: "default", compute: {kind: "serverless", version: "5"}, }); expect(args).to.not.include("--dry-run"); diff --git a/packages/databricks-vscode/src/python-setup/utils/setupLocalArgs.ts b/packages/databricks-vscode/src/python-setup/utils/setupLocalArgs.ts index f694b024e..1cfa45548 100644 --- a/packages/databricks-vscode/src/python-setup/utils/setupLocalArgs.ts +++ b/packages/databricks-vscode/src/python-setup/utils/setupLocalArgs.ts @@ -1,10 +1,8 @@ -import {PythonSetupMode} from "../models/PythonSetupResult"; - /** - * A resolved `environments setup-local` invocation: the mode, the compute - * target (cluster or serverless), and an optional dev-only constraint-source - * override. This is the pure input to {@link buildSetupLocalArgs}; the gateway - * turns the argv into a spawned process. + * A resolved `environments setup-local` invocation: the compute target (cluster + * or serverless), which of the two behavioral steps to skip, and an optional + * dev-only constraint-source override. This is the pure input to + * {@link buildSetupLocalArgs}; the gateway turns the argv into a spawned process. * * There is deliberately no profile here: authentication reaches the CLI through * the spawned process's environment (see `CliWrapper.getSetupLocalEnvVars`), @@ -12,7 +10,6 @@ import {PythonSetupMode} from "../models/PythonSetupResult"; * flag would be a redundant second source of truth. */ export interface SetupLocalInvocation { - mode: PythonSetupMode; /** * When true, pass `--dry-run`: the CLI resolves compute and reports the * environment key without provisioning or writing to disk. Used by drift @@ -23,6 +20,17 @@ export interface SetupLocalInvocation { compute: | {kind: "cluster"; clusterId: string} | {kind: "serverless"; version: string}; + /** + * When true, pass `--no-constraints`: don't write the remote Python-version + * and dependency pins. Orthogonal to {@link skipDbconnect}. + */ + skipConstraints?: boolean; + /** + * When true, pass `--no-dbconnect`: don't add the databricks-connect + * dependency. The orthogonal replacement for the deprecated + * `--constraints-only`. Orthogonal to {@link skipConstraints}. + */ + skipDbconnect?: boolean; /** * Hidden `--constraint-source-url` override (dev/testing only). The * serverless version is passed verbatim as a bare number, e.g. "5" — the @@ -34,7 +42,8 @@ export interface SetupLocalInvocation { /** * Build the argv for `databricks environments setup-local --output json`. * Deterministic and side-effect-free so it is trivially unit-testable; the - * order is fixed (compute → mode → source → output) for stable tests. + * order is fixed (compute → skip flags → dry-run → source → output) for stable + * tests. */ export function buildSetupLocalArgs(inv: SetupLocalInvocation): string[] { const args = ["environments", "setup-local"]; @@ -45,8 +54,11 @@ export function buildSetupLocalArgs(inv: SetupLocalInvocation): string[] { args.push("--serverless-version", inv.compute.version); } - if (inv.mode === "constraints-only") { - args.push("--constraints-only"); + if (inv.skipConstraints) { + args.push("--no-constraints"); + } + if (inv.skipDbconnect) { + args.push("--no-dbconnect"); } if (inv.dryRun) { args.push("--dry-run"); From c95891a444bc4f847537152b3a5399f3603fc001 Mon Sep 17 00:00:00 2001 From: "@rugpanov" Date: Mon, 7 Sep 2026 11:04:35 +0200 Subject: [PATCH 2/2] Trim the setup-local telemetry mode comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit *Why* A code-conventions review flagged the 4-line comment at the attempt-telemetry emit site as longer than the single line it guards (CODE_CONVENTIONS.md §4b: "shorter than the code it guards"). *What* Reduce it to the one load-bearing fact — why `skipDbconnect` maps to the `constraints-only` telemetry mode (it is the orthogonal spelling of the legacy `--constraints-only`). No behavior change; comment only. *Verification* - `tsc --noEmit`: clean (modulo an unrelated local symlink artifact). - eslint + prettier: clean. Co-authored-by: Isaac --- .../python-setup/controllers/PythonSetupEnvironmentSetup.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/databricks-vscode/src/python-setup/controllers/PythonSetupEnvironmentSetup.ts b/packages/databricks-vscode/src/python-setup/controllers/PythonSetupEnvironmentSetup.ts index 0d3dff3a8..99391faaa 100644 --- a/packages/databricks-vscode/src/python-setup/controllers/PythonSetupEnvironmentSetup.ts +++ b/packages/databricks-vscode/src/python-setup/controllers/PythonSetupEnvironmentSetup.ts @@ -634,10 +634,8 @@ export class PythonSetupEnvironmentSetup implements Disposable { targetType: compute.kind, serverlessVersion: compute.kind === "serverless" ? compute.version : undefined, - // The attempt event keeps its default/constraints-only mode - // dimension; derive it from the orthogonal flag that drops - // databricks-connect (the CLI treats --no-dbconnect and the old - // --constraints-only as the same mode). + // --no-dbconnect is the orthogonal spelling of the legacy + // --constraints-only, so it maps to that telemetry mode. mode: invocation.skipDbconnect ? "constraints-only" : "default", isGreenfield, // A run against a project already marked ready this session is a