Skip to content

[9/?] feat(python-setup): extension wiring & config-view dispatch (DRAFT — blocked) - #2054

Draft
rugpanov wants to merge 6 commits into
mainfrom
rugpanov/python-setup-wiring
Draft

[9/?] feat(python-setup): extension wiring & config-view dispatch (DRAFT — blocked)#2054
rugpanov wants to merge 6 commits into
mainfrom
rugpanov/python-setup-wiring

Conversation

@rugpanov

@rugpanov rugpanov commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Why

Final integration for the uv-native Python environment setup: construct the stack in activate(), register its command, and let the config view dispatch to it. Turns the feature on for opted-in users while leaving everyone else on the unchanged legacy flow.

Rebased on fresh main now that all dependencies have merged (#2039 client, #2046/#2063 orchestrator, #2052 picker, #2053 compute-picker, #2055 state key). The diff is now just the wiring.

What

  1. EnvironmentComponent dispatchbuildPythonSetupEntry (pure) + the component returns the single uv entry when visible, else the unchanged legacy checklist (mutually exclusive). Adds isVisible() to the orchestrator so the component can decide dispatch without knowing the gate's inputs.
  2. makePythonSetupDeps — fills the orchestrator's seams with real implementations: visibility gate (isPythonSetupEnabled + shouldShowPythonSetup), compute resolution (cluster / persisted serverlessVersion), interpreter adoption via the MS Python extension, progress/toasts via window. The progress notification is cancellable (so a user "Cancel" tears down the CLI), and the CLI's streamed output is forwarded to a dedicated "Databricks Python Environment Setup" output channel, which is revealed on failure.
  3. extension.ts — construct detector/client/orchestrator, register databricks.environment.setupPythonEnv, create the output channel, and thread the entry through ConfigurationDataProvider. saveState is wired to the databricks.pythonSetup.setupState storage key (workspace-scoped), guarded so a rejected write logs rather than surfacing as an unhandled rejection.
  4. Command Palette hidden"when": "false" so the command is reachable only via the gated tree entry.

Backward compatibility

The stack is constructed unconditionally but inert: the gate keeps the entry hidden (flag off, or non-uv project), the command is palette-hidden, and the PythonSetupEntry dependency is optional end-to-end. Existing users see the identical legacy checklist — traced explicitly, and covered by the new EnvironmentComponent dispatch tests (no entry / not visible → checklist; visible → uv entry only).

Verification

  • build / test:lint — clean.
  • test:unit435 passing (run with VSCODE_TEST_VERSION=1.130.0; default stable pulls VS Code 1.131.0, which breaks @vscode/test-electron locally — repo-wide infra skew, unrelated).

This pull request and its description were written by Isaac.

@rugpanov

rugpanov commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Integration tests ❌ 1 of 35 test jobs failed for 0254d5eb (34 passed).
View run

@rugpanov
rugpanov force-pushed the rugpanov/python-setup-wiring branch from 0254d5e to 6350a60 Compare July 27, 2026 11:34
@rugpanov
rugpanov temporarily deployed to test-trigger-is July 27, 2026 11:34 — with GitHub Actions Inactive
@rugpanov
rugpanov temporarily deployed to test-trigger-is July 27, 2026 11:34 — with GitHub Actions Inactive
@rugpanov

Copy link
Copy Markdown
Contributor Author

🤖 Integration tests triggered for 6350a602 — ⏳ running.
View run

@rugpanov
rugpanov force-pushed the rugpanov/python-setup-wiring branch from 6350a60 to 47e53f5 Compare July 27, 2026 11:46
@rugpanov
rugpanov temporarily deployed to test-trigger-is July 27, 2026 11:46 — with GitHub Actions Inactive
@rugpanov
rugpanov temporarily deployed to test-trigger-is July 27, 2026 11:46 — with GitHub Actions Inactive
@rugpanov

rugpanov commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Integration tests ✅ all 35 test jobs passed for 47e53f5e.
View run

rugpanov added a commit that referenced this pull request Jul 28, 2026
#2055)

## Why

After a successful uv-native Python environment setup, the extension
needs to remember what environment was provisioned, so a later change of
compute target can be detected as **drift** (the local env no longer
matches the selected target) and surfaced to the user. This lands the
persistence key.

Scoped deliberately to the storage layer: it builds off `main` with no
dependency on the (still-open) wiring stack. The **write site** — the
orchestrator's `saveState` — is wired separately when the
extension-wiring PR (#2054) lands; it currently holds a
`TODO(DECO-27784)` no-op that this key will back.

## What

- **`PythonSetupState`** type — `{envKey, pythonVersion, timestamp}`:
- `envKey`: the CLI's resolved environment key for the target (e.g.
`serverless/serverless-v5`).
- `pythonVersion`: the provisioned interpreter minor version (e.g.
`3.12`).
  - `timestamp`: ISO-8601 time of the successful setup.
- **`"databricks.pythonSetup.setupState"`** `StorageConfigurations`
entry, **workspace-scoped** (drift is per-project). Flows through the
existing typed `get`/`set` surface — no new accessor code.
- **First `StateStorage` unit test** (in-memory `Memento` fake):
round-trip, undefined-before-write, clear-on-undefined, and
workspace-not-global placement.

## Verification

- `yarn --cwd packages/databricks-vscode build / test:lint / test:unit`
— 329 passing (4 new). Build confirms the typed key resolves through
`get`/`set`.

This pull request and its description were written by Isaac.
rugpanov added a commit that referenced this pull request Jul 29, 2026
## Why

**#2046 (DECO-27785, the setup orchestrator) shows as MERGED but its
code never reached `main`.** It merged into the
`rugpanov/python-setup-cli-client` branch *after* that branch's PR
(#2039) had already been **squash-merged** to main — so the orchestrator
landed on a now-stranded branch and `main` has no
`PythonSetupEnvironmentSetup`. This re-lands the reviewed orchestrator
directly on main.

Without this, the feature's first real `PythonSetupCliClient` caller is
missing from main, and the extension-wiring PR (#2054) has nothing to
build on.

## What

`PythonSetupEnvironmentSetup` + its test, **restored byte-identical**
from #2046 as reviewed/approved. The orchestrator:
- gates on `isVisible`, resolves the compute target, runs the CLI under
a progress indicator (with cancellation),
- on a ready real-run adopts the venv interpreter + persists setup
state,
- maps failures to `getPythonSetupErrorMessage`,
- coalesces concurrent runs via an in-flight guard.

The gate / compute-resolution / adopt / persist / progress collaborators
are injected seams, wired to real implementations by the
extension-wiring PR (#2054).

## Verification

- `build` + `test:lint` — clean.
- `test:unit` — **415 passing**.
- Note: run with `VSCODE_TEST_VERSION=1.130.0`. The default `"stable"`
now pulls VS Code **1.131.0**, whose macOS bundle renames the Electron
executable to `Code` and breaks `@vscode/test-electron ^3.0.0` locally
(`spawn … Electron ENOENT`). That's a repo-wide test-infra skew,
unrelated to this change — flagging it separately.

## Context

This is a process-recovery PR. Numbered `[6b/?]` since it restores what
`[6/?]` (#2046) was meant to deliver. DECO-27785.

This pull request and its description were written by Isaac.
rugpanov added 4 commits July 29, 2026 17:06
*Why*
The Python Environment section must show the single uv-native setup entry when
the feature is visible, and the legacy pip checklist otherwise -- the two are
mutually exclusive and never rendered together.

*What*
- Add buildPythonSetupEntry (pure): the single Python Environment row -- a rocket
  run CTA when not ready, a check status line once ready -- both running the
  setup command. Plus the PythonSetupEntry interface (isVisible/ready/
  onDidChangeState), a narrow slice of the orchestrator so the component carries
  no controller/gateway dependency.
- EnvironmentComponent takes an optional PythonSetupEntry; getChildren returns
  that single entry when isVisible(), else the unchanged checklist. Refreshes
  the view on its onDidChangeState. Optional, so the legacy construction is
  byte-for-byte unchanged when absent.

*Verification*
yarn --cwd packages/databricks-vscode test:unit (399 passing; 3 new entry-builder
cases: run CTA, ready line, single entry). build clean.

Co-authored-by: Isaac
*Why*
The orchestrator's gate and compute-resolution seams shipped as stubs. This
fills them with the real implementations, wiring the pieces from across the
stack together for the extension.

*What*
- makePythonSetupVisibility: the config-view gate -- opted-in (flag on) AND the
  active project fits (shouldShowPythonSetup: clean uv/greenfield, no competing
  manager). No project / detection failure -> hidden.
- resolveComputeFrom: map the attached compute to a setup-local target. A cluster
  wins outright; a serverless session resolves to its persisted serverlessVersion
  (from the compute picker, Diff A) -- no version -> undefined (abort silently,
  no guessing).
- makePythonSetupDeps: assemble the full PythonSetupSetupDeps -- adoptInterpreter
  via venvInterpreterPath + the MS Python extension, progress/toasts via window.
  saveState is a documented no-op TODO(DECO-27784) until the storage key lands.

*Verification*
yarn --cwd packages/databricks-vscode test:unit (408 passing; 9 new: 4 visibility
gate, 5 compute resolution incl. cluster-over-serverless and version-less-abort).
build clean.

Co-authored-by: Isaac
*Why*
Final integration: construct the python-setup stack in activate(), register its
command, and let the config view dispatch to it. This turns the feature on for
opted-in users while leaving everyone else on the unchanged legacy flow.

*What*
- extension.ts: build PythonSetupManagerDetector (real collector via
  collectPackageManagerSignals + the active interpreter), PythonSetupCliClient
  (resolveCliPath: cliPathOverride else bundled), and PythonSetupEnvironmentSetup
  via makePythonSetupDeps -- projectRoot from workspaceFolderManager, gate from
  isPythonSetupEnabled + detection, compute from connectionManager
  (cluster / persisted serverlessVersion), interpreter adopted through the MS
  Python extension. Register databricks.environment.setupPythonEnv.
- ConfigurationDataProvider: thread the (optional) PythonSetupEntry into
  EnvironmentComponent.
- PythonSetupEnvironmentSetup: expose isVisible() (delegates to the gate seam) so
  it satisfies the component's PythonSetupEntry contract.
- package.json: register the setupPythonEnv command.

Backward compatibility: the stack is constructed unconditionally but inert --
the gate (flag off, or non-uv project) keeps the entry hidden and the command a
no-op, so existing users see the identical legacy checklist. The dependency is
optional end-to-end, so nothing changes when it is absent.

*Verification*
yarn --cwd packages/databricks-vscode build / test:lint / test:unit
(408 passing; build type-checks the full activate() wiring). Manual dogfff
smoke deferred to a custom-CLI environment.

Co-authored-by: Isaac
*Why*
The feature must stay hidden until a user opts in. A registered command is
palette-invokable by default, which would expose "Set up Python environment
(uv)" to every connected user regardless of the opt-in flag or project type.

*What*
- Add a commandPalette menu entry with "when": "false" for
  databricks.environment.setupPythonEnv, so it is reachable only via the gated
  config-view tree entry (which already checks the opt-in flag + package-manager
  gate), never the palette. Matches the repo's pattern for tree-only commands.

*Verification*
yarn --cwd packages/databricks-vscode build / test:lint (both clean).

Co-authored-by: Isaac
@rugpanov
rugpanov force-pushed the rugpanov/python-setup-wiring branch from 47e53f5 to 2c6ecb4 Compare July 29, 2026 15:07
@rugpanov
rugpanov temporarily deployed to test-trigger-is July 29, 2026 15:07 — with GitHub Actions Inactive
@rugpanov
rugpanov temporarily deployed to test-trigger-is July 29, 2026 15:08 — with GitHub Actions Inactive
@rugpanov

Copy link
Copy Markdown
Contributor Author

🤖 Integration tests triggered for 2c6ecb43 — ⏳ running.
View run

*Why*
The extension-wiring PR left saveState as a TODO(DECO-27784) no-op and carried
seam-doc comments claiming the visibility/compute seams were still stubbed.
Both are now resolved: the storage key landed (#2055) and this PR wires the real
implementations.

*What*
- Implement saveState (DECO-27784 write half): makePythonSetupDeps stamps the
  orchestrator's {envKey, pythonVersion} with an ISO-8601 timestamp and forwards
  it through a new injected persistSetupState seam; extension.ts wires that to
  stateStorage.set("databricks.pythonSetup.setupState", ...). Workspace-scoped,
  so drift detection is per-project.
- Correct the now-stale PythonSetupSetupDeps seam docs on isVisible/resolveCompute
  -- they described "the extension passes a stub"; the real gate (flag +
  shouldShowPythonSetup) and compute resolution (cluster / persisted
  serverlessVersion) are now wired.
- Tests: makePythonSetupDeps.saveState stamps+forwards a valid timestamp;
  adoptInterpreter routes through the seam with the OS-correct venv path.

*Verification*
yarn --cwd packages/databricks-vscode build / test:lint (clean); test:unit
429 passing (VSCODE_TEST_VERSION=1.130.0; default stable pulls VS Code 1.131.0
which breaks @vscode/test-electron locally -- repo-wide infra skew, unrelated).

Co-authored-by: Isaac
@rugpanov
rugpanov temporarily deployed to test-trigger-is July 29, 2026 15:15 — with GitHub Actions Inactive
@rugpanov
rugpanov temporarily deployed to test-trigger-is July 29, 2026 15:16 — with GitHub Actions Inactive
@rugpanov

rugpanov commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Integration tests ✅ all 35 test jobs passed for 29e5435f.
View run

…spatch

*Why*

Review of PR #2054 surfaced three defects confined to the opted-in uv path:
the progress notification was not `cancellable`, so the token never fired and
the whole downstream cancellation path (CLI teardown, PythonSetupCancelledError,
the orchestrator's quiet-on-cancel branch) was dead code — a user could not
cancel a minutes-long project-mutating run. The CLI's streamed output was piped
to a no-op `onLog`, so failures showed only a one-line message with the real
stderr discarded. And the `EnvironmentComponent` dispatch that guarantees the
uv entry and legacy checklist are mutually exclusive had no direct test.

*What*

- `pythonSetupDeps.ts`: add a `log` seam (`append`/`show`); `withProgress` now
  passes `cancellable: true` and forwards chunks to `log.append`; `showError`
  reveals the log channel. Fix the stale visibility-gate comment to state the
  gate fails *open* on detection failure (matches `detect`'s swallow-to-unknown).
- `extension.ts`: create a "Databricks Python Environment Setup" output channel
  (disposed via subscriptions) and wire it to the `log` seam; guard the
  fire-and-forget `persistSetupState` write with a `.catch` that logs so a
  rejected workspaceState update is not an unhandled rejection.
- Tests: new `EnvironmentComponent.test.ts` covers all three dispatch branches
  (no entry / not visible → checklist; visible → uv entry only). Add
  `withProgress` tests (log forwarding + cancellation token) and a
  cluster-wins-over-serverless precedence case.

*Verification*

- `build` / `test:lint` — clean.
- `test:unit` — 435 passing (VSCODE_TEST_VERSION=1.130.0), up from 427; 8 new.

Co-authored-by: Isaac
@rugpanov
rugpanov temporarily deployed to test-trigger-is July 29, 2026 16:53 — with GitHub Actions Inactive
@rugpanov

rugpanov commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Integration tests ❌ 1 of 35 test jobs failed for 71c4de36 (34 passed).
View run

@rugpanov
rugpanov deployed to test-trigger-is July 29, 2026 16:54 — with GitHub Actions Active
@github-actions

Copy link
Copy Markdown
Contributor

If integration tests don't run automatically, an authorized user can run them manually by following the instructions below:

Trigger:
go/deco-tests-run/vscode

Inputs:

  • PR number: 2054
  • Commit SHA: 71c4de368e15ff6aeda775e51e18f7e04e2c2a69

Checks will be approved automatically on success.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant