Show debugger setup hints for missing VS Code extensions - #19131
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 19131Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 19131" |
There was a problem hiding this comment.
Pull request overview
Adds missing-debugger install hints for Python, Go, and Bun resources in the VS Code extension.
Changes:
- Adds install notifications, suppression, failure handling, and restart guidance.
- Adds contextual debugger-install CodeLens actions.
- Updates Python detection to use
ms-python.debugpyand adds unit/E2E coverage.
Show a summary per file
| File | Description |
|---|---|
extension/src/capabilities.ts |
Detects standalone debugpy. |
extension/src/debugger/debuggerInstallHints.ts |
Implements hint and installation service. |
extension/src/debugger/languages/python.ts |
Uses the debugpy extension ID. |
extension/src/editor/AspireCodeLensProvider.ts |
Adds install CodeLens actions. |
extension/src/editor/DebuggerInstallHintWatcher.ts |
Detects running debuggable resources. |
extension/src/extension.ts |
Registers and wires the feature. |
extension/src/loc/strings.ts |
Adds localized UI strings. |
extension/package.nls.json |
Registers localization resources. |
extension/loc/xlf/aspire-vscode.xlf |
Updates the localization catalog. |
extension/src/test/aspireCodeLensProvider.test.ts |
Tests debugger CodeLens behavior. |
extension/src/test/debuggerInstallHints.test.ts |
Tests hint service behavior. |
extension/src/test/debuggerInstallHintWatcher.test.ts |
Tests resource detection and parsing. |
extension/src/test/pythonDebugger.test.ts |
Tests debugpy capability detection. |
extension/src/test/strings.test.ts |
Tests localization registration. |
extension/src/test-e2e/debuggerInstallHint.e2e.test.ts |
Covers notification and CodeLens UX. |
extension/src/test-e2e/helpers/extester.ts |
Extends notification test typings. |
extension/src/test-e2e/helpers/vscode.ts |
Adds notification-action inspection. |
Review details
- Files reviewed: 17/17 changed files
- Comments generated: 1
- Review effort level: Balanced
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
There was a problem hiding this comment.
Review details
Suppressed comments (1)
extension/src/test-e2e/debuggerInstallHint.e2e.test.ts:61
- This E2E test still waits for the old CodeLens tooltip text, but the notification now emits
Debug 1 Aspire resource by installing the Python debugger extension.. As written,waitForNotificationMessagewill time out even when the feature works. Update the expected notification text to the new singular toast.
'Install the Python debugger extension to debug this Aspire resource.',
- Files reviewed: 17/17 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
8bedf7d to
69bfc0a
Compare
There was a problem hiding this comment.
Review details
Files not reviewed (1)
- src/Aspire.Hosting/Resources/MessageStrings.Designer.cs: Generated file
Suppressed comments (3)
extension/src/debugger/debuggerInstallHints.ts:55
- This assumes every debugger entry has exactly one install prerequisite, but
azure-functionsis only usable when both the C# and Azure Functions extensions are installed (getResourceDebuggerExtensions()checks both), while its entry exposes onlyms-dotnettools.csharp. After this hint installs C#, the resource still cannot debug and no hint is offered for the missing Azure Functions extension. Limit install hints to the three supported single-extension types in this PR (Python, Go, and Bun), or model all prerequisites explicitly.
for (const debuggerExtension of getAllResourceDebuggerExtensions()) {
if (!debuggerExtension.extensionId) {
// A null extensionId means the debug adapter ships with VS Code, so there is nothing to install.
continue;
}
extension/src/debugger/debuggerInstallHints.ts:115
- The current implementation reintroduces the previously reported toast bug: it fires on the first matching resource and deliberately omits the count, while the PR description promises a single cross-AppHost notification with singular/plural
Debug {N} Aspire resource(s)...wording. Because_notificationsShownThisSessionis set immediately, later resources can never update that first snapshot. Aggregate all matching running resources before showing the toast and pass the count to separate localized singular/plural strings.
* The toast is coalesced to one per extension id per session, so it deliberately says nothing about
* how many resources are affected: that number is a snapshot that goes stale as soon as another
* resource starts, and the actionable fact is that the debug adapter is missing.
src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.in.targets:88
- This adds two MSBuild probes for every Aspire project resource on AppHost builds, but the install-hint implementation only consumes
resource.launchConfigurationType; no production code in this PR reads the newly publishedproject.assemblyName. The public metadata contract, dashboard property, CLI pass-through test, localization, and build cost are also absent from the PR description. Remove this unrelated assembly-name feature from this PR, or add the missing consumer and document why it is required here.
<Target Name="_GetAspireProjectResourceAssemblyNames"
DependsOnTargets="_CreateAspireProjectResources;PrepareProjectReferences"
Condition="'$(SkipAspireProjectResourceAssemblyName)' != 'true'">
- Files reviewed: 50/51 changed files
- Comments generated: 0 new
- Review effort level: Balanced
69bfc0a to
700b025
Compare
There was a problem hiding this comment.
Review details
Files not reviewed (1)
- src/Aspire.Hosting/Resources/MessageStrings.Designer.cs: Generated file
Suppressed comments (3)
extension/src/debugger/debuggerInstallHints.ts:62
- This assumes
extensionIdis the debugger's complete prerequisite set, but Azure Functions requires both C# andms-azuretools.vscode-azurefunctions(getResourceDebuggerExtensions()checks both, and its launch callback calls the Functions extension API). With C# installed and the Functions extension missing, this map reports no missing debugger; with both missing, installing C# produces restart guidance even though debugging still cannot work. Model all required extensions, or exclude compound adapters from these hints.
debuggerInstallHintsByLaunchConfigurationType.set(debuggerExtension.resourceType, {
// The display name describes the extension, not the language, because several launch
// configuration types can share one extension ('project' and 'azure-functions' both need the C#
// extension) and the toast is coalesced per extension id.
debuggerName: debuggerExtension.extensionDisplayName ?? debuggerExtension.extensionId,
extensionId: debuggerExtension.extensionId,
src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.in.targets:166
%(TargetPath.Filename)is$(TargetName), not necessarily$(AssemblyName). A project can explicitly set those properties to different values, in which case this publishes the output target name throughIProjectMetadata.AssemblyNameandproject.assemblyName, violating the new contract. Either evaluate the actualAssemblyNameproperty or rename the API/wire property to describe the target/output name.
<PropertyGroup>
<_AspireResolvedProjectFile>%(_AspireProjectResourceTargetPath.MSBuildSourceProjectFile)</_AspireResolvedProjectFile>
<_AspireResolvedProjectFile Condition="'$(_AspireResolvedProjectFile)' != ''">$([System.IO.Path]::GetFullPath('$(_AspireResolvedProjectFile)'))</_AspireResolvedProjectFile>
<_AspireResolvedAssemblyName>%(_AspireProjectResourceTargetPath.Filename)</_AspireResolvedAssemblyName>
extension/src/debugger/debuggerInstallHints.ts:171
- If persisting “Don't Show Again” fails, this rejection is caught only by
notifyMissingDebuggersand written to the output channel. The user receives no failure message, while_notificationsShownThisSessionstill prevents another prompt until restart, so the suppression action silently did nothing. Handle this failure explicitly, restore retryability, and show a localized error.
if (selected === dontShowAgainLabel) {
await this._globalState.update(`${notificationSuppressedKeyPrefix}${hint.extensionId}`, true);
- Files reviewed: 50/51 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
700b025 to
8bf3cb1
Compare
There was a problem hiding this comment.
Review details
Files not reviewed (1)
- src/Aspire.Hosting/Resources/MessageStrings.Designer.cs: Generated file
Suppressed comments (3)
extension/src/debugger/debuggerInstallHints.ts:60
- This assumes each debugger has exactly one prerequisite, but Azure Functions is only registered when both
ms-dotnettools.csharpandms-azuretools.vscode-azurefunctionsare installed (debuggerExtensions.ts:126-131). Because its entry records only the C# extension, a host with C# installed but the Azure Functions extension missing gets no hint; with neither installed, the one-click action still leaves debugging unavailable. Restrict install hints to the Bun/Python/Go adapters described by this PR, or model and verify every required extension for a launch type.
debuggerInstallHintsByLaunchConfigurationType.set(debuggerExtension.resourceType, {
// The display name describes the extension, not the language, because several launch
// configuration types can share one extension ('project' and 'azure-functions' both need the C#
// extension) and the toast is coalesced per extension id.
extension/src/extension.ts:460
dataRepository.appHostsis not all known AppHosts: in workspace view, its getter filters to workspace AppHosts (AppHostDataRepository.ts:329-333). As a result, a running debuggable resource in any other AppHost is omitted from this scan and never produces the promised install hint while the user remains in workspace view, despite this helper claiming to cover all AppHosts. Use an unfiltered repository accessor for notification discovery (while retaining the filtered accessor for UI rendering).
function getAllKnownResources(dataRepository: AppHostDataRepository): ResourceJson[] {
const resources: ResourceJson[] = [...dataRepository.workspaceResources];
for (const appHost of dataRepository.appHosts) {
if (appHost.resources) {
resources.push(...appHost.resources);
src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.in.targets:98
- This introduces two MSBuild probes for every Aspire project reference, but the install-hint implementation only consumes
resource.launchConfigurationType; the newproject.assemblyNamevalue has no production reader in this change (only publication/display and pass-through tests). That creates an unrelated build-time cost and public snapshot/API contract not described by this PR. Remove this assembly-name pipeline from this PR, or split it into a separately justified change with its consumer and performance rationale.
<MSBuild Projects="@(_AspirePreparedProjectResource)"
Targets="GetTargetFrameworks"
BuildInParallel="$(BuildInParallel)"
Properties="%(_AspirePreparedProjectResource.SetConfiguration); %(_AspirePreparedProjectResource.SetPlatform)"
ContinueOnError="!$(BuildingProject)"
- Files reviewed: 50/51 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address correctness and lifecycle issues found reviewing the debugger extension install hint. - Tell the user that the AppHost must be restarted after installing a debugger extension. Debug capabilities are snapshotted into DEBUG_SESSION_INFO / ASPIRE_EXTENSION_CAPABILITIES when the AppHost process starts, so a debugger installed mid-run does not attach to the already-running resource. The guidance is deferred until the extension is actually visible to `vscode.extensions.getExtension`, because `workbench.extensions.installExtension` resolves before the extension host publishes the new extension. - Surface install failures. Marketplace installs fail when the user is offline, behind a proxy, or on a build without gallery access; that was previously swallowed into the output channel with the hint left in place and no explanation. - Derive hint extension ids from the resource debugger extension registry (`languages/python.ts`, `languages/go.ts`, `languages/bun.ts`) so the hint can no longer drift from the debugger that actually launches, and skip languages whose adapter ships with VS Code. - Look up Add* method names case-insensitively instead of enumerating both spellings. The JS/TS parser matches `/^add\w+$/i`, so any casing can reach the lookup. - Skip opening and parsing AppHost source documents once every hint has been shown, suppressed, or satisfied, instead of re-parsing on every resource poll for the rest of the session. - Bound the parse-retry loop so a document being edited continuously cannot spin it forever. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 506d8f00-881f-402a-b6fd-837dad6bbd55
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 506d8f00-881f-402a-b6fd-837dad6bbd55
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 506d8f00-881f-402a-b6fd-837dad6bbd55
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 506d8f00-881f-402a-b6fd-837dad6bbd55
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 506d8f00-881f-402a-b6fd-837dad6bbd55
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 506d8f00-881f-402a-b6fd-837dad6bbd55
|
Copilot resolve the merge conflicts in this pull request |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c705d12c-0838-443f-b083-ed7e745fbfeb
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c705d12c-0838-443f-b083-ed7e745fbfeb
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c705d12c-0838-443f-b083-ed7e745fbfeb
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c705d12c-0838-443f-b083-ed7e745fbfeb
Karol Zadora-Przylecki (karolz-ms)
left a comment
There was a problem hiding this comment.
Reviewed the full diff with the branch checked out, tracing the new resource.launchConfigurationType property from ResourceSnapshotBuilder through the CLI snapshot mapper into the extension. The core feature logic holds up: Rust adapter selection across platforms, Java/Azure Functions multi-extension handling, install coalescing, registration waiting, and per-debugger-type suppression all behave as intended, and the unit + CodeLens + Hosting + E2E coverage is genuinely thorough.
6 findings, none blocking:
- Performance / resource usage (2): the background data lease is never released, keeping
aspire ps --followalive for the whole session; the E2E NuGet cache lives in the per-run temp root, forcing a cold restore every run. - Correctness / UX (1): an extension-registration timeout is reported as "extensions are disabled" regardless of the actual cause.
- Maintainability (2): the CodeLLDB extension ID is hardcoded instead of using the
capabilities.tsconstants this PR introduced;getEnvironmentWithoutE2EBridgeVariables()now injects an E2E variable rather than only stripping them. - Test coverage (1): the
HasPersistentLifetime()exclusion inGetLaunchConfigurationTypeis untested.
Karol Zadora-Przylecki (karolz-ms)
left a comment
There was a problem hiding this comment.
Overall looks good but please take a look at my comments, especially #19131 (comment) we do not want to contribute to "VS Code burning CPU at idle" problem
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
Refresh the final shutdown deadline when a blocked extension host resumes after the original deadline, and cover every final-phase deadline consumer with a deterministic regression test. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
The CI build failed due to test failure(s) that appear unrelated to the PR changes. These may be flaky tests. Suspected flaky test(s): Suggested actions:
You can re-run the failed jobs from the workflow run page. |
Description
Shows debugger setup guidance when a running resource supports debugging but its required VS Code extension is unavailable.
resource.launchConfigurationTypein resource snapshots.redhat.javaandvscjava.vscode-java-debug; Azure Functions requires the C# and Azure Functions extensions. Only missing requirements are installed.Older Hosting versions omit
resource.launchConfigurationType; the extension treats the missing property as unsupported and continues without showing a hint.The E2E harness also isolates NuGet packages per run and configures local package sources for workspace and external AppHosts so the debugger scenario consistently tests the current repository build. Workspace exclusions keep Pyright from scanning generated and test artifacts.
Fixes #17845
Validation
Checklist