diff --git a/src/ucode/databricks.py b/src/ucode/databricks.py index 69c0267f2..7358b1cf3 100644 --- a/src/ucode/databricks.py +++ b/src/ucode/databricks.py @@ -66,6 +66,10 @@ MIN_DATABRICKS_CLI_VERSION = (1, 0, 0) # v1.11.0 fixes `fs cp` (create -> finalize), which the skills MCP uploads rely on. SKILLS_MCP_MIN_DATABRICKS_CLI_VERSION = (1, 11, 0) +# `databricks --version` is normally instant, but a wrapper binary (e.g. dbexec on +# arca) under concurrent startups can take several seconds; a timeout reads as an +# unparseable version and fails the launch. +_DATABRICKS_CLI_VERSION_TIMEOUT_SECONDS = 30 TOKEN_REFRESH_INTERVAL_SECONDS = 1800 # Substrings the Databricks CLI emits when it loses the token-cache write lock # to a concurrent `databricks auth token` (e.g. another ucode helper process @@ -789,7 +793,13 @@ def _read_databricks_cli_version(path: str) -> tuple[int, int, int] | None: place — every other caller reads a version through ``databricks_cli_path()``. """ try: - result = run([path, "--version"], check=False, capture_output=True, text=True, timeout=10) + result = run( + [path, "--version"], + check=False, + capture_output=True, + text=True, + timeout=_DATABRICKS_CLI_VERSION_TIMEOUT_SECONDS, + ) except (OSError, subprocess.TimeoutExpired): return None raw = result.stdout or result.stderr or "" @@ -980,7 +990,7 @@ def ensure_databricks_cli_version( check=False, capture_output=True, text=True, - timeout=10, + timeout=_DATABRICKS_CLI_VERSION_TIMEOUT_SECONDS, ) raw = result.stdout or result.stderr or "" output = (raw if isinstance(raw, str) else raw.decode(errors="replace")).strip() diff --git a/tests/test_databricks.py b/tests/test_databricks.py index b0efa989f..4959f9b8e 100644 --- a/tests/test_databricks.py +++ b/tests/test_databricks.py @@ -4280,6 +4280,17 @@ def boom(*a, **kw): monkeypatch.setattr(db_mod, "run", boom) assert db_mod._read_databricks_cli_version("/usr/bin/databricks") is None + def test_read_version_allows_slow_cli(self, monkeypatch): + seen = {} + + def fake_run(*a, **kw): + seen["timeout"] = kw["timeout"] + return subprocess.CompletedProcess(a, 0, "Databricks CLI v1.20.0", "") + + monkeypatch.setattr(db_mod, "run", fake_run) + db_mod._read_databricks_cli_version("/usr/bin/databricks") + assert seen["timeout"] == db_mod._DATABRICKS_CLI_VERSION_TIMEOUT_SECONDS >= 30 + def test_read_version_parses_output(self, monkeypatch): monkeypatch.setattr( db_mod,