Skip to content

Unveil environments setup-local: make the command visible - #5835

Merged
rugpanov merged 1 commit into
mainfrom
dbconnect/08-unveil
Aug 11, 2026
Merged

Unveil environments setup-local: make the command visible#5835
rugpanov merged 1 commit into
mainfrom
dbconnect/08-unveil

Conversation

@rugpanov

@rugpanov rugpanov commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Changes

Unveils the databricks environments setup-local command: removes Hidden: true from cmd/environments/sync.go so it appears in environments --help and shell completion, and adds a changelog fragment announcing it.

Why

The command was kept hidden while the feature landed across the VPEX stack and while its constraint-artifact repo was private. databricks/environments is now public and the command's source is hardcoded to it (#6136), so the command works end-to-end for users and is ready to expose.

Tests

  • go build + golangci-lint clean on cmd/environments.
  • ./task check-changelog passes.
  • localenv + help acceptance suites pass with no golden changes (-update produces no diff): the top-level help lists command groupsenvironments is a generated API group and is always shown — and the command's own --help golden (acceptance/localenv/help) was already captured while hidden.
  • Manually verified databricks environments --help now lists setup-local, and end-to-end provisioning against the public repo works (serverless dry-run + real provision, merge into an existing / bundle init project).

Note: this branch was rebuilt on current main. The original 5 commits predated the targetcompute rename (#6100), the cmd/localenvcmd/environments rename, and the hardcoded-source change (#6136); four of them only added acceptance tests that already exist on main, and the fifth edited a since-renamed file. The unveil now collapses to this single focused commit.

This PR was written by Claude Code.

@eng-dev-ecosystem-bot

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

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: b6a88d8

Run: 31406211812

Env 🔄​flaky 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
🔄​ aws linux 1 4 4 293 1123 12:31
💚​ aws windows 4 4 296 1121 8:20
💚​ azure linux 4 4 293 1123 11:23
💚​ azure windows 4 4 295 1121 8:37
💚​ gcp linux 1 5 294 1123 12:14
💚​ gcp windows 1 5 296 1121 8:11
9 interesting tests: 4 RECOVERED, 4 SKIP, 1 flaky
Test Name aws linux aws windows azure linux azure windows gcp linux gcp windows
💚​ TestAccept 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/invariant/no_drift 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🔄​ TestSyncIncrementalFileOverwritesFolder 🔄​f ✅​p ✅​p ✅​p ✅​p ✅​p
💚​ TestFetchRepositoryInfoAPI_FromRepo 💚​R 💚​R 💚​R 💚​R 🙈​S 🙈​S
💚​ TestFetchRepositoryInfoAPI_FromRepo/root 💚​R 💚​R 💚​R 💚​R
💚​ TestFetchRepositoryInfoAPI_FromRepo/subdir 💚​R 💚​R 💚​R 💚​R
Top 14 slowest tests (at least 2 minutes):
duration env testname
4:18 gcp windows TestAccept
3:49 azure windows TestAccept
3:13 aws windows TestAccept
3:04 gcp linux TestAccept
2:58 aws linux TestAccept
2:53 azure linux TestAccept
2:43 azure windows TestFilerWorkspaceFilesExtensionsRead
2:41 gcp windows TestFilerWorkspaceNotebook/scalaJupyterNb.ipynb
2:34 azure windows TestFilerWorkspaceFilesExtensionsReadDir
2:29 aws linux TestFilerWorkspaceFilesExtensionsRead
2:22 gcp linux TestFilerWorkspaceFilesExtensionsReadDir
2:18 gcp linux TestImportDir
2:08 gcp linux TestFilerRecursiveDelete/workspace_files_extensions
2:04 azure linux TestFilerRecursiveDelete/workspace_files

@rugpanov
rugpanov force-pushed the dbconnect/07-acceptance branch from 6c2fadc to c45ca0c Compare July 6, 2026 11:47
@rugpanov
rugpanov force-pushed the dbconnect/08-unveil branch from 3123548 to 8008a65 Compare July 6, 2026 11:47
@rugpanov
rugpanov temporarily deployed to test-trigger-is July 6, 2026 11:48 — with GitHub Actions Inactive
@rugpanov
rugpanov temporarily deployed to test-trigger-is July 6, 2026 11:48 — with GitHub Actions Inactive
@rugpanov
rugpanov force-pushed the dbconnect/07-acceptance branch from c45ca0c to 25d07fe Compare July 7, 2026 08:35
@rugpanov
rugpanov force-pushed the dbconnect/08-unveil branch from 8008a65 to 27f8064 Compare July 7, 2026 08:35
@rugpanov
rugpanov temporarily deployed to test-trigger-is July 7, 2026 08:35 — with GitHub Actions Inactive
@rugpanov
rugpanov temporarily deployed to test-trigger-is July 7, 2026 08:35 — with GitHub Actions Inactive
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.
ZaSkittles pushed a commit to ZaSkittles/cli that referenced this pull request Jul 7, 2026
…atabricks#5827)

## 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.

| # | PR | What |
|---|----|------|
| 1 | databricks#5823 | foundation: result types + env-key mapping |
| 2 | databricks#5824 | compute-target resolution |
| 3 | databricks#5826 | constraint fetch + offline cache |
| 4 | **databricks#5827 ← you are here** | 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.
@rugpanov
rugpanov force-pushed the dbconnect/07-acceptance branch from 25d07fe to da5bf75 Compare July 14, 2026 11:51
@rugpanov
rugpanov force-pushed the dbconnect/08-unveil branch from 27f8064 to 8fb5ea2 Compare July 14, 2026 11:52
@rugpanov
rugpanov temporarily deployed to test-trigger-is July 14, 2026 11:52 — with GitHub Actions Inactive
@rugpanov
rugpanov temporarily deployed to test-trigger-is July 14, 2026 11:52 — with GitHub Actions Inactive
@rugpanov
rugpanov force-pushed the dbconnect/07-acceptance branch from da5bf75 to b537940 Compare July 14, 2026 12:50
@rugpanov
rugpanov force-pushed the dbconnect/08-unveil branch from 8fb5ea2 to d3c1357 Compare July 14, 2026 12:50
@rugpanov
rugpanov temporarily deployed to test-trigger-is July 14, 2026 12:51 — with GitHub Actions Inactive
@rugpanov
rugpanov force-pushed the dbconnect/07-acceptance branch from c6d4042 to 58b80d9 Compare July 16, 2026 11:31
@rugpanov
rugpanov force-pushed the dbconnect/08-unveil branch from 3bea91d to 43f5af9 Compare July 16, 2026 11:32
@rugpanov

Copy link
Copy Markdown
Contributor Author

Do not merge until databricks/environments is publicly available and exposed.

This PR unveils local-env — it removes Hidden: true, adds the command to root --help/completion, and ships the user-facing changelog entry. Once merged, the command becomes discoverable to all users.

But the command fetches its Python version / databricks-connect pin / dependency constraints from the databricks/environments repo (via the default constraint base URL). Until that repo is public and serving the pyproject.toml files, an unveiled command would be discoverable but non-functional for users without the constraint-source override.

Holding in draft until the environments repo is live. The underlying command already works via dogfooding (hidden) on main today; this is purely the public-exposure gate.

@rugpanov
rugpanov marked this pull request as draft July 16, 2026 12:47
Base automatically changed from dbconnect/07-acceptance to main July 16, 2026 12:55
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
…ts (databricks#5936)

Follow-up refinements from the
[databricks#5832](databricks#5832) review (all were
left as non-blocking there and deferred with a "will address in a
follow-up" reply). The `local-env` command remains hidden until databricks#5835,
so there is no user-visible changelog entry.

## Changes

- **uv install consent** (`libs/localenv/uv.go`) — `EnsureAvailable` no
longer runs the remote uv installer (`curl … | sh` / `irm … | iex`)
silently. It now requires consent: a truthy
`DATABRICKS_LOCALENV_AUTO_INSTALL_UV` opt-in for non-interactive runs
(CI/IDE), or an interactive `y/N` prompt via `cmdio.AskYesOrNo`. A
non-interactive run without the opt-in returns an actionable error
instead of downloading and executing an installer. (The `--debug` log of
the exact installer command from databricks#5832 is kept.)
- **serverless job version** (`cmd/localenv/compute.go`) —
`GetJobSparkVersion` now reads
`Environments[0].Spec.EnvironmentVersion`, so a serverless `--job`
resolves to its actual `serverless-vN` instead of always defaulting to
v4. Empty still falls back to v4.
- **double bundle load** (`cmd/localenv/sync.go`) — skip `bundleTarget`
when an explicit `--cluster/--serverless/--job` flag is set.
`ResolveTarget` only consults the bundle as a fallback, so the second
`TryConfigureBundle` load (and its re-printed load-time diagnostics) was
wasted for the explicit-flag case.
- **robust `Validate` parse** (`libs/localenv/uv.go`) — the `uv run`
probe now prints `PYVER:` / `DBCVER:` sentinels and parses by prefix
instead of line position, so a stray stdout line from uv doesn't shift
the parse.
- **cleanup** (`libs/localenv/uv.go`) — fold single-caller
`newUvManager` into `NewUvManager`; collapse the triplicated
`resolveIndexURL` + conditional `WithEnv` into one `runUv` helper (the
conditional stays so an already-set `UV_INDEX_URL` isn't clobbered).

## Tests

Adds unit tests for the install consent gate (`TestConfirmUvInstall`)
and the sentinel parse (`TestLineWithPrefix`). Full `libs/localenv`
suite passes, including under a CI-like environment with
`UV_INDEX_URL`/`PIP_INDEX_URL` set.

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.
@rugpanov rugpanov changed the title [VPEX][8/8] Unveil local-env: make the command visible, add help + changelog [VPEX final] Unveil environments setup-local: make the command visible, add help + changelog Jul 17, 2026
tanishgupta-db pushed a commit to tanishgupta-db/cli that referenced this pull request Jul 21, 2026
…correct package (databricks#5959)

## What

Renames the local-environment command from `databricks local-env python
sync` to
**`databricks environments setup-local`** and moves it into the existing
`environments` command group, per the updated `[P0] CLI Changes` spec.

The `environments` group intentionally spans both server-side
environment-resource
APIs and local provisioning, so a local-install verb belongs there
rather than in a
standalone top-level group.

## Why here (package placement)

`cmd/workspace/environments/environments.go` is generated (`DO NOT
EDIT`), so the
command is attached the same way `cmd/apps` extends the generated `apps`
group:

- **`cmd/environments/`** (hand-written, moved from `cmd/localenv/`)
exposes a
  `Commands()` function returning the `setup-local` verb.
- **`cmd/workspace/environments/overrides.go`** (new, non-generated) has
an `init()`
that appends to the generated group's `cmdOverrides` hook, attaching the
command.
- The standalone `cli.AddCommand(localenv.New())` is dropped from
`cmd/cmd.go`.

## Changes

- **Command tree:** `local-env python sync` → `environments
setup-local`. The `python`
subgroup is removed — P0 is Python-only with no language selector (a
language axis
like `setup-local python` would be additive later; nothing is reserved
now).
- **Constants:** `CommandGroup`/`CommandVerb`/`CommandName` updated in
`libs/localenv/result.go`; JSON `command` field is now `"environments
setup-local"`.
- **Managed markers:** the `pyproject.toml` managed-block markers now
derive from
`CommandName` (they previously hard-coded the old name and are written
into user
  files), so the command name lives in exactly one place.
- **Still hidden:** the command remains `Hidden` until the environment
constraints
  repository is public — unchanged behavior from before the rename.
- Regenerated acceptance goldens + help output.

## Out of scope (deliberate)

- **Flag renames** (`--cluster` → `--cluster-id`, `--serverless` →
`--serverless-version`,
  `--job` → `--job-id`, `--check` → `--dry-run`, `--constraint-source` →
`--constraint-source-url`) — the spec renames these too, but they are a
separate
  follow-up to keep this PR to the command rename + package move.
- The `libs/localenv` package name and `acceptance/localenv/` directory
name are left
  as-is (internal, not user-visible; renaming is cosmetic churn).

## Interaction with the stack

databricks#5835 (`[VPEX][8/8]`, held in draft until the constraints repo is
public) unhides and
documents this command under its **old** name. Whichever lands second
must reconcile:
the changelog fragment and the `Hidden` flag should reflect
`environments setup-local`.

## Testing

- `go build ./...`, lint (0 issues), `deadcode` clean
- `libs/localenv` + `cmd/environments` unit tests pass
- `acceptance/localenv` + `acceptance/help` regenerated and green
- Verified `databricks environments setup-local` is runnable, appears
under the
`environments` group when unhidden, and stays out of help while `Hidden`

This pull request and its description were written by Isaac.
@rugpanov
rugpanov force-pushed the dbconnect/08-unveil branch from 43f5af9 to 85a944e Compare August 4, 2026 08:18
@rugpanov rugpanov changed the title [VPEX final] Unveil environments setup-local: make the command visible, add help + changelog Unveil environments setup-local: make the command visible Aug 4, 2026
@rugpanov
rugpanov force-pushed the dbconnect/08-unveil branch from 85a944e to b7115f7 Compare August 4, 2026 08:24
yolocs pushed a commit to yolocs/dbcli that referenced this pull request Aug 4, 2026
databricks#6136)

## Changes
The environment constraint artifacts now live in the public
`databricks/environments` repo, so the CLI no longer needs a
configurable source:

- Hardcode the default base URL
`https://raw.githubusercontent.com/databricks/environments/main/python`
(anchored at the `python/` subtree where the artifacts live).
- Remove the hidden `--constraint-source-url` flag and the
`DATABRICKS_LOCALENV_CONSTRAINT_REPO` (owner/name) env var.
- Replace `RepoConstraintBaseURL` with `ConstraintBaseURL(ctx)`, which
returns the hardcoded default and still honors a single full-URL
override env var — renamed to
`DATABRICKS_LOCALENV_CONSTRAINT_SOURCE_URL_TEST_OVERRIDE` so it reads as
test-only / power-user, not a supported knob.
- Update the acceptance `test.toml` files to the renamed override var.

The command stays `Hidden: true`; unhiding it (help, changelog,
completion) is the separate unveil change (databricks#5835).

## Why
The repo publishing its constraint artifacts was the precondition for a
non-empty default. Now that it is public, the empty-default +
owner/name-repo plumbing (needed only while the artifacts lived in a
private/personal repo) is dead weight, and a real default is safe to
ship.

## Tests
- `go test ./libs/localenv/... ./cmd/environments/...` and the
`localenv`/`help` acceptance suites pass (no golden changes — the
override value is unchanged and the removed flag was already hidden).
- `TestConstraintBaseURL` now covers the hardcoded default + override;
verified the published `serverless-v5` artifact resolves (`HTTP 200`) at
the hardcoded URL.
- `golangci-lint` and `deadcode` clean; full `go build ./...` green.

_This PR was written by Claude Code._
@rugpanov
rugpanov force-pushed the dbconnect/08-unveil branch from b7115f7 to 0f7db2c Compare August 10, 2026 12:53
@rugpanov
rugpanov marked this pull request as ready for review August 10, 2026 13:46
The environment constraint artifacts are now published in the public
databricks/environments repo and the command's source is hardcoded to it
(#6136), so the command is ready for users. Remove Hidden:true so
`databricks environments setup-local` appears in `environments --help`
and completion, and add a changelog fragment announcing it.

Adds an acceptance test (localenv/group-help) that asserts setup-local is
listed under the generated environments group — the visibility this change
unveils. It greps a single line rather than snapshotting the whole group
help, which also lists generated API subcommands that churn on SDK regen.
No existing golden changes: the top-level help lists command groups
(environments is always shown), and the command's own --help golden was
already captured while it was hidden.

Co-authored-by: Isaac
@rugpanov
rugpanov force-pushed the dbconnect/08-unveil branch from 0f7db2c to b6a88d8 Compare August 10, 2026 15:54

@anton-107 anton-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.

Reviewed at b6a88d8. Approving the change itself — it's clean and the new test is a real guard. One sequencing caveat below that I'd weigh before actually hitting merge; it's about something outside this PR, not a defect in it.

The acceptance test is genuinely load-bearing. I didn't want to assume trace ... | grep fails correctly, so I mutation-tested it: re-added Hidden: true, re-ran, and got the right failure —

Error: Not equal:
expected: "\n>>> [CLI] environments --help\n  setup-local ... "
actual  : "\n>>> [CLI] environments --help\n\nExit code: 1\n"

then restored and confirmed green. The framework runs scripts under bash -euo pipefail, so grep's exit 1 propagates and the golden picks up an Exit code: 1 line. Good call grepping one line instead of snapshotting group help that churns on SDK regen. Full TestAccept/(localenv|help) = 54 passed. out.test.toml matches every sibling directory under acceptance/localenv/, so it's conventional rather than stray.

Readiness checks all came back clean: no TODOs or experimental markers in the command path, no dogfooding references leaking into user-facing text, no debug-only flags, help text reads appropriately for a first-time user, and no stale Hidden-era comment left behind.

The caveat: one thing to fix upstream first

Unveiling promotes a reporting bug from dogfood-only to user-visible. libs/localenv/pipeline.go:551 versionFromPin derives the reported dbconnectVersion by taking everything from the first digit of the published pin string, so a pin of ~=17.3.0 reports 17.3.0 (correct today) but a bare-major pin like ~=17.0 would report "17.0" — not a version anything installs. In --dry-run nothing corrects it, because validate is stubbed at pipeline.go:203-211; on a real run validate overwrites it with the truth at pipeline.go:462-467. So dry-run and real runs would disagree, and the --output json contract the VS Code extension consumes carries the wrong value.

That's latent right now. It becomes live if the pending databricks/environments PR 15 (bare-major serverless pins) lands. I've asked for changes there and suggested hardening versionFromPin regardless — a pin string is not a version, and today nothing on either side of that cross-repo contract tests the coupling. My suggestion is to land that fix before flipping this on, so the first users to see this command don't see a wrong version.

Minor, non-blocking: merging this ahead of the setup-local telemetry PR means early adopters generate no adoption data — the exact metric that PR exists to capture. If the ordering is easy to swap, telemetry first is slightly better.

Reviewed with Claude Code; all claims above were verified against the branch.

@rugpanov

Copy link
Copy Markdown
Contributor Author

@anton-107 thank you for the review. Very good finding with dry-run functionality. Reported an internal ticket for it and will work on it as a fast follow-up.

@rugpanov
rugpanov added this pull request to the merge queue Aug 11, 2026
Merged via the queue into main with commit 74a3042 Aug 11, 2026
26 checks passed
@rugpanov
rugpanov deleted the dbconnect/08-unveil branch August 11, 2026 08:08
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 74a3042

Run: 31471965813

Env ❌​FAIL 🟨​KNOWN 🔄​flaky 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
❌​ aws linux 6 1 6 5 2 1159 931 206:54
❌​ aws windows 6 1 4 4 2 1101 950 238:49
💚​ azure linux 6 2 1065 968 177:32
💚​ azure windows 6 2 1004 987 202:32
🔄​ gcp linux 3 2 3 1053 972 216:54
💚​ gcp windows 3 3 994 991 226:52
25 interesting tests: 12 flaky, 6 FAIL, 4 RECOVERED, 2 SKIP, 1 KNOWN
Test Name aws linux aws windows azure linux azure windows gcp linux gcp windows
🟨​ TestAccept 🟨​K 🟨​K 💚​R 💚​R 🔄​f 💚​R
🔄​ TestAccept/bundle/invariant/no_drift 💚​R 🔄​f 💚​R 💚​R 💚​R 💚​R
🔄​ TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=pipeline_allow_duplicate_names.yml.tmpl/READPLAN= ✅​p 🔄​f ✅​p ✅​p ✅​p ✅​p
🔄​ TestAccept/bundle/resources/grants/catalogs 🔄​f ✅​p ✅​p ✅​p ✅​p ✅​p
🔄​ TestAccept/bundle/resources/grants/catalogs/DATABRICKS_BUNDLE_ENGINE=direct 🔄​f ✅​p ✅​p ✅​p ✅​p ✅​p
❌​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions ❌​F ❌​F 🙈​s 🙈​s 🙈​s 🙈​s
❌​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=direct ❌​F ❌​F
❌​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=terraform ❌​F ❌​F
❌​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions ❌​F ❌​F 🙈​s 🙈​s 🙈​s 🙈​s
❌​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=direct ❌​F ❌​F
❌​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=terraform ❌​F ❌​F
🔄​ TestAccept/bundle/resources/postgres_roles/replace_existing 🔄​f ✅​p 🙈​s 🙈​s 🙈​s 🙈​s
🔄​ TestAccept/bundle/resources/postgres_roles/replace_existing/DATABRICKS_BUNDLE_ENGINE=direct 🔄​f ✅​p
🔄​ TestAccept/bundle/resources/postgres_roles/update 🔄​f ✅​p 🙈​s 🙈​s 🙈​s 🙈​s
🔄​ TestAccept/bundle/resources/postgres_roles/update/DATABRICKS_BUNDLE_ENGINE=direct 🔄​f ✅​p
🔄​ TestAccept/bundle/resources/quality_monitors/change_assets_dir ✅​p ✅​p ✅​p ✅​p 🔄​f ✅​p
🔄​ TestAccept/bundle/resources/quality_monitors/change_assets_dir/DATABRICKS_BUNDLE_ENGINE=direct ✅​p ✅​p ✅​p ✅​p 🔄​f ✅​p
🔄​ TestAccept/bundle/resources/registered_models/basic/DATABRICKS_BUNDLE_ENGINE=direct ✅​p 🔄​f ✅​p ✅​p ✅​p ✅​p
🔄​ TestAccept/bundle/resources/registered_models/basic/DATABRICKS_BUNDLE_ENGINE=terraform ✅​p 🔄​f ✅​p ✅​p ✅​p ✅​p
💚​ TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
💚​ TestFetchRepositoryInfoAPI_FromRepo 💚​R 💚​R 💚​R 💚​R 🙈​S 🙈​S
💚​ TestFetchRepositoryInfoAPI_FromRepo/root 💚​R 💚​R 💚​R 💚​R
💚​ TestFetchRepositoryInfoAPI_FromRepo/subdir 💚​R 💚​R 💚​R 💚​R
Top 50 slowest tests (at least 2 minutes):
duration env testname
15:29 gcp windows TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
15:21 gcp linux TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
15:20 gcp linux TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
11:59 azure linux TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
9:56 aws windows TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:37 aws linux TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:30 aws windows TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:20 azure windows TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:01 azure windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
7:50 aws linux TestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=terraform
7:46 aws linux TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:45 azure windows TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:42 gcp linux TestAccept/bundle/resources/clusters/deploy/data_security_mode/DATABRICKS_BUNDLE_ENGINE=direct
7:33 azure windows TestAccept/bundle/resources/clusters/deploy/data_security_mode/DATABRICKS_BUNDLE_ENGINE=direct
7:32 gcp windows TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:29 gcp windows TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DATABRICKS_BUNDLE_ENGINE=direct
7:26 gcp windows TestAccept
7:24 gcp linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
7:21 azure windows TestAccept
7:11 gcp linux TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DATABRICKS_BUNDLE_ENGINE=direct
7:09 aws linux TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
7:06 azure linux TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
6:52 azure windows TestAccept/bundle/resources/clusters/deploy/simple/DATABRICKS_BUNDLE_ENGINE=direct
6:45 gcp linux TestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=terraform
6:41 gcp linux TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
6:27 azure linux TestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=terraform
6:07 azure windows TestAccept/bundle/invariant/destroy_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=
6:01 azure linux TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
5:57 gcp linux TestAccept/bundle/resources/apps/lifecycle-started-omitted/DATABRICKS_BUNDLE_ENGINE=direct
5:56 gcp linux TestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=direct
5:55 azure linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
5:55 aws windows TestAccept/bundle/resources/apps/lifecycle-started-omitted/DATABRICKS_BUNDLE_ENGINE=direct
5:49 gcp windows TestAccept/bundle/resources/apps/lifecycle-started-omitted/DATABRICKS_BUNDLE_ENGINE=direct
5:29 aws linux TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=direct
5:21 gcp windows TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=direct
5:21 gcp windows TestAccept/bundle/resources/apps/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
5:21 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
5:16 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
5:13 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
5:11 aws windows TestAccept/bundle/templates/default-python/integration_classic/DATABRICKS_BUNDLE_ENGINE=terraform/UV_PYTHON=3.11
5:01 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:58 gcp windows TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
4:54 gcp linux TestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=1
4:52 aws windows TestAccept/bundle/templates/default-python/integration_classic/DATABRICKS_BUNDLE_ENGINE=direct/UV_PYTHON=3.12
4:51 azure linux TestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=direct
4:51 gcp linux TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=direct
4:39 gcp linux TestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=pipeline.yml.tmpl/READPLAN=1
4:38 gcp windows TestAccept/bundle/templates/default-python/combinations/serverless/DATABRICKS_BUNDLE_ENGINE=terraform/DLT=no/NBOOK=no/PY=yes/READPLAN=
4:36 azure linux TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
4:35 gcp windows TestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=job_apply_policy_default_values_for_each_task.yml.tmpl/READPLAN=

yolocs pushed a commit to yolocs/dbcli that referenced this pull request Aug 11, 2026
… setup-local --dry-run (databricks#6218)

## Summary

Found in code review of the unveil PR (databricks#5835). P1, not P0: it only
affects `--dry-run`; real runs are correct.

`versionFromPin` in `libs/localenv/pipeline.go` derives the reported
`dbconnectVersion` by taking everything from the first digit of the
published pin string. A pin string is not a version:

- A point pin like `~=17.3.0` happens to return `17.3.0` (correct).
- A major-only pin like `~=17.0` returns `"17.0"` — a `major.minor`
floor that nothing installs. Serverless has pinned by major (`~=17.0`,
not `~=17.3.0`) since databricks/environments#15 (merged 2026-08-11), so
this is live now for serverless targets.

On a real run `validate` overwrites `dbconnectVersion` with the
actually-installed version, so the reported value is correct. Under
`--dry-run`, `validate` is stubbed, so the fabricated value is what gets
reported — dry-run and real runs disagree, and the `--output json`
contract the VS Code extension consumes carries the wrong value in
dry-run. No wrong install happens; the provisioned venv is still
correct.

## Fix

Gate the reported version behind a new `dbcVersionFromPin`, which emits
a version only when the pin carries a full `major.minor.patch` (e.g.
`~=17.3.0` → `17.3.0`) and returns `""` (omitted from JSON) for a
range-only pin such as `~=17.0`.

The key constraint: `versionFromPin` has two callers. Hardening it
directly would break `dbcMajorFromPin`'s major extraction for `~=17.0`
and regress *real* runs (validate would fail to determine the major) —
worse than the dry-run bug. So the gate is applied only to the reporting
path; `versionFromPin` is left untouched.

## Tests

The ticket noted nothing tested this cross-repo coupling. Added:

- `TestPipelineDryRunOmitsFabricatedDBConnectVersion` — end-to-end
dry-run with a `~=17.0` pin (written red-first; reported `17.0` before
the fix).
- `TestDBCVersionFromPin` — table-driven, including the `~=17.0`
bare-major case.
- `TestDBCMajorFromPinHandlesMajorOnlyPin` — guards that the real-run
major path still works for `~=17.0`.

`go test ./libs/localenv` (170 pass) and related acceptance tests (9
pass) are green; the existing JSON goldens use a full `17.2.0` pin, so
their output is unchanged. `go vet`, `golangci-lint`, and `gofmt` are
clean.

No changelog fragment: `setup-local` is still hidden pending the unveil
(databricks#5835), matching the prior decision to drop a premature fragment for
it.

This pull request and its description were written by Isaac.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants