Skip to content

Commit c817a7a

Browse files
committed
Pass DATABRICKS_CLI_PATH and resolve the platform-specific CLI binary
*Why*: * On Windows, `databricks bundle` deploys failed with "databricks CLI not found" because the SDK/Terraform provider could not locate the bundled CLI. * The forwarded path was extensionless (`bin/databricks`), but the bundled Windows binary is `databricks.exe`; the Go SDK/Terraform do a literal file lookup and do not auto-append `.exe` the way Windows CreateProcess does. *What:* * Forward the resolved CLI path to subprocesses via the DATABRICKS_CLI_PATH env var so they don't fall back to a PATH search. * Make CliWrapper.cliPath return the platform-specific binary name (`databricks.exe` on win32, `databricks` elsewhere). *Verification:* * Added and ran unit tests for the platform-specific binary name and for toEnv() exposing DATABRICKS_CLI_PATH (13 passing in the VS Code test host). * Built the win32-x64 VSIX and confirmed the bundled extension contains both the env var and the `.exe` path, with `bin/databricks.exe` packaged. Co-authored-by: Isaac
1 parent 79ef3eb commit c817a7a

4 files changed

Lines changed: 78 additions & 1 deletion

File tree

packages/databricks-vscode/src/cli/CliWrapper.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,33 @@ describe(__filename, function () {
5353
assert.ok(result.stdout.indexOf("databricks") > 0);
5454
});
5555

56+
it("should resolve the platform-specific CLI binary name", () => {
57+
const cli = createCliWrapper();
58+
const originalPlatform = process.platform;
59+
const setPlatform = (platform: NodeJS.Platform) =>
60+
Object.defineProperty(process, "platform", {value: platform});
61+
try {
62+
// On Windows the bundled binary is `databricks.exe`. The `.exe` is
63+
// required because cliPath is forwarded to the SDK/Terraform via
64+
// DATABRICKS_CLI_PATH, which does a literal (no auto-`.exe`) lookup.
65+
setPlatform("win32");
66+
assert.ok(
67+
cli.cliPath.endsWith(path.join("bin", "databricks.exe")),
68+
`expected win32 cliPath to end with bin/databricks.exe, got ${cli.cliPath}`
69+
);
70+
71+
for (const platform of ["linux", "darwin"] as NodeJS.Platform[]) {
72+
setPlatform(platform);
73+
assert.ok(
74+
cli.cliPath.endsWith(path.join("bin", "databricks")),
75+
`expected ${platform} cliPath to end with bin/databricks, got ${cli.cliPath}`
76+
);
77+
}
78+
} finally {
79+
setPlatform(originalPlatform);
80+
}
81+
});
82+
5683
let mocks: any[] = [];
5784
afterEach(() => {
5885
mocks.forEach((mock) => reset(mock));

packages/databricks-vscode/src/cli/CliWrapper.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,15 @@ export class CliWrapper {
298298
}
299299

300300
get cliPath(): string {
301-
return this.extensionContext.asAbsolutePath("./bin/databricks");
301+
// The bundled binary is named `databricks.exe` on Windows. We must
302+
// include the extension here: while spawning the CLI ourselves works
303+
// without it (Windows' CreateProcess auto-appends `.exe`), this path is
304+
// also forwarded to the Databricks Go SDK / Terraform provider via the
305+
// DATABRICKS_CLI_PATH env var, and they do a literal file lookup that
306+
// fails on an extensionless path with "databricks CLI not found".
307+
const binName =
308+
process.platform === "win32" ? "databricks.exe" : "databricks";
309+
return this.extensionContext.asAbsolutePath(`./bin/${binName}`);
302310
}
303311

304312
getLoggingArguments(): string[] {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import * as assert from "assert";
2+
import {instance, mock} from "ts-mockito";
3+
import {DatabricksCliAuthProvider} from "./AuthProvider";
4+
import {CliWrapper} from "../../cli/CliWrapper";
5+
6+
describe(__filename, () => {
7+
describe("DatabricksCliAuthProvider.toEnv", () => {
8+
const host = new URL("https://test.cloud.databricks.com");
9+
const cliPath = "/path/to/bin/databricks";
10+
11+
function createProvider(profile?: string, workspaceId?: string) {
12+
return new DatabricksCliAuthProvider(
13+
host,
14+
cliPath,
15+
instance(mock(CliWrapper)),
16+
profile,
17+
workspaceId
18+
);
19+
}
20+
21+
it("should expose DATABRICKS_CLI_PATH so the SDK/Terraform provider can locate the bundled CLI", () => {
22+
const env = createProvider("dev").toEnv();
23+
24+
assert.equal(env["DATABRICKS_CLI_PATH"], cliPath);
25+
assert.equal(env["DATABRICKS_HOST"], host.toString());
26+
assert.equal(env["DATABRICKS_AUTH_TYPE"], "databricks-cli");
27+
assert.equal(env["DATABRICKS_CONFIG_PROFILE"], "dev");
28+
});
29+
30+
it("should include DATABRICKS_CLI_PATH even without a profile or workspace id", () => {
31+
const env = createProvider().toEnv();
32+
33+
assert.equal(env["DATABRICKS_CLI_PATH"], cliPath);
34+
assert.ok(!("DATABRICKS_CONFIG_PROFILE" in env));
35+
assert.ok(!("DATABRICKS_WORKSPACE_ID" in env));
36+
});
37+
});
38+
});

packages/databricks-vscode/src/configuration/auth/AuthProvider.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,10 @@ export class DatabricksCliAuthProvider extends AuthProvider {
355355
const env: Record<string, string> = {
356356
DATABRICKS_HOST: this.host.toString(),
357357
DATABRICKS_AUTH_TYPE: "databricks-cli",
358+
// Point the SDK/Terraform provider at the bundled CLI so they don't
359+
// fall back to searching PATH (and fail with "databricks CLI not
360+
// found") in subprocesses that don't inherit our resolved path.
361+
DATABRICKS_CLI_PATH: this.cliPath,
358362
};
359363
if (this.profile) {
360364
env["DATABRICKS_CONFIG_PROFILE"] = this.profile;

0 commit comments

Comments
 (0)