Skip to content

[VPEX][4/8] Add local-env formatting-preserving pyproject.toml merge - #5827

Merged
rugpanov merged 21 commits into
mainfrom
dbconnect/04-merge
Jul 7, 2026
Merged

[VPEX][4/8] Add local-env formatting-preserving pyproject.toml merge#5827
rugpanov merged 21 commits into
mainfrom
dbconnect/04-merge

Conversation

@rugpanov

@rugpanovrugpanov commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Why

  • local-env must apply the resolved Python version and constraints to the user's pyproject.toml without disturbing their own content — comments, ordering, formatting, and unrelated config must survive untouched.
  • Re-running must be safe and idempotent, and a greenfield project needs a sensible file created from scratch.
  • This is the most intricate logic in the feature, so it lands in its own PR for focused review.

What

  • merge.go — a formatting-preserving merge that rewrites only the env-owned regions (requires-python, the databricks-connect entry in [dependency-groups].dev, and a marker-bracketed managed [tool.uv] block) and preserves every other byte incl. CRLF; idempotent. RenderFreshPyproject builds a complete managed file for a greenfield project.
  • Scoping/robustness: managed constraint-dependencies nests header-less inside an existing user [tool.uv] (never a duplicate header); single- vs multi-line array detection tracks real bracket depth outside strings/comments; the databricks-connect rewrite is confined to dev and leaves trailing comments alone; requires-python's inline comment is preserved; table-header parsing tolerates inline comments and recognizes [[array.of.tables]].

Testing strategy

  • Unit tests that parse the merged output as TOML (not just substring checks), covering idempotency, CRLF preservation, user-key preservation, the duplicate-[tool.uv] case, bracket-in-element arrays, sibling-group/comment non-clobbering, and [[tool.uv.index]] children (merge_test.go).
  • Gates: go build, go test, golangci-lint, deadcode, gofmt — all green.
  • Reviewed with codex to a clean pass (multiple TOML-corruption edge cases were caught and fixed).

About this stack

This is one of a series of small, stacked PRs that together add the databricks local-env python sync command — it provisions a local Python environment (Python version, databricks-connect pin, and dependency constraints) matched to a selected Databricks compute target. The work was split from one large branch into single-concern layers so each is independently reviewable; the command is kept hidden until the final PR so nothing is user-visible mid-stack.

Review bottom-up. Each PR targets the previous one as its base branch, so its diff shows only that layer.

#PRWhat
1#5823foundation: result types + env-key mapping
2#5824compute-target resolution
3#5826constraint fetch + offline cache
4#5827 ← you are hereformatting-preserving pyproject.toml merge
5#5828six-phase pipeline + detection + package-manager interface
6#5832uv backend + CLI command (registered hidden)
7#5833acceptance tests
8#5835unveil (unhide + help + changelog)

This pull request and its description were written by Isaac.

@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 49b1825

Run: 28869122864

Env🔄​flaky💚​RECOVERED🙈​SKIP✅​pass🙈​skipTime
💚​aws linux4423010645:31
💚​aws windows4423210625:46
💚​aws-ucws linux443149826:49
💚​aws-ucws windows443169806:25
💚​azure linux4423010636:13
💚​azure windows4423210616:17
💚​azure-ucws linux443169796:56
🔄​azure-ucws windows2243189778:06
💚​gcp linux4422910655:08
💚​gcp windows4423110635:49
8 interesting tests: 4 SKIP, 2 RECOVERED, 2 flaky
Test Nameaws linuxaws windowsaws-ucws linuxaws-ucws windowsazure linuxazure windowsazure-ucws linuxazure-ucws windowsgcp linuxgcp windows
💚​TestAccept💚​R💚​R💚​R💚​R💚​R💚​R💚​R💚​R💚​R💚​R
🙈​TestAccept/bundle/invariant/no_drift🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/ssh/connection🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
💚​TestFetchRepositoryInfoAPI_FromRepo💚​R💚​R💚​R💚​R💚​R💚​R💚​R💚​R💚​R💚​R
🔄​TestFetchRepositoryInfoAPI_FromRepo/root💚​R💚​R💚​R💚​R💚​R💚​R💚​R🔄​f💚​R💚​R
🔄​TestFetchRepositoryInfoAPI_FromRepo/subdir💚​R💚​R💚​R💚​R💚​R💚​R💚​R🔄​f💚​R💚​R
Top 11 slowest tests (at least 2 minutes):
durationenvtestname
5:07azure windowsTestAccept
5:03aws-ucws windowsTestAccept
5:03azure-ucws windowsTestAccept
5:01gcp windowsTestAccept
4:57aws windowsTestAccept
3:05aws-ucws windowsTestFilerWorkspaceFilesExtensionsReadDir
3:03aws-ucws linuxTestAccept
2:56azure linuxTestAccept
2:53gcp linuxTestAccept
2:49aws linuxTestAccept
2:46azure-ucws linuxTestAccept

First of a stacked series adding `databricks local-env python sync`, which
provisions a local Python environment matched to a Databricks compute
target. The feature lands across small, single-concern PRs; each layer is
independently reviewable and adds no user-facing surface until the final PR
wires the command in.
This PR is the foundation the rest of the stack builds on:
- result.go: the result types and the --json / E_* error contract that
every phase reports through (Result, PipelineError, ErrorCode, PhaseName,
PhaseStatus, Mode, TargetInfo, ResolvedInfo, Plan, Warning), plus the
command-path constants (local-env / python / sync) defined once.
- envkey.go: mapping a compute target to an environment key and parsing the
Python minor from a requires-python specifier.
Nothing imports this package yet, so the CLI is unchanged. The unexported
filesystem/artifact constants and the canonical phase-order slice live with
the pipeline that consumes them (a later PR in the stack).
Co-authored-by: Isaac
@rugpanov
rugpanovforce-pushed the dbconnect/03-constraints branch from d867d1f to 64d8996CompareJuly 3, 2026 13:20
@rugpanov
rugpanovforce-pushed the dbconnect/04-merge branch from ccc19f1 to 92f1ddaCompareJuly 3, 2026 13:20
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 3, 2026 13:21 — with GitHub Actions Inactive
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 3, 2026 13:21 — with GitHub Actions Inactive
@rugpanovrugpanov changed the title Add dbconnect formatting-preserving pyproject.toml merge[VPEX][4/8] Add local-env formatting-preserving pyproject.toml mergeJul 3, 2026
…pecifiers
Review of the foundation layer flagged that PythonMinorFromRequires took the
first MAJOR.MINOR in the string via first-match regex. For a multi-clause
requires-python where the exclusive upper bound comes first — e.g.
"<3.13,>=3.10" — it returned 3.13, the version the "<3.13" clause forbids,
because PEP 440 clause order is arbitrary. The result feeds
PM.EnsurePython, so the tool could target a Python the constraint excludes.
Prefer a lower-bound / pinning clause (>=, >, ==, ~=, ===) and only fall
back to the first version when none is present. Adds multi-clause test
coverage; the prior tests exercised only single-bound specifiers.
Co-authored-by: Isaac
@rugpanov
rugpanovforce-pushed the dbconnect/03-constraints branch from 64d8996 to 3fd0bfeCompareJuly 3, 2026 15:26
@rugpanov
rugpanovforce-pushed the dbconnect/04-merge branch from 92f1dda to b96bf8bCompareJuly 3, 2026 15:26
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 3, 2026 15:27 — with GitHub Actions Inactive
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 3, 2026 15:27 — with GitHub Actions Inactive
@rugpanov
rugpanovforce-pushed the dbconnect/03-constraints branch from 3fd0bfe to ce01647CompareJuly 3, 2026 15:31
@rugpanov
rugpanovforce-pushed the dbconnect/04-merge branch from b96bf8b to 6477a4cCompareJuly 3, 2026 15:31
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 3, 2026 15:31 — with GitHub Actions Inactive
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 3, 2026 15:31 — with GitHub Actions Inactive
@rugpanov
rugpanovforce-pushed the dbconnect/04-merge branch from 6477a4c to f4cc1c7CompareJuly 3, 2026 15:41
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 3, 2026 15:42 — with GitHub Actions Inactive
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 3, 2026 15:42 — with GitHub Actions Inactive
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 3, 2026 17:55 — with GitHub Actions Inactive
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 3, 2026 17:55 — with GitHub Actions Inactive
…dden version
Round-2 review of the foundation layer noted that when a requires-python has
no lower-bound/pin clause at all (only upper-bound or exclusion, e.g.
"<3.13,!=3.12"), PythonMinorFromRequires fell back to the first number and
returned 3.13 — a version the specifier forbids. Such a spec has no floor to
install from, so it now errors rather than guessing. A bare "3.12" (no
operator) is still accepted as a valid floor.
Co-authored-by: Isaac
@rugpanov
rugpanovforce-pushed the dbconnect/03-constraints branch from 797c4ac to 03b4a2bCompareJuly 3, 2026 18:15
@rugpanov
rugpanovforce-pushed the dbconnect/04-merge branch from 56b496d to 7b23583CompareJuly 3, 2026 18:15
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 3, 2026 18:15 — with GitHub Actions Inactive
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 3, 2026 18:15 — with GitHub Actions Inactive
@rugpanov
rugpanovforce-pushed the dbconnect/04-merge branch from 79113e4 to a711aa4CompareJuly 6, 2026 11:47
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 6, 2026 11:47 — with GitHub Actions Inactive
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 6, 2026 11:47 — with GitHub Actions Inactive
@rugpanov

Copy link
Copy Markdown
ContributorAuthor

@anton-107 both correctness gaps addressed in a711aa44a: the merge now refuses (E_MERGE) on a TOML multi-line string and on a missing [project] table, rather than risking silent corruption / a skipped pin. Tests added for both. Ready for another look.

@anton-107anton-107 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both correctness gaps are addressed:

  • Multi-line stringsMergeManaged now detects a """ / ''' delimiter (comment-aware via commentStart) and bails with errMultilineStringE_MERGE rather than scanning a string body as structure. The detection is intentionally conservative (even a single-line """x""" is refused), which is safe, and the test plants a fake header + fake constraint-dependencies inside the string. This preserves the no-corruption guarantee.
  • No [project] table — now fails loudly with errNoProjectTableE_MERGE instead of silently skipping requires-python; greenfield still routes through RenderFreshPyproject.

LGTM.

…al-repo default
Per review, the constraint artifacts must not be sourced from a hardcoded
personal GitHub repo. This parameterizes the host: RepoConstraintBaseURL reads
the hosting repo ("owner/name") from the DATABRICKS_LOCALENV_CONSTRAINT_REPO
environment variable and builds a raw.githubusercontent.com main-branch URL.
The built-in default is intentionally empty and resolution errors when no repo
is configured, so no untrusted default controls what the CLI installs. This is
temporary: once the Databricks-owned databricks/environments repo can publish
the artifacts (its GitHub Actions are currently disabled), defaultConstraintRepo
becomes that constant and the env var is no longer required. Tracked as a
follow-up; the command stays hidden until the unveil PR regardless.
Co-authored-by: Isaac
Fourth in the stacked local-env series.
merge.go rewrites only the env-owned sections of a pyproject.toml and
preserves every other byte (comments, ordering, whitespace, CRLF). It
updates requires-python and the databricks-connect pin in place, and
maintains a marker-bracketed managed [tool.uv] constraint block. The
operation is idempotent: feeding its own output back in is byte-identical.
RenderFreshPyproject produces a complete managed file for a greenfield
project.
Two correctness properties this file has to get right, both covered by
tests that parse the result as TOML rather than asserting on strings:
- When the user's pyproject.toml already has a [tool.uv] table with a
non-constraint key, the managed constraint-dependencies nests header-less
inside that table instead of emitting a second [tool.uv] header (two
headers for one table is invalid TOML that uv rejects).
- Single- vs multi-line constraint-dependencies detection tracks real
bracket depth outside strings and comments, so an opening line that
contains a "]" inside an element (e.g. "requests[security]~=2.0") or a
trailing comment is not misread as single-line and mis-stripped.
Depends on the constraints PR for the Constraints type. Still dormant.
Co-authored-by: Isaac
Review of the merge layer found the databricks-connect rewrite was not scoped
to [dependency-groups].dev:
- It walked every line of [dependency-groups] and rewrote the first
databricks-connect element found, so a pin in a sibling group (docs/test)
was clobbered instead of the dev entry. It now locates the dev assignment
and edits only within that array's line span.
- The single-line branch replaced the databricks-connect token anywhere on
the dev line, including inside a trailing comment (user content). Replacement
is now confined to the array portion (through its closing "]"); the trailing
comment is preserved byte-for-byte.
- mergeRequiresPython replaced the whole line, dropping an inline comment such
as `requires-python = ">=3.10" # maintained by platform team`. It now
reattaches the trailing comment, honoring the byte-preservation contract for
everything outside the managed value.
Adds tests for each: sibling-group untouched, comment not clobbered, inline
comment preserved.
Co-authored-by: Isaac
Round-2 review found the merge did not tolerate a trailing comment on a table
header line (e.g. "[project] # note"). Two consequences: mergeRequiresPython
could not find a commented [project] header (managed value silently not
updated), and worse, the [dependency-groups] end bound could run past a
commented sibling header, so a dev key in a following table was mistaken for
[dependency-groups].dev and rewritten.
tableHeaderRe now allows a trailing comment and a new headerName helper matches
a header by its bracketed name ignoring the comment; both the table lookup and
the [tool.uv] attachment check use it. Also documents that line endings are a
whole-file property (a CRLF-anywhere file is emitted entirely as CRLF), which
is faithful for real single-ending pyproject.toml files.
Co-authored-by: Isaac
…ldren
Round-3 review found tableHeaderRe did not match TOML array-of-tables headers
like "[[tool.uv.index]]". A [tool.uv] table's end bound therefore ran through
its [[tool.uv.index]] children, and the header-less managed constraint block
could be inserted inside the last index item instead of under [tool.uv],
producing wrong or invalid uv config.
tableHeaderRe now matches both "[...]" and "[[...]]"; headerName returns the
full "[[...]]" token so an array-of-tables header is never treated as the same
table as its "[...]" parent. Adds a merge test with a [[tool.uv.index]] child.
Co-authored-by: Isaac
…oject files
Two correctness gaps in the line-based merge, from review:
- The scanner does not track TOML multi-line string state ("""...""" / '''...''')
across lines, so a line inside such a string that looks like a table header,
key, or bracket could mis-scope the managed-region edits and silently corrupt
the file. MergeManaged now detects a multi-line string delimiter and returns an
error (surfaced as E_MERGE) rather than risking corruption — the guarantee the
merge exists to uphold. Multi-line strings are rare in a pyproject.toml.
- A partial existing file with no [project] table would be "merged" with
requires-python silently skipped. MergeManaged now errors when [project] is
absent (greenfield goes through RenderFreshPyproject, which always writes it).
Adds tests for both bail-outs.
Co-authored-by: Isaac
@rugpanov
rugpanovforce-pushed the dbconnect/04-merge branch from a711aa4 to 41d052aCompareJuly 7, 2026 08:35
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 7, 2026 08:35 — with GitHub Actions Inactive
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 7, 2026 08:35 — with GitHub Actions Inactive
Base automatically changed from dbconnect/03-constraints to mainJuly 7, 2026 13:12
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 7, 2026 13:16 — with GitHub Actions Inactive
@rugpanov
rugpanovtemporarily deployed to test-trigger-is July 7, 2026 13:16 — with GitHub Actions Inactive
@rugpanov
rugpanov enabled auto-merge July 7, 2026 13:47
@rugpanov
rugpanov added this pull request to the merge queueJul 7, 2026
Merged via the queue into main with commit 4a55027Jul 7, 2026
23 checks passed
@rugpanov
rugpanov deleted the dbconnect/04-merge branch July 7, 2026 14:06
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 4a55027

Run: 28872585952

Env❌​FAIL🟨​KNOWN🔄​flaky💚​RECOVERED🙈​SKIP✅​pass🙈​skipTime
🔄​aws linux33452799342:58
🔄​aws windows644480100649:10
❌​aws-ucws linux26332962808137:01
❌​aws-ucws windows20152910826131:24
🔄​azure linux64451799445:29
🔄​azure windows1244468100759:03
❌​azure-ucws linux12742886839109:23
🔄​azure-ucws windows1362823857114:36
🔄​gcp linux33451299941:49
💚​gcp windows44471101248:18
62 interesting tests: 32 FAIL, 22 flaky, 3 KNOWN, 3 RECOVERED, 2 SKIP
Test Nameaws linuxaws windowsaws-ucws linuxaws-ucws windowsazure linuxazure windowsazure-ucws linuxazure-ucws windowsgcp linuxgcp windows
🟨​TestAccept🔄​f💚​R🟨​K🟨​K💚​R💚​R🟨​K💚​R🔄​f💚​R
❌​TestAccept/bundle/deploy/spark-jar-task✅​p✅​p❌​F❌​F✅​p✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=direct✅​p✅​p✅​p❌​F✅​p✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform✅​p✅​p❌​F❌​F✅​p✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/generate/pipeline_and_deploy✅​p🔄​f❌​F❌​F✅​p🔄​f🔄​f🔄​f✅​p✅​p
❌​TestAccept/bundle/generate/pipeline_and_deploy/DATABRICKS_BUNDLE_ENGINE=direct✅​p🔄​f❌​F✅​p✅​p✅​p🔄​f🔄​f✅​p✅​p
❌​TestAccept/bundle/generate/pipeline_and_deploy/DATABRICKS_BUNDLE_ENGINE=terraform✅​p✅​p✅​p❌​F✅​p🔄​f✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/generate/python_job_and_deploy✅​p🔄​f❌​F❌​F✅​p🔄​f🔄​f🔄​f✅​p✅​p
❌​TestAccept/bundle/generate/python_job_and_deploy/DATABRICKS_BUNDLE_ENGINE=direct✅​p✅​p❌​F✅​p✅​p🔄​f🔄​f🔄​f✅​p✅​p
❌​TestAccept/bundle/generate/python_job_and_deploy/DATABRICKS_BUNDLE_ENGINE=terraform✅​p🔄​f✅​p❌​F✅​p✅​p✅​p✅​p✅​p✅​p
🔄​TestAccept/bundle/invariant/continue_293🙈​s🙈​s✅​p✅​p🙈​s🙈​s🔄​f🔄​f🙈​s🙈​s
🔄​TestAccept/bundle/invariant/continue_293/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=synced_database_table.yml.tmpl✅​p✅​p🔄​f🔄​f
🟨​TestAccept/bundle/invariant/no_drift🙈​S🙈​S🟨​K💚​R🙈​S🙈​S🟨​K💚​R🙈​S🙈​S
❌​TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=❌​F✅​p✅​p✅​p
❌​TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=1❌​F✅​p✅​p✅​p
❌​TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=❌​F✅​p✅​p✅​p
❌​TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=1❌​F✅​p✅​p✅​p
🔄​TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=database_catalog.yml.tmpl/READPLAN=✅​p✅​p✅​p🔄​f
🔄​TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=synced_database_table.yml.tmpl/READPLAN=✅​p✅​p🔄​f🔄​f
❌​TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=synced_database_table.yml.tmpl/READPLAN=1✅​p✅​p❌​F🔄​f
🔄​TestAccept/bundle/resources/alerts/basic✅​p✅​p✅​p✅​p✅​p✅​p✅​p🔄​f✅​p✅​p
🔄​TestAccept/bundle/resources/alerts/basic/DATABRICKS_BUNDLE_ENGINE=terraform✅​p✅​p✅​p✅​p✅​p✅​p✅​p🔄​f✅​p✅​p
🔄​TestAccept/bundle/resources/apps/inline_config✅​p✅​p✅​p✅​p✅​p✅​p✅​p🔄​f✅​p✅​p
🔄​TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct✅​p✅​p✅​p✅​p✅​p✅​p✅​p🔄​f✅​p✅​p
❌​TestAccept/bundle/resources/clusters/deploy/data_security_mode✅​p✅​p❌​F❌​F✅​p✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/clusters/deploy/data_security_mode/DATABRICKS_BUNDLE_ENGINE=direct✅​p✅​p❌​F❌​F✅​p✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/clusters/deploy/simple✅​p✅​p❌​F✅​p✅​p✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/clusters/deploy/simple/DATABRICKS_BUNDLE_ENGINE=direct✅​p✅​p❌​F✅​p✅​p✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/clusters/deploy/update-after-create✅​p✅​p❌​F❌​F✅​p✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct✅​p✅​p❌​F❌​F✅​p✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform✅​p✅​p❌​F❌​F✅​p✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/clusters/lifecycle-started✅​p✅​p❌​F❌​F✅​p✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/clusters/lifecycle-started-toggle✅​p✅​p❌​F✅​p✅​p✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct✅​p✅​p❌​F✅​p✅​p✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct✅​p✅​p❌​F❌​F✅​p✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/clusters/resize-terminated-fallback✅​p✅​p❌​F❌​F✅​p✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/clusters/resize-terminated-fallback/DATABRICKS_BUNDLE_ENGINE=direct✅​p✅​p❌​F❌​F✅​p✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/dashboards/change-name✅​p✅​p❌​F❌​F✅​p🔄​f✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/dashboards/change-name/DATABRICKS_BUNDLE_ENGINE=terraform✅​p✅​p❌​F❌​F✅​p🔄​f✅​p✅​p✅​p✅​p
🔄​TestAccept/bundle/resources/pipelines/allow-duplicate-names✅​p✅​p✅​p✅​p✅​p✅​p✅​p✅​p🔄​f✅​p
🔄​TestAccept/bundle/resources/pipelines/allow-duplicate-names/DATABRICKS_BUNDLE_ENGINE=direct✅​p✅​p✅​p✅​p🔄​f✅​p✅​p✅​p🔄​f✅​p
🔄​TestAccept/bundle/resources/pipelines/allow-duplicate-names/DATABRICKS_BUNDLE_ENGINE=terraform✅​p✅​p✅​p✅​p🔄​f✅​p✅​p✅​p✅​p✅​p
🔄​TestAccept/bundle/resources/pipelines/auto-approve✅​p✅​p✅​p✅​p🔄​f✅​p✅​p✅​p✅​p✅​p
🔄​TestAccept/bundle/resources/pipelines/auto-approve/DATABRICKS_BUNDLE_ENGINE=direct✅​p✅​p✅​p✅​p🔄​f✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/postgres_projects/update_display_name🙈​s🙈​s✅​p❌​F🙈​s🙈​s🙈​s🙈​s🙈​s🙈​s
❌​TestAccept/bundle/resources/postgres_projects/update_display_name/DATABRICKS_BUNDLE_ENGINE=terraform✅​p❌​F
🟨​TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name🙈​S🙈​S🟨​K💚​R🙈​S🙈​S💚​R💚​R🙈​S🙈​S
❌​TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name/DATABRICKS_BUNDLE_ENGINE=direct❌​F✅​p✅​p✅​p
🙈​TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🔄​TestAccept/bundle/templates/default-python/combinations/classic🔄​f✅​p✅​p✅​p✅​p🔄​f✅​p✅​p✅​p✅​p
🔄​TestAccept/bundle/templates/default-python/combinations/classic/DATABRICKS_BUNDLE_ENGINE=terraform/DLT=yes/NBOOK=no/PY=yes/READPLAN=🔄​f✅​p✅​p✅​p✅​p🔄​f✅​p✅​p✅​p✅​p
🔄​TestAccept/bundle/templates/default-python/integration_classic/DATABRICKS_BUNDLE_ENGINE=direct/UV_PYTHON=3.11✅​p✅​p✅​p✅​p✅​p🔄​f✅​p✅​p✅​p✅​p
🔄​TestAccept/bundle/templates/default-python/integration_classic/DATABRICKS_BUNDLE_ENGINE=direct/UV_PYTHON=3.12✅​p✅​p✅​p✅​p✅​p🔄​f✅​p✅​p✅​p✅​p
🔄​TestAccept/bundle/templates/default-python/integration_classic/DATABRICKS_BUNDLE_ENGINE=direct/UV_PYTHON=3.9✅​p✅​p✅​p✅​p🔄​f✅​p✅​p✅​p✅​p✅​p
🔄​TestAccept/bundle/templates/default-python/integration_classic/DATABRICKS_BUNDLE_ENGINE=terraform/UV_PYTHON=3.11✅​p✅​p✅​p✅​p✅​p🔄​f✅​p✅​p✅​p✅​p
🔄​TestAccept/bundle/templates/default-python/integration_classic/DATABRICKS_BUNDLE_ENGINE=terraform/UV_PYTHON=3.13✅​p✅​p✅​p✅​p🔄​f🔄​f✅​p✅​p✅​p✅​p
🔄​TestAccept/selftest/record_cloud/pipeline-crud✅​p🔄​f✅​p✅​p✅​p✅​p✅​p✅​p✅​p✅​p
🔄​TestAccept/selftest/record_cloud/pipeline-crud/DATABRICKS_BUNDLE_ENGINE=direct✅​p🔄​f✅​p✅​p✅​p✅​p✅​p✅​p✅​p✅​p
🙈​TestAccept/ssh/connection🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
💚​TestFetchRepositoryInfoAPI_FromRepo💚​R💚​R💚​R💚​R💚​R💚​R💚​R💚​R💚​R💚​R
💚​TestFetchRepositoryInfoAPI_FromRepo/root💚​R💚​R💚​R💚​R💚​R💚​R💚​R💚​R💚​R💚​R
💚​TestFetchRepositoryInfoAPI_FromRepo/subdir💚​R💚​R💚​R💚​R💚​R💚​R💚​R💚​R💚​R💚​R
Top 50 slowest tests (at least 2 minutes):
durationenvtestname
14:04aws-ucws windowsTestAccept/bundle/resources/clusters/deploy/simple/DATABRICKS_BUNDLE_ENGINE=direct
12:33aws-ucws linuxTestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=direct
12:01gcp linuxTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
10:23aws-ucws windowsTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
9:18gcp windowsTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:54azure-ucws windowsTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:52azure-ucws linuxTestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=terraform
8:24aws-ucws linuxTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:09azure-ucws linuxTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:06gcp windowsTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:52aws linuxTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:37azure-ucws windowsTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:36aws-ucws windowsTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:26gcp windowsTestAccept/bundle/resources/clusters/deploy/local_ssd_count/DATABRICKS_BUNDLE_ENGINE=direct
7:19aws windowsTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:16aws-ucws windowsTestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=
7:16azure windowsTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:14aws-ucws windowsTestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=1
7:08aws windowsTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:05gcp linuxTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:04aws-ucws windowsTestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=1
6:58azure linuxTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
6:54aws-ucws windowsTestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=
6:53aws linuxTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
6:44azure-ucws windowsTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
6:37aws linuxTestAccept/bundle/resources/clusters/deploy/simple/DATABRICKS_BUNDLE_ENGINE=direct
6:29azure-ucws linuxTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
6:28gcp linuxTestAccept/bundle/resources/clusters/deploy/local_ssd_count/DATABRICKS_BUNDLE_ENGINE=direct
6:07aws-ucws linuxTestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=terraform
5:11aws linuxTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
5:08aws windowsTestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=direct
5:02azure linuxTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
4:59azure windowsTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
4:59gcp windowsTestAccept
4:57gcp linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:53gcp windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:43gcp windowsTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
4:39aws-ucws windowsTestAccept/bundle/resources/registered_models/basic/DATABRICKS_BUNDLE_ENGINE=terraform
4:39gcp linuxTestAccept/bundle/resources/apps/lifecycle-started-omitted/DATABRICKS_BUNDLE_ENGINE=direct
4:30gcp linuxTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
4:29azure-ucws linuxTestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=direct
4:26gcp windowsTestAccept/bundle/resources/apps/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
4:26gcp linuxTestAccept/bundle/resources/apps/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
4:23azure-ucws windowsTestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
4:20gcp windowsTestAccept/bundle/resources/apps/lifecycle-started-omitted/DATABRICKS_BUNDLE_ENGINE=direct
4:19azure-ucws windowsTestAccept/bundle/resources/apps/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
4:16gcp windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
4:05azure windowsTestAccept/bundle/resources/apps/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
3:57gcp linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:50aws windowsTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct

ZaSkittles pushed a commit to ZaSkittles/cli that referenced this pull request Jul 7, 2026
databricks#5823)
## Why
- The `local-env` feature needs a shared vocabulary before any behavior
can be built: the result shape, the error taxonomy, and how a compute
target maps to an environment key.
- Landing these contract types first lets every later layer (resolve /
fetch / merge / pipeline / command) depend on stable, reviewed
definitions.
- Kept deliberately minimal and dependency-free so it reviews on its own
and stays `unused`/`deadcode`-clean with no consumers yet.
## What
- **`result.go`** — the `--json` / `E_*` output contract: `Result`,
`PipelineError`, `ErrorCode`, `PhaseName`, `PhaseStatus`, `Mode`,
`TargetInfo`, `ResolvedInfo`, `Plan`, `Warning`; plus the command-path
constants (`local-env` / `python` / `sync`) defined in one place.
- **`envkey.go`** — `EnvKeyForServerless` / `EnvKeyForSparkVersion` /
`NormalizeServerless`, and `PythonMinorFromRequires` (clause-aware:
returns the effective highest lower bound of a `requires-python`).
- No wiring into `cmd/`, so the CLI is unchanged. Filesystem/artifact
constants and the phase-order slice deliberately live with their
consumer (PR 5).
## Testing strategy
- Unit tests for the error/type contract (`result_test.go`) and env-key
mapping incl. multi-clause / strict-`>` / no-floor `requires-python`
cases (`envkey_test.go`).
- Gates: `go build`, `go test`, `golangci-lint`, `deadcode`, `gofmt` —
all green.
- Reviewed with codex across several rounds to convergence (all findings
fixed or explicitly rejected as speculative).
---
## About this stack
This is one of a series of small, stacked PRs that together add the
`databricks local-env python sync` command — it provisions a local
Python environment (Python version, `databricks-connect` pin, and
dependency constraints) matched to a selected Databricks compute target.
The work was split from one large branch into single-concern layers so
each is independently reviewable; the command is kept hidden until the
final PR so nothing is user-visible mid-stack.
**Review bottom-up.** Each PR targets the previous one as its base
branch, so its diff shows only that layer.
| # | PR | What |
|---|----|------|
| 1 | **databricks#5823 ← you are here** | foundation: result types + env-key
mapping |
| 2 | databricks#5824 | compute-target resolution |
| 3 | databricks#5826 | constraint fetch + offline cache |
| 4 | databricks#5827 | formatting-preserving pyproject.toml merge |
| 5 | databricks#5828 | six-phase pipeline + detection + package-manager interface
|
| 6 | databricks#5832 | uv backend + CLI command (registered hidden) |
| 7 | databricks#5833 | acceptance tests |
| 8 | databricks#5835 | unveil (unhide + help + changelog) |
This pull request and its description were written by Isaac.
ZaSkittles pushed a commit to ZaSkittles/cli that referenced this pull request Jul 7, 2026
## Why
- `local-env` must turn the user's compute selection into a single
environment key before it can fetch anything, and the selection can come
from several places with a defined precedence.
- Isolating resolution behind a narrow seam keeps it testable without a
live workspace and keeps SDK details out of the engine.
## What
- **`target.go`** — `ResolveTarget` with ordered precedence `--cluster`
→ `--serverless` → `--job` → bundle target, producing a `TargetInfo` +
env key.
- Compute lookups go through the narrow `ComputeClient` interface
(stubbable in tests).
- `ValidateTargetFlags` rejects more than one target flag;
`ResolveTarget` runs it up front so a non-Cobra caller can't silently
resolve the wrong target.
- Classic-compute jobs read the Spark version from the documented first
return of `GetJobSparkVersion` (not the recorded-version third return).
## Testing strategy
- Unit tests against a stub `ComputeClient` covering each precedence
branch, the mutually-exclusive-flags error, and the job classic-compute
contract (`target_test.go`).
- Gates: `go build`, `go test`, `golangci-lint`, `deadcode`, `gofmt` —
all green.
- Reviewed with codex to a clean pass.
---
## About this stack
This is one of a series of small, stacked PRs that together add the
`databricks local-env python sync` command — it provisions a local
Python environment (Python version, `databricks-connect` pin, and
dependency constraints) matched to a selected Databricks compute target.
The work was split from one large branch into single-concern layers so
each is independently reviewable; the command is kept hidden until the
final PR so nothing is user-visible mid-stack.
**Review bottom-up.** Each PR targets the previous one as its base
branch, so its diff shows only that layer.
| # | PR | What |
|---|----|------|
| 1 | databricks#5823 | foundation: result types + env-key mapping |
| 2 | **databricks#5824 ← you are here** | compute-target resolution |
| 3 | databricks#5826 | constraint fetch + offline cache |
| 4 | databricks#5827 | formatting-preserving pyproject.toml merge |
| 5 | databricks#5828 | six-phase pipeline + detection + package-manager interface
|
| 6 | databricks#5832 | uv backend + CLI command (registered hidden) |
| 7 | databricks#5833 | acceptance tests |
| 8 | databricks#5835 | unveil (unhide + help + changelog) |
This pull request and its description were written by Isaac.
ZaSkittles pushed a commit to ZaSkittles/cli that referenced this pull request Jul 7, 2026
…icks#5826)
## Why
- Once a target resolves to an env key, `local-env` needs the pinned
Python version, `databricks-connect` version, and dependency constraints
published for that key.
- The fetch must degrade gracefully offline and distinguish “this
environment isn't published” from “the network is down,” because those
call for different user action.
- The artifact host must be a Databricks-owned, access-controlled
location and must never default to a personal repo — whoever controls
the host controls what the CLI installs.
## What
- **`constraints.go`** — fetches the per-environment `pyproject.toml`,
parses `requires-python`, the `databricks-connect` pin, and `[tool.uv]`
`constraint-dependencies`, and caches it on disk.
- **Host parameterization** — no host is hardcoded:
`RepoConstraintBaseURL` reads the repo (`owner/name`) from the temporary
`DATABRICKS_LOCALENV_CONSTRAINT_REPO` env var and builds a
`raw.githubusercontent.com/<repo>/main` URL; the built-in default is
empty. When unset it returns `""` and `FetchConstraints` reports the
missing source as a fetch-phase `E_FETCH` error (so there is no
untrusted default, and the failure flows through the normal phase/JSON
reporting). Once `databricks/environments` can publish, that becomes the
hardcoded default and the env var is no longer required.
- Failure classification: **404** → `E_ENV_UNSUPPORTED` (no cache
fallback — a distinct non-transient condition); **transport / non-404**
→ `E_FETCH` with fallback to the last-good cached copy.
- Robustness: validate the body (parse + require `requires-python`)
**before** caching so a bad 2xx can't poison the cache; atomic cache
write (mkdir + temp-file + rename); dedicated `http.Client` with a 30s
timeout; body read bounded by `io.LimitReader` at 1 MiB;
`databricks-connect` matched by leading package name under PEP 503
normalization (so `Databricks_Connect` matches, `databricks-connectors`
does not); cache filename = readable slug + sha256 suffix to prevent
collisions.
## Testing strategy
- Unit tests with an `httptest` server: 200-parse, 404 →
`E_ENV_UNSUPPORTED`, transport failure + cache fallback,
missing-`requires-python` rejection, PEP 503 name matching, cache-dir
creation, collision-free filenames, oversized-body rejection
(`constraints_test.go`).
- Host resolution: `TestRepoConstraintBaseURL` (env var → URL, unset →
`""`, whitespace treated as unset) and
`TestFetchConstraintsNoSourceConfigured` (empty host → `E_FETCH` naming
the env var).
- Gates: `go build`, `go test`, `golangci-lint`, `deadcode`, `gofmt` —
all green.
- Reviewed with codex to a clean pass (several fetch/cache edge-case
fixes landed from review).
---
## About this stack
This is one of a series of small, stacked PRs that together add the
`databricks local-env python sync` command — it provisions a local
Python environment (Python version, `databricks-connect` pin, and
dependency constraints) matched to a selected Databricks compute target.
The work was split from one large branch into single-concern layers so
each is independently reviewable; the command is kept hidden until the
final PR so nothing is user-visible mid-stack.
**Review bottom-up.** Each PR targets the previous one as its base
branch, so its diff shows only that layer.
| # | PR | What |
|---|----|------|
| 1 | databricks#5823 | foundation: result types + env-key mapping |
| 2 | databricks#5824 | compute-target resolution |
| 3 | **databricks#5826 ← you are here** | constraint fetch + offline cache |
| 4 | databricks#5827 | formatting-preserving pyproject.toml merge |
| 5 | databricks#5828 | six-phase pipeline + detection + package-manager interface
|
| 6 | databricks#5832 | uv backend + CLI command (registered hidden) |
| 7 | databricks#5833 | acceptance tests |
| 8 | databricks#5835 | unveil (unhide + help + changelog) |
This pull request and its description were written by Isaac.
swearyangupta pushed a commit to swearyangupta/cli that referenced this pull request Jul 17, 2026
…ks#5832)
## Why
- The engine needs a real `PackageManager` implementation (uv) and a CLI
entry point so a user can actually run the feature.
- Wiring it in while keeping it `Hidden` lets the command be dogfooded
and exercised by acceptance tests without becoming user-visible until
the stack is complete.
- This is the first layer reachable from `main`, so it also makes the
whole `libs/localenv` package live for the `deadcode` checker.
## What
- **`libs/localenv/uv.go`** — the uv implementation of `PackageManager`:
discover/install uv, install the Python minor, `uv sync`, seed pip into
the venv, validate; plus the `pip.conf` → `UV_INDEX_URL` bridge for
Databricks-managed machines.
- **`cmd/localenv/`** — the command tree matching `local-env python
sync`: a `local-env` group (`Hidden: true`), a `python` subgroup, and
the `sync` verb. Parent nodes use `root.ReportUnknownSubcommand`; `sync`
uses `cobra.NoArgs`, resolves flags/bundle target, builds the `Pipeline`
with the uv manager, and renders text or `--json`. All Cobra `Use`
values + the `--json` command field come from the `libs/localenv`
constants.
- **`cmd/cmd.go`** — registers the group.
## Testing strategy
- Unit tests for uv helper logic (discovery, `pip.conf` index-url, arg
builders, stderr surfacing) (`uv_test.go`).
- Runtime smoke test of the hidden 3-level command: absent from
top-level help; help works at each level; unknown subcommand exits
non-zero; bare group shows help; flags + mutual-exclusion + `NoArgs`
behave.
- Gates: `go build ./...`, `go test`, `golangci-lint`, `deadcode` (whole
tree, no pragmas), `gofmt` — all green.
- `cmd/localenv/` unit tests are intentionally deferred to the
acceptance PR (databricks#5833), per the repo convention that user-visible CLI
output is covered by acceptance tests.
---
## About this stack
This is one of a series of small, stacked PRs that together add the
`databricks local-env python sync` command — it provisions a local
Python environment (Python version, `databricks-connect` pin, and
dependency constraints) matched to a selected Databricks compute target.
The work was split from one large branch into single-concern layers so
each is independently reviewable; the command is kept hidden until the
final PR so nothing is user-visible mid-stack.
**Review bottom-up.** Each PR targets the previous one as its base
branch (this PR targets `main`), so its diff shows only that layer.
Layers 1–5 have merged; the original layer-5 PR (databricks#5828) was split into
5a/5b/5c during review.
| # | PR | What | Status |
|---|----|------|--------|
| 1 | databricks#5823 | foundation: result types + env-key mapping | merged |
| 2 | databricks#5824 | compute-target resolution | merged |
| 3 | databricks#5826 | constraint fetch + offline cache | merged |
| 4 | databricks#5827 | formatting-preserving pyproject.toml merge | merged |
| 5a | databricks#5850 | package-manager interface + detection | merged |
| 5b | databricks#5851 | six-phase pipeline orchestrator | merged |
| 5c | databricks#5854 | --check cache purity, greenfield name, dbc insertion |
merged |
| 6 | **databricks#5832 ← you are here** | uv backend + CLI command (registered
hidden) | |
| 7 | databricks#5833 | acceptance tests | |
| 8 | databricks#5835 | unveil (unhide + help + changelog) | |
This pull request and its description were written by Isaac.
swearyangupta pushed a commit to swearyangupta/cli that referenced this pull request Jul 17, 2026
## Why
- The command's user-visible behavior — text and `--json` output, and
every error path — needs end-to-end coverage against the real CLI.
- `cmd/localenv/` carries no unit tests by design, so acceptance tests
are where that surface is verified (repo convention: user-visible CLI
output is covered by acceptance tests).
## What
- **`acceptance/localenv/`** — 9 scenarios driven through the (hidden)
command against the in-process fake server: `help` (three-level tree),
`no-target` (`E_NO_TARGET`), `flag-conflict` (Cobra mutual-exclusion),
`manager-unsupported` (conda project → clean P1 exit), `env-unsupported`
(404 → `E_ENV_UNSUPPORTED` at fetch), `json-error` (`--output json`
error object), `serverless-check` (dry-run plan), `serverless-json`
(`--json` plan), and `constraints-only`.
- Scripts use `local-env python sync` and the
`DATABRICKS_LOCALENV_CONSTRAINT_SOURCE` override; goldens show the
`local-env python sync` command field and managed marker. No source
changes.
## Testing strategy
- Goldens generated with `-update` and verified **stable on a clean
re-run** (no `-update`); all 9 subtests pass.
- `musterr` guards the five expected-failure scenarios; `trace` shows
the three output-producing ones.
- Full acceptance suite run to confirm no regressions elsewhere (only
pre-existing, environment-specific failures unrelated to this change).
- Diff confined to `acceptance/localenv/`.
- Independently verified by a review subagent (PASS — goldens, scripts,
stubs, stale-refs, hygiene) and by codex (no issues).
---
## About this stack
This is one of a series of small, stacked PRs that together add the
`databricks local-env python sync` command — it provisions a local
Python environment (Python version, `databricks-connect` pin, and
dependency constraints) matched to a selected Databricks compute target.
The work was split from one large branch into single-concern layers so
each is independently reviewable; the command is kept hidden until the
final PR so nothing is user-visible mid-stack.
**Review bottom-up.** Layers 1–5 have merged (the original layer-5 PR
databricks#5828 was split into 5a/5b/5c during review).
| # | PR | What | Status |
|---|----|------|--------|
| 1 | databricks#5823 | foundation: result types + env-key mapping | merged |
| 2 | databricks#5824 | compute-target resolution | merged |
| 3 | databricks#5826 | constraint fetch + offline cache | merged |
| 4 | databricks#5827 | formatting-preserving pyproject.toml merge | merged |
| 5a | databricks#5850 | package-manager interface + detection | merged |
| 5b | databricks#5851 | six-phase pipeline orchestrator | merged |
| 5c | databricks#5854 | --check cache purity, greenfield name, dbc insertion |
merged |
| 6 | databricks#5832 | uv backend + CLI command (registered hidden) | |
| 7 | **databricks#5833 ← you are here** | acceptance tests | |
| 8 | databricks#5835 | unveil (unhide + help + changelog) | |
This pull request and its description were written by Isaac.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@rugpanov@eng-dev-ecosystem-bot@anton-107