Skip to content

Utilize absolute runtime paths in language extensions#14813

Open
samclark2015 wants to merge 15 commits into
mainfrom
issue-12942
Open

Utilize absolute runtime paths in language extensions#14813
samclark2015 wants to merge 15 commits into
mainfrom
issue-12942

Conversation

@samclark2015

@samclark2015 samclark2015 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Fixes #12942

runtimePath was being set to a ~-shortened string on non-Windows user installs, which broke execFile callers (e.g. the Publisher extension) that passed it directly to the OS. This PR fixes that by splitting the two concerns:

  • runtimePath — always the full absolute path, suitable for execFile, cache keys, and disk operations. Never contains ~.
  • runtimeDisplayPath — a ~-shortened version for UI display, computed once by LanguageRuntimeService.registerRuntime via the tildify helper when a runtime is registered. Extensions no longer supply this field; it is removed from the public positron.d.ts API. All ~9 UI display sites use the existing getRuntimeDisplayPath(metadata) helper (falls back to runtimePath when runtimeDisplayPath is absent), and are unchanged.

A follow-up fix heals affiliated-runtime storage: pre-fix workspace affiliations had runtimePath = ~/... persisted in positron.affiliatedRuntimeMetadata.v2.*. onDidRegisterRuntime now overwrites the stored metadata with the fresh (absolute-path) version on each discovery pass, and getAffiliatedRuntimes() prefers live registered metadata over the stored copy (matching what getPreferredRuntime already did). The discovery cache schema is bumped v2→v3 to flush stale entries.

Release Notes

New Features

  • N/A

Bug Fixes

Validation Steps

@:console @:interpreter-status

  • Start a Python or R runtime whose interpreter lives under the home directory (e.g. a user-installed R or a pyenv/venv Python).
  • Verify the path shown in the UI is ~-shortened: click the (Console info) button in the Console panel — the Path row should show ~/….
  • Verify runtimePath is the full absolute path: open the Command Palette (Cmd+Shift+P) → "Clear Saved Interpreter" → the description next to each interpreter name must not start with ~. Press Escape to dismiss without clearing anything.

samclark2015 and others added 2 commits July 9, 2026 16:10
…ened)

Fixes execFile callers that received a ~-prefixed path. Adds getRuntimeDisplayPath()
helper for all UI display sites; bumps discovery cache schema v2->v3 to discard stale entries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers the three gaps PETE identified: a Vitest for the new getRuntimeDisplayPath
pure helper, a Mocha suite for R makeMetadata path fields, and a focused suite
for Python createPythonRuntimeMetadata path fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown

E2E Tests 🚀
This PR will run tests tagged with: @:critical @:console @:interpreter @:ark @:new-folder-flow @:sessions

Why these tags?
Tag Source
@:critical Always runs (required)
@:console PR description
@:interpreter Changed files
@:ark Changed files
@:new-folder-flow Changed files
@:sessions Changed files

More on automatic tags from changed files.

Warning

Unrecognized tag(s) in the PR description, ignored: @:interpreter-status. Check for a typo, or see the valid tags list.

Warning

This PR touches a Positron directory that isn't mapped in test-tag-paths-map.json: src/vs/workbench/contrib/chat/. Add an entry (an e2e tag or [] for no coverage) so future changes are tagged automatically.

readme  valid tags

samclark2015 and others added 2 commits July 10, 2026 11:30
getAffiliatedRuntimes() now prefers the live registered metadata over the
stored copy (same pattern getPreferredRuntime already uses), so the Clear
Saved Interpreter quick pick always shows the current runtimePath.
onDidRegisterRuntime heals the stored affiliation at discovery time so the
storage doesn't accumulate stale values across sessions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eService

Instead of each extension manually computing a ~-shortened display path and
storing it in metadata, LanguageRuntimeService.registerRuntime now enriches
incoming metadata with runtimeDisplayPath via tildify() at registration time.

IPathService is injected once into the service; all 9 display call sites and
the getRuntimeDisplayPath() helper are unchanged. runtimeDisplayPath is removed
from the public positron.d.ts API since extensions no longer need to supply it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@samclark2015 samclark2015 changed the title Split runtimePath (absolute) from runtimeDisplayPath (UI display only) Compute runtimeDisplayPath centrally at registration instead of in each extension Jul 10, 2026
@samclark2015 samclark2015 changed the title Compute runtimeDisplayPath centrally at registration instead of in each extension Utilize absolute runtime paths in language extensions Jul 10, 2026
samclark2015 and others added 4 commits July 13, 2026 12:01
…ealing test

The extensions no longer compute runtimeDisplayPath (that moved to
LanguageRuntimeService), so drop the ~-shorthand assertions from the
positron-python and positron-r unit tests. Add a runtimeStartup vitest
covering stale ~-prefixed affiliation paths being healed on register.

Also picks up incidental package-lock.json optional-dep drift.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@samclark2015
samclark2015 requested a review from isabelizimm July 13, 2026 20:58
Comment thread src/vs/workbench/contrib/languageRuntime/browser/languageRuntimeActions.ts Outdated

// Enrich metadata with a workbench-computed display path (~-shortened
// on non-Windows; absolute path unchanged on Windows or system paths).
const userHome = this._pathService.userHome({ preferLocal: true }).fsPath;

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.

what does preferLocal mean for, eg, posit workbench or a web build?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch! This would return a fake home dir on web. I updated it so the true local path is cached (it's async, so fetched + cached in constructor). If that's unavailable, we just display the non-tildified path instead.

samclark2015 and others added 5 commits July 14, 2026 15:40
…t mock enums

runtimeDisplayPath is now computed in LanguageRuntimeService, so the extension-level
runtimePath test is obsolete. The LanguageRuntimeStartupBehavior and
LanguageRuntimeSessionLocation enum copies in the pst mock (and their vscode-mock
wiring) are no longer needed since the test that used them is gone.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…h tildification

Uses preferLocal: false so paths are tildified against the remote home
directory when connected to a remote, not the local one. Adds a ?? fallback
to preserve a caller-supplied runtimeDisplayPath rather than overwriting it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tructor

IPathService resolves remote home during workbench startup, well before
any extension activates. Cache the result in the constructor via a fire-and-
forget Promise so registerRuntime stays synchronous. Falls back to the local
preferLocal:true overload on the rare chance it is called before the Promise
resolves.

Also fix TestPathService.userHome() to return Promise<URI> for the async
overload (preferLocal != true), matching the real service contract.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…structor comment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@samclark2015
samclark2015 requested a review from isabelizimm July 16, 2026 14:40
The LanguageRuntimeService constructor starts a userHome() Promise to
populate _cachedUserHome; registerRuntime skips tildification when it
hasn't resolved yet. The test was synchronous, so the microtask never
ran before registerRuntime was called. Making the test async and
awaiting one Promise.resolve() lets the microtask run first.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@isabelizimm isabelizimm 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.

the code and trying locally looks good to me, it doesn't look like the failing CI is related at first glance (but might be worth investigating if these are new failures?)

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.

Provide 2 distinct fields for runtime path: one full path, one for display

2 participants