Repository navigation
fix(python-setup): recover a uv workspace member from a constraint conflict - #2254
Conversation
|
🤖 Integration tests ✅ passed for |
|
🤖 Integration tests running for |
a5ce3bd to
1201dd5
Compare
|
🤖 Integration tests ✅ passed for |
misha-db
left a comment
There was a problem hiding this comment.
Automated review (xhigh) findings, posted inline. All low-severity — no correctness blockers. Findings 1 and 3 are related (two divergent copies of a path-containment check). Nothing here blocks approval.
| function assertBackupInside(folder: string, backupPath: string): string { | ||
| const root = path.resolve(folder); | ||
| const rel = path.relative(root, path.resolve(backupPath)); | ||
| if (rel === "" || rel.startsWith("..") || path.isAbsolute(rel)) { |
There was a problem hiding this comment.
Severity: Low · Category: Correctness (latent)
assertBackupInside uses rel.startsWith(".."), which also rejects a path legitimately inside the folder whose first segment merely begins with .. (e.g. /mono/..cache.bak gives rel = "..cache.bak"), throwing "Refusing to restore ... outside" and aborting a valid retry.
Unreachable in practice today because the CLI backup name is the fixed pyproject.toml.bak. The sibling containsPath helper in PythonSetupEnvironmentSetup.ts already uses the precise form, so the two containment checks disagree on this edge. Suggested: rel === ".." || rel.startsWith(".." + path.sep).
| const recoverableConflict = conflictBackupPath !== undefined; | ||
| !invocation.skipConstraints; | ||
| const cwdForms = isPinConflict | ||
| ? [cwd, await this.realPathOrSelf(cwd)] |
There was a problem hiding this comment.
Severity: Low · Category: Efficiency
In the pin-conflict branch, cwdForms recomputes [cwd, await this.realPathOrSelf(cwd)], duplicating the entry already pushed to runFolders earlier in runResolved. Every conflicting run makes two fs.realpath(cwd) calls instead of one. Harmless (realpath is cheap, conflicts are rare), but the second could reuse the existing runFolders tail.
| } | ||
|
|
||
| /** Whether `child` is `parent` or a path inside it, compared by text. */ | ||
| function containsPath(parent: string, child: string): boolean { |
There was a problem hiding this comment.
Severity: Low · Category: Maintainability (duplication)
This lexical containsPath helper and the extracted assertBackupInside in pythonSetupDeps.ts do the same "is path inside dir" job with divergent edge semantics: this one uses rel === ".." || rel.startsWith(".." + path.sep), the other uses rel.startsWith(".."). Two copies in one feature means future fixes must land in both. Consider consolidating into one shared containment util.
| {folder: root, backupPath: result.uvWorkspaceBackupPath}, | ||
| ]; | ||
| const {backupPath} = result; | ||
| if (backupPath) { |
There was a problem hiding this comment.
Severity: Low · Category: Simplification (duplication)
conflictBackupsOf builds {folder: backupFolder(bp, cwdForms), backupPath: bp} in both the member and non-member branches; the two could share one expression so that a change to how the folder for a project-file backup is derived lives in a single place. Pure cleanup, no behavioral defect.
| @@ -1,3 +1,4 @@ | |||
| import path from "node:path"; | |||
There was a problem hiding this comment.
Severity: Low · Category: Consistency
Import spelling is mixed across files touched by this PR: this file and PythonSetupAdoptionManager.ts use node:path, while pythonSetupDeps.ts and setupSummary.ts use path. No runtime effect and yarn fix will not normalize it, but worth picking one spelling for the feature.
…e member (#2253) **Stack** (merge in this order): 1. #2253 fix(python-setup): select the shared uv workspace venv for a workspace member ([see diff](main...uv-workspace-shared-venv)) **<- this PR** 2. #2254 fix(python-setup): recover a uv workspace member from a constraint conflict ([see diff](uv-workspace-shared-venv...uv-workspace-shared-venv-2)) ## Changes In a monorepo with one uv workspace and one bundle per member, the Python environment setup selects the wrong interpreter (#2157). The Databricks CLI now supports uv workspace members in `databricks environments setup-local` (databricks/cli#7018, databricks/cli#7026). For a member, the CLI provisions one shared venv at the workspace root. It reports `venvPath` relative to the member folder, for example `../../.venv`, and it adds `uvWorkspaceRoot` and `uvWorkspaceBackupPath`. - The extension resolves `venvPath` to an absolute folder. It uses this folder to select the interpreter and to read the venv name from `pyvenv.cfg`. - A value that climbs (`../../.venv`) counts from the symlink-resolved project folder on Unix, as the CLI does. - `.venv` stays under the project folder as opened. - An absolute value (from an absolute `UV_PROJECT_ENVIRONMENT`) stays as it is. - Before this change, the extension gave the Python extension a relative interpreter path. It also read `pyvenv.cfg` relative to the working folder of the extension host, so the venv name did not show in the summary. These fixes also apply to a standalone project. - The result parser reads `uvWorkspaceRoot` and `uvWorkspaceBackupPath`. - For a member, the success summary names the shared venv and the root `pyproject.toml`. The venv folder uses Windows separators on Windows. - The warning histogram counts `W_UV_WORKSPACE_CONSTRAINTS_REPLACED`. ## Tests - New unit tests cover the venv folder for a member, a symlinked project folder, `.venv` under the opened folder, an absolute `venvPath`, the venv name, the result fields, the summary text on Linux and Windows, and the warning count. - Full unit suite: 1401 passing, 0 failing. `yarn run test:lint` reports 0 errors. ## Known limitations - The fix for #2157 needs a bundled CLI that contains databricks/cli#7018. With the current CLI (1.20.0), `venvPath` is always `.venv`, and this PR changes only the two standalone fixes above. - On Windows, the extension does not resolve a directory symlink in the project folder. The CLI resolves it for a member, so a member opened through such a symlink gets a wrong venv folder. This pull request and its description were written by Isaac. --------- Co-authored-by: Isaac <no-reply@databricks.com>
…nflict *Why*: * For a uv workspace member, setup-local writes the constraint pins into the root pyproject.toml. On a conflict, "Retry DB Connect setup" restored only the member file, so the retry with --no-constraints kept the conflicting root pins, and "Open pyproject.toml" opened the member file. * The adoption gauge checked `<project>/.venv`, so it reported a member's shared venv as missing. *What*: * The retry also restores the root pyproject.toml from `uvWorkspaceBackupPath`, and it checks every backup before it restores the first one. It trusts `uvWorkspaceRoot` only when the root contains the project folder (as opened or symlink-resolved). A member with no root backup gets no retry. * "Open pyproject.toml" opens the root file for a member. * Any later setup run makes an older retry stale, because it can rewrite the shared root file. A stale click tells the user to run the setup again. * The saved setup state records the project folder and the absolute venv folder. The adoption gauge checks that venv folder, and it skips a reading while the state belongs to another bundle in the same workspace folder. Co-authored-by: Isaac <no-reply@databricks.com>
…tore * A later setup run makes an older retry stale only when it ran inside the folder whose files the retry restores: the uv workspace root for a member, else the project. A run of an unrelated project no longer disables the retry. * Pick the backup folder in the form the CLI named the backup, also for a standalone project opened through a symlink. * checkProjectFileBackup checks that the backup is readable, not only that it exists. Co-authored-by: Isaac <no-reply@databricks.com>
Each run records its project folder as opened and symlink-resolved, so a later run of a sibling member counts as stale also when the workspace is open through a symlink and the CLI reports the real root. The stale message no longer says that another setup ran when the retry itself changed the files. Co-authored-by: Isaac <no-reply@databricks.com>
1201dd5 to
5728708
Compare
|
🤖 Integration tests ❌ failed for |
assertBackupInside rejected a backup inside the folder whose name starts
with "..", because it used rel.startsWith(".."). Move containsPath to
utils and use it for both the backup check and the conflict scope.
Co-authored-by: Isaac <no-reply@databricks.com>
|
Thanks for the review!
|
|
If integration tests don't run automatically, an authorized user can run them manually by following the instructions below: Trigger: Inputs:
Checks will be approved automatically on success. |
|
🤖 Integration tests ✅ passed for |
## packages/databricks-vscode ## (2026-10-22) - fix(bundle): find sub-projects without blocking the extension host (#2249) ([5ebf39f](5ebf39f)) - fix(network): let VS Code handle proxies and certificates for SDK calls (#2266) ([589041a](589041a)) - fix(python-setup): recover a uv workspace member from a constraint conflict (#2254) ([7752f5d](7752f5d)) - fix(python-setup): select the shared uv workspace venv for a workspace member (#2253) ([faa6f58](faa6f58)) - Expose the bundle Configuration view and Bundle Resource Explorer in Databricks Remote SSH mode (#2236) ([375dd8d](375dd8d)) - Update Databricks CLI to v1.22.1 (#2270) ([bc837b5](bc837b5)) ## packages/databricks-vscode-types ## (2026-10-22) Co-authored-by: releasebot <noreply@github.com>
Stack (merge in this order):
Changes
For a uv workspace member,
setup-localwrites the constraint pins into the rootpyproject.tomland shares one venv at the root. The conflict recovery and the adoption gauge assumed onepyproject.tomland one.venvin the project folder.pyproject.tomlfromuvWorkspaceBackupPath. Without this, the retry with--no-constraintskeeps the conflicting root pins and fails again.uvWorkspaceRootonly when it contains the project folder, as opened or symlink-resolved, because the restore writes there.Tests
yarn run test:lintreports 0 errors.Known limitations
This pull request and its description were written by Isaac.