Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
3603c27
localenv: discover the uv workspace root with uv workspace dir
rugpanov Oct 12, 2026
269d4a8
localenv: provision the shared venv and write pins at the uv workspac…
rugpanov Oct 12, 2026
8174563
environments: report the uv workspace root in setup-local output
rugpanov Oct 12, 2026
428f359
localenv: add an integration test for a uv workspace member
rugpanov Oct 12, 2026
c154c98
localenv: keep the workspace root and venvPath correct through symlinks
rugpanov Oct 12, 2026
e158b24
environments: say when setup-local leaves the workspace root unchanged
rugpanov Oct 12, 2026
35c5a6a
localenv: remove a stale managed constraint block from a workspace me…
rugpanov Oct 12, 2026
f1b8dc2
Add the PR link to the changelog fragment
rugpanov Oct 12, 2026
656a53a
localenv: compare the workspace root by folder identity, not by name
rugpanov Oct 12, 2026
c8bad8f
localenv: keep user [tool.uv] keys and reject a dotted-key workspace …
rugpanov Oct 12, 2026
2ed7edd
localenv: address review findings for uv workspace members
rugpanov Oct 12, 2026
e0aed5f
localenv: harden uv workspace root handling after review
rugpanov Oct 12, 2026
af7d70d
localenv: validate the merged workspace root before writing it
rugpanov Oct 12, 2026
1f1b5db
localenv: plan a root write only when the root changes; keep user bla…
rugpanov Oct 12, 2026
163f8b6
localenv: prefix the uv workspace JSON fields with uv
rugpanov Oct 12, 2026
a5a514b
localenv: stop on an interrupt during workspace discovery; uv-prefix …
rugpanov Oct 12, 2026
045a812
localenv: skip comments when checking for root pins; skip an unchange…
rugpanov Oct 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .nextchanges/cli/setup-local-uv-workspace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* `databricks environments setup-local` now supports uv workspace members: it uses the shared `.venv` at the workspace root and writes the dependency constraints to the root `pyproject.toml`. ([#7018](https://github.com/databricks/cli/pull/7018))
2 changes: 2 additions & 0 deletions acceptance/localenv/help/output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ Set up a local Python environment that matches a Databricks cluster or serverles

Use this when you want to develop or debug a Databricks project locally: it installs the matching Python version and a compatible databricks-connect, and pins your dependencies to versions known to work with your chosen compute. It creates or updates a .venv (managed by uv) in the current directory and records the setup in pyproject.toml, leaving the rest of your project untouched.

In a uv workspace member, the command uses the shared .venv at the workspace root and writes the dependency constraints to the root pyproject.toml, because uv reads them only from there. uv sync installs the current member's dependencies into the shared .venv and removes packages that only other members need. All members share one environment, so they must target compatible compute.

Usage:
databricks environments setup-local [flags]

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[project]
name = "a"
version = "0.1.0"
requires-python = ">=3.10"
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
# uv older than 0.9.9 has no `workspace` subcommand.
if [ "$1" = "workspace" ]; then
echo "error: unrecognized subcommand 'workspace'" >&2
exit 2
fi
echo "fake uv: unexpected call: $*" >&2
exit 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Cloud = false
GOOS.windows = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@

>>> [CLI] environments setup-local --serverless-version 4 --dry-run --output json
{
"schemaVersion": 1,
"command": "environments setup-local",
"ok": true,
"mode": "default",
"dryRun": true,
"compute": {
"source": "serverless",
"serverlessVersion": "v4",
"envKey": "serverless/serverless-v4"
},
"resolved": {
"pythonVersion": "3.12",
"dbconnectVersion": "17.2.0",
"artifactSource": "network"
},
"greenfield": false,
"plan": {
"wouldWrite": "[TEST_TMP_DIR]/bundles/a/pyproject.toml",
"wouldBackup": "[TEST_TMP_DIR]/bundles/a/pyproject.toml.bak",
"wouldInstallPython": "3.12",
"diff": "--- pyproject.toml\n+++ pyproject.toml.new\n@@ -1,4 +1,20 @@\n [project]\n name = \"a\"\n version = \"0.1.0\"\n-requires-python = \"\u003e=3.10\"\n+requires-python = \"\u003e=3.12\"\n+\n+[dependency-groups]\n+dev = [\n+ \"databricks-connect~=17.2.0\",\n+]\n+\n+[tool.databricks.environment]\n+environment_version = \"4\"\n+\n+# managed by databricks environments setup-local — do not edit\n+[tool.uv]\n+constraint-dependencies = [\n+ \"pyarrow\u003c19\",\n+ \"pandas\u003c3\",\n+]\n+# end managed by databricks environments setup-local\n"
},
"phases": [
{
"phase": "preflight",
"status": "ok"
},
{
"phase": "resolve",
"status": "ok"
},
{
"phase": "fetch",
"status": "ok"
},
{
"phase": "merge",
"status": "ok"
},
{
"phase": "provision",
"status": "ok"
},
{
"phase": "validate",
"status": "ok"
}
],
"warnings": [
{
"code": "W_REQUIRES_PYTHON_OVERRIDDEN",
"message": "requires-python \"\u003e=3.10\" is replaced by the environment's \"\u003e=3.12\""
}
],
"error": null,
"durationMs": [DURATION_MS]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[project]
name = "root"
version = "0.1.0"
requires-python = ">=3.10"

[tool.uv.workspace]
members = ["bundles/*"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# uv older than 0.9.9 cannot report the workspace root, so in a workspace member
# the plan is the same as for a standalone project: no workspace fields.
mv fake-uv.sh uv
chmod +x uv
cleanup() {
rm -f uv
}
trap cleanup EXIT
export PATH="$(pwd):$PATH"

withdir bundles/a trace $CLI environments setup-local --serverless-version 4 --dry-run --output json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# The fake uv is a bash script, so this test cannot run on Windows runners
# (mirrors acceptance/cmd/psql).
GOOS.windows = false

Env.DATABRICKS_LOCALENV_CONSTRAINT_SOURCE_URL_TEST_OVERRIDE = "$DATABRICKS_HOST"

[[Server]]
Pattern = "GET /serverless/serverless-v4/pyproject.toml"
Response.Body = '''
[project]
requires-python = ">=3.12"

[dependency-groups]
dev = ["databricks-connect~=17.2.0"]

[tool.uv]
constraint-dependencies = ["pyarrow<19", "pandas<3"]
'''
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[project]
name = "a"
version = "0.1.0"
requires-python = ">=3.10"
8 changes: 8 additions & 0 deletions acceptance/localenv/workspace-member-check/fake-uv.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
# The workspace root is the test folder, where this script is installed as uv.
if [ "$1" = "workspace" ] && [ "$2" = "dir" ]; then
cd "$(dirname "$0")" && pwd
exit 0
fi
echo "fake uv: unexpected call: $*" >&2
exit 1
2 changes: 2 additions & 0 deletions acceptance/localenv/workspace-member-check/out.test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Cloud = false
GOOS.windows = false
73 changes: 73 additions & 0 deletions acceptance/localenv/workspace-member-check/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@

>>> [CLI] environments setup-local --serverless-version 4 --dry-run
warning: requires-python ">=3.10" is replaced by the environment's ">=3.12"
Plan: [TEST_TMP_DIR]/bundles/a/pyproject.toml
Plan: [TEST_TMP_DIR]/pyproject.toml (uv workspace root)
changed region: requires-python
changed region: databricks-connect
changed region: tool.databricks.environment
changed region: tool.uv.constraint-dependencies (uv workspace root)
Check complete. No files were modified.

>>> [CLI] environments setup-local --serverless-version 4 --dry-run --output json
{
"schemaVersion": 1,
"command": "environments setup-local",
"ok": true,
"mode": "default",
"dryRun": true,
"compute": {
"source": "serverless",
"serverlessVersion": "v4",
"envKey": "serverless/serverless-v4"
},
"resolved": {
"pythonVersion": "3.12",
"dbconnectVersion": "17.2.0",
"artifactSource": "network"
},
"greenfield": false,
"plan": {
"wouldWrite": "[TEST_TMP_DIR]/bundles/a/pyproject.toml",
"wouldBackup": "[TEST_TMP_DIR]/bundles/a/pyproject.toml.bak",
"wouldWriteUvWorkspace": "[TEST_TMP_DIR]/pyproject.toml",
"wouldBackupUvWorkspace": "[TEST_TMP_DIR]/pyproject.toml.bak",
"wouldInstallPython": "3.12",
"diff": "--- pyproject.toml\n+++ pyproject.toml.new\n@@ -1,4 +1,12 @@\n [project]\n name = \"a\"\n version = \"0.1.0\"\n-requires-python = \"\u003e=3.10\"\n+requires-python = \"\u003e=3.12\"\n+\n+[dependency-groups]\n+dev = [\n+ \"databricks-connect~=17.2.0\",\n+]\n+\n+[tool.databricks.environment]\n+environment_version = \"4\"\n--- uv workspace root pyproject.toml\n+++ uv workspace root pyproject.toml.new\n@@ -5,3 +5,11 @@\n \n [tool.uv.workspace]\n members = [\"bundles/*\"]\n+\n+# managed by databricks environments setup-local — do not edit\n+[tool.uv]\n+constraint-dependencies = [\n+ \"pyarrow\u003c19\",\n+ \"pandas\u003c3\",\n+]\n+# end managed by databricks environments setup-local\n"
},
"uvWorkspaceRoot": "[TEST_TMP_DIR]",
"phases": [
{
"phase": "preflight",
"status": "ok"
},
{
"phase": "resolve",
"status": "ok"
},
{
"phase": "fetch",
"status": "ok"
},
{
"phase": "merge",
"status": "ok"
},
{
"phase": "provision",
"status": "ok"
},
{
"phase": "validate",
"status": "ok"
}
],
"warnings": [
{
"code": "W_REQUIRES_PYTHON_OVERRIDDEN",
"message": "requires-python \"\u003e=3.10\" is replaced by the environment's \"\u003e=3.12\""
}
],
"error": null,
"durationMs": [DURATION_MS]
}
7 changes: 7 additions & 0 deletions acceptance/localenv/workspace-member-check/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[project]
name = "root"
version = "0.1.0"
requires-python = ">=3.10"

[tool.uv.workspace]
members = ["bundles/*"]
13 changes: 13 additions & 0 deletions acceptance/localenv/workspace-member-check/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# A uv workspace member: the plan writes the member pyproject.toml and the
# constraint block into the workspace root pyproject.toml. A fake uv on PATH
# answers `uv workspace dir` (see acceptance/cmd/psql for the pattern).
mv fake-uv.sh uv
chmod +x uv
cleanup() {
rm -f uv
}
trap cleanup EXIT
export PATH="$(pwd):$PATH"

withdir bundles/a trace $CLI environments setup-local --serverless-version 4 --dry-run
withdir bundles/a trace $CLI environments setup-local --serverless-version 4 --dry-run --output json
18 changes: 18 additions & 0 deletions acceptance/localenv/workspace-member-check/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# The fake uv is a bash script, so this test cannot run on Windows runners
# (mirrors acceptance/cmd/psql).
GOOS.windows = false

Env.DATABRICKS_LOCALENV_CONSTRAINT_SOURCE_URL_TEST_OVERRIDE = "$DATABRICKS_HOST"

[[Server]]
Pattern = "GET /serverless/serverless-v4/pyproject.toml"
Response.Body = '''
[project]
requires-python = ">=3.12"

[dependency-groups]
dev = ["databricks-connect~=17.2.0"]

[tool.uv]
constraint-dependencies = ["pyarrow<19", "pandas<3"]
'''
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[project]
name = "a"
version = "0.1.0"
requires-python = ">=3.10"
24 changes: 24 additions & 0 deletions acceptance/localenv/workspace-member/fake-uv.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash
# The workspace root is the test folder, where this script is installed as uv.
# `uv sync` creates the shared venv there with a fake interpreter that answers
# the validate probe (prefixes from libs/localenv/uv.go).
root="$(cd "$(dirname "$0")" && pwd)"
case "$1 $2" in
"--version ") echo "uv 0.12.9 (fake)"; exit 0 ;;
"workspace dir") echo "$root"; exit 0 ;;
"python install") exit 0 ;;
"pip install") exit 0 ;;
"sync --python")
mkdir -p "$root/.venv/bin"
cat > "$root/.venv/bin/python" <<'PY'
#!/bin/bash
echo "PYVER:3.12"
echo "DBCVER:17.2.0"
echo "PYSPARKVER:"
echo "DBCIMPORT:"
PY
chmod +x "$root/.venv/bin/python"
exit 0 ;;
esac
echo "fake uv: unexpected call: $*" >&2
exit 1
2 changes: 2 additions & 0 deletions acceptance/localenv/workspace-member/out.test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Cloud = false
GOOS.windows = false
105 changes: 105 additions & 0 deletions acceptance/localenv/workspace-member/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@

>>> [CLI] environments setup-local --serverless-version 4 --output json
{
"schemaVersion": 1,
"command": "environments setup-local",
"ok": true,
"mode": "default",
"dryRun": false,
"compute": {
"source": "serverless",
"serverlessVersion": "v4",
"envKey": "serverless/serverless-v4"
},
"resolved": {
"pythonVersion": "3.12",
"dbconnectVersion": "17.2.0",
"artifactSource": "network"
},
"greenfield": false,
"venvPath": "../../.venv",
"uvWorkspaceRoot": "[TEST_TMP_DIR]",
"pythonResolution": "uv_install_succeeded",
"phases": [
{
"phase": "preflight",
"status": "ok"
},
{
"phase": "resolve",
"status": "ok"
},
{
"phase": "fetch",
"status": "ok"
},
{
"phase": "merge",
"status": "ok"
},
{
"phase": "provision",
"status": "ok"
},
{
"phase": "validate",
"status": "ok"
}
],
"warnings": [
{
"code": "W_REQUIRES_PYTHON_OVERRIDDEN",
"message": "requires-python \"\u003e=3.10\" is replaced by the environment's \"\u003e=3.12\""
}
],
"error": null,
"backupPath": "[TEST_TMP_DIR]/bundles/a/pyproject.toml.bak",
"uvWorkspaceBackupPath": "[TEST_TMP_DIR]/pyproject.toml.bak",
"durationMs": [DURATION_MS]
}

>>> [CLI] environments setup-local --serverless-version 4
✔ Local environment ready

Compute target serverless 4
Python 3.12
databricks-connect 17.2.0
Virtual env ../../.venv
pyproject.toml updated
uv workspace root [TEST_TMP_DIR]
Root pyproject.toml unchanged

Next steps:
• Activate it: source ../../.venv/bin/activate
• Or select ../../.venv as the Python interpreter in VS Code / Cursor

>>> cat pyproject.toml
[project]
name = "root"
version = "0.1.0"
requires-python = ">=3.10"

[tool.uv.workspace]
members = ["bundles/*"]

# managed by databricks environments setup-local — do not edit
[tool.uv]
constraint-dependencies = [
"pyarrow<19",
"pandas<3",
]
# end managed by databricks environments setup-local

>>> cat bundles/a/pyproject.toml
[project]
name = "a"
version = "0.1.0"
requires-python = ">=3.12"

[dependency-groups]
dev = [
"databricks-connect~=17.2.0",
]

[tool.databricks.environment]
environment_version = "4"
Loading
Loading