Skip to content

[release/13.4] Fix VS Code AppHost launch path resolution#17560

Open
adamint wants to merge 1 commit into
release/13.4from
adamint/manual-backport-17408-to-release-13.4
Open

[release/13.4] Fix VS Code AppHost launch path resolution#17560
adamint wants to merge 1 commit into
release/13.4from
adamint/manual-backport-17408-to-release-13.4

Conversation

@adamint
Copy link
Copy Markdown
Member

@adamint adamint commented May 27, 2026

Backport of #17408 to release/13.4

/cc @davidfowl

Customer Impact

Customers launching or debugging Aspire AppHosts from VS Code can fail when the active file or launch config points at an SDK-style AppHost Program.cs instead of the project file. Configured AppHosts outside normal workspace discovery can also be missed.

Testing

Manual, using playground apps

Risk

Low. Localized VS Code AppHost discovery/launch path handling and CLI AppHost path/lifetime handling, covered by targeted tests.

Regression?

No

* Fix VS Code AppHost launch path resolution

* Use aspire ls for extension AppHost discovery

* Refresh AppHost discovery consumers on changes

* Use CLI language ids for AppHost discovery

* Handle AppHost discovery failures in editor commands

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add TypeScript AppHost launch discovery coverage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Harden AppHost discovery process handling

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Honor configured AppHosts in discovery

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Consolidate extension AppHost discovery

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix workspace test path separators

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix AppHost configured path selection

Ensure workspace AppHost selection checks all configured paths before falling back to the single discovered candidate, and add a regression test for multiple config files.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Keep extension-launched AppHost CLI alive

Keep the Aspire CLI process alive after VS Code launches an AppHost so the CLI-owned backchannel remains available for the lifetime of the extension-managed debug session. Propagate extension backchannel startup failures instead of returning success.

Also ignore generated .modules directories for polyglot AppHosts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Update extension build and CLI debug logging

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address AppHost launch review feedback

Keep extension-managed CLI runs alive only until the AppHost backchannel disconnects, align configured AppHost discovery UX with scanned candidates, restore dynamic debug fallbacks, and return no disposable for async workspace prompts.

Restore Yarn 1 frozen install compatibility for extension CI and avoid the npx package-name/bin-name mismatch in localization export.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address extension discovery review feedback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Stabilize pipeline unit tests without Docker

Use the existing fake container runtime resolver in the pipeline unit test context so the built-in container runtime check doesn't depend on Docker availability on CI runners.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Use ordinal comparison in pipeline test provider

Keep the fake container runtime service-provider lookup null-safe and explicit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Revert pipeline test isolation changes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Adam Ratzman <adam@adamratzman.com>
(cherry picked from commit 00eff9d)
Copilot AI review requested due to automatic review settings May 27, 2026 21:20
@github-actions
Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 17560

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 17560"

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Backports PR #17408 to release/13.4 to fix Aspire VS Code AppHost launch/discovery path handling (notably resolving SDK-style Program.cs editor/debug targets to the owning .csproj), unify the extension’s AppHost discovery on aspire ls --format json, and keep extension-launched AppHost CLI backchannels alive for the debug session lifetime.

Changes:

  • Adds CLI/extension backchannel lifetime handling so the CLI stays alive until the AppHost backchannel disconnects when launched by the VS Code extension.
  • Introduces a shared VS Code extension AppHostDiscoveryService (CLI-first, legacy fallback) and wires it through editor commands, debug configuration provider, and workspace tree discovery/refresh.
  • Expands CLI and extension test coverage for configured AppHosts outside the working directory and TypeScript AppHost scenarios.

Reviewed changes

Copilot reviewed 30 out of 31 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/Aspire.Cli.Tests/TestServices/TestAppHostCliBackchannel.cs Adds disconnect signaling to the test backchannel implementation.
tests/Aspire.Cli.Tests/Projects/ProjectLocatorTests.cs Adds coverage for configured AppHost selection messaging and unsupported configured AppHost paths.
tests/Aspire.Cli.Tests/DotNet/DotNetCliRunnerTests.cs Updates tests to assert CLI stays alive until backchannel disconnects and propagates backchannel failures.
tests/Aspire.Cli.Tests/Commands/PublishCommandPromptingIntegrationTests.cs Updates test backchannel to implement the new disconnect-wait API.
tests/Aspire.Cli.Tests/Commands/LsCommandTests.cs Adds coverage ensuring aspire ls --format json includes configured AppHosts outside the working directory.
src/Aspire.Cli/Templating/Templates/ts-starter/.gitignore Ignores generated .modules/ output in TS starter template.
src/Aspire.Cli/Templating/Templates/py-starter/.gitignore Ignores generated .modules/ output in Python starter template.
src/Aspire.Cli/Templating/Templates/java-starter/.gitignore Ignores generated .modules/ output in Java starter template.
src/Aspire.Cli/Projects/ProjectLocator.cs Ensures configured AppHost from settings is included as a discovery candidate (with validation/status reporting).
src/Aspire.Cli/Program.cs Ensures console debug logging still flows when hosted by the VS Code extension.
src/Aspire.Cli/DotNet/DotNetCliRunner.cs Keeps CLI alive for extension-launched AppHosts by awaiting backchannel disconnect.
src/Aspire.Cli/Backchannel/AppHostCliBackchannel.cs Adds a disconnect-wait primitive driven by JsonRpc.Disconnected.
playground/TypeScriptAppHost/.gitignore Ignores .modules/ in the playground TS AppHost.
extension/src/views/AppHostDataRepository.ts Switches workspace AppHost fetch to shared discovery service + change notifications.
extension/src/utils/workspace.ts Removes ad-hoc aspire ls spawning in favor of shared discovery service usage.
extension/src/utils/settings.ts Adds setting for AppHost discovery timeout (bounded minimum).
extension/src/utils/cliTypes.ts Fixes JSON file decoding by converting Uint8Array to UTF-8 string correctly.
extension/src/utils/appHostDiscovery.ts New shared discovery implementation (CLI-first, legacy fallback, caching/watchers, editor target resolution).
extension/src/test/workspace.test.ts Updates workspace tests to use the new shared discovery service and path-separator handling.
extension/src/test/aspireEditorCommandProvider.test.ts Adds coverage for editor Run/Debug target resolution and discovery-failure behavior.
extension/src/test/aspireDebugConfigurationProvider.test.ts Adds coverage for debug config normalization and dynamic debug config behavior.
extension/src/test/appHostDiscovery.test.ts Adds direct tests for discovery parsing, caching, watcher invalidation, timeouts, and configured-path selection.
extension/src/test/appHostDataRepository.test.ts Updates repository tests for shared discovery timing and config-file discovery stubbing.
extension/src/extension.ts Wires a single AppHostDiscoveryService instance through extension components.
extension/src/editor/AspireEditorCommandProvider.ts Replaces heuristic AppHost detection with discovery-backed candidate resolution and workspace context updates.
extension/src/debugger/AspireDebugConfigurationProvider.ts Resolves launch targets via discovery (including substituted variable paths) and improves dynamic config selection.
extension/package.nls.json Adds localized string for new discovery timeout setting.
extension/package.json Registers new aspire.appHostDiscoveryTimeoutMs setting metadata.
extension/loc/xlf/aspire-vscode.xlf Updates localization bundle with new/previously-missing strings.
extension/gulpfile.js Hardens l10n export by invoking the @vscode/l10n-dev CLI via Node (execFile).
.gitignore Ignores .modules/ at repo root.

all_project_file_candidates: string[];
}

const discoveryExcludePattern = '{**/artifacts/**,**/[Bb]in/**,**/[Oo]bj/**,**/node_modules/**,**/.git/**,**/.vs/**,**/.vscode-test/**,**/.idea/**,**/.aspire/modules/**}';
|| lowerSegment === '.git'
|| lowerSegment === '.vs'
|| lowerSegment === '.vscode-test'
|| lowerSegment === '.idea'
@github-actions
Copy link
Copy Markdown
Contributor

Re-running the failed jobs in the CI workflow for this pull request because 1 job was identified as retry-safe transient failures in the CI run attempt.
GitHub was asked to rerun all failed jobs for that attempt, and the rerun is being tracked in the rerun attempt.
The job links below point to the failed attempt jobs that matched the retry-safe transient failure rules.

@github-actions
Copy link
Copy Markdown
Contributor

CLI E2E Tests failed — 105 passed, 2 failed, 2 unknown (commit 6d2d74b)

Failed Tests

View all recordings
Status Test Recording
AddPackageInteractiveWhileAppHostRunningDetached ▶️ View recording
AddPackageWhileAppHostRunningDetached ▶️ View recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View recording
AgentInitCommand_DefaultSelection_InstallsDefaultSkills ▶️ View recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View recording
AgentMcpListStructuredLogsReturnsLogsFromStarterApp ▶️ View recording
AgentMcpListStructuredLogsReturnsLogsFromStarterApp_DevLocalhost ▶️ View recording
AgentMcpListStructuredLogsReturnsLogsFromStarterApp_Isolated ▶️ View recording
AllPublishMethodsBuildDockerImages ▶️ View recording
AspireAddAndStartWorkAgainstLegacyAppHostTs ▶️ View recording
AspireAddPackageVersionToDirectoryPackagesProps ▶️ View recording
AspireInitSingleFileAppHostRunsViaDotnetRunAppHost ▶️ View recording
AspireInitWithExistingAppHostDirRecreatesMissingNuGetConfigAndPreservesFiles ▶️ View recording
AspireInitWithSolutionFileGeneratesAppHostThatBuildsAgainstChannelHive ▶️ View recording
AspireStartUpdatesStaleTypeScriptAppHostPath ▶️ View recording
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps ▶️ View recording
AspireUpdateRemovesOrphanAppHostPackageVersionWhenSdkAlreadyCurrent ▶️ View recording
Banner_DisplayedOnFirstRun ▶️ View recording
Banner_DisplayedWithExplicitFlag ▶️ View recording
Banner_NotDisplayedWithNoLogoFlag ▶️ View recording
CertificatesClean_RemovesCertificates ▶️ View recording
CertificatesTrust_WithNoCert_CreatesAndTrustsCertificate ▶️ View recording
CertificatesTrust_WithUntrustedCert_TrustsCertificate ▶️ View recording
ConfigSetGet_CreatesNestedJsonFormat ▶️ View recording
CreateAndRunAspireStarterProject ▶️ View recording
CreateAndRunAspireStarterProjectWithBundle ▶️ View recording
CreateAndRunEmptyAppHostProject ▶️ View failure recording
CreateAndRunJavaEmptyAppHostProject ▶️ View recording
CreateAndRunJsReactProject ▶️ View recording
CreateAndRunPythonReactProject ▶️ View recording
CreateAndRunTypeScriptEmptyAppHostProject ▶️ View recording
CreateAndRunTypeScriptStarterProject ▶️ View recording
CreateJavaAppHostWithViteApp ▶️ View recording
CreateTypeScriptAppHostWithViteApp_AllowsGuestAppPackageManagerToDiffer ▶️ View recording
CreateTypeScriptAppHostWithViteApp_UsesConfiguredToolchain ▶️ View recording
DashboardRunWithAgentMcpListTracesReturnsNoTraces ▶️ View recording
DashboardRunWithAgentMcpListTracesReturnsNoTraces_DevLocalhost ▶️ View recording
DashboardRunWithOtelTracesReturnsNoTraces ▶️ View recording
DashboardRunWithOtelTracesReturnsNoTraces_DevLocalhost ▶️ View recording
DeployK8sBasicApiService ▶️ View recording
DeployK8sWithExternalHelmChart ▶️ View recording
DeployK8sWithGarnet ▶️ View recording
DeployK8sWithMongoDB ▶️ View recording
DeployK8sWithMySql ▶️ View recording
DeployK8sWithPostgres ▶️ View recording
DeployK8sWithRabbitMQ ▶️ View recording
DeployK8sWithRedis ▶️ View recording
DeployK8sWithSqlServer ▶️ View recording
DeployK8sWithValkey ▶️ View recording
DeployTypeScriptAppToKubernetes ▶️ View recording
DescribeCommandResolvesReplicaNames ▶️ View recording
DescribeCommandShowsRunningResources ▶️ View recording
DetachFormatJsonProducesValidJson ▶️ View recording
DetachFormatJsonProducesValidJsonWhenRestartingExistingInstance ▶️ View recording
DoPublishAndDeployListStepsWork ▶️ View recording
DocsCommand_RendersInteractiveMarkdownFromLocalSource ▶️ View recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View recording
DoctorCommand_TypeScriptAppHostReportsMissingConfiguredToolchain ▶️ View recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View recording
GatewayWithoutExternalEndpoint_FailsPublishWithGuidance ▶️ View recording
GeneratedAspireDevScript_StartsWatchMode_WithConfiguredToolchain ▶️ View recording
GlobalMigration_HandlesCommentsAndTrailingCommas ▶️ View recording
GlobalMigration_HandlesMalformedLegacyJson ▶️ View recording
GlobalMigration_PreservesAllValueTypes ▶️ View recording
GlobalMigration_SkipsWhenNewConfigExists ▶️ View recording
GlobalSettings_MigratedFromLegacyFormat ▶️ View recording
IngressWithoutExternalEndpoint_FailsPublishWithGuidance ▶️ View recording
InitTypeScriptAppHost_AugmentsExistingViteRepoInWorkspaceSubdirectory ▶️ View recording
InteractiveCSharpInitCreatesExpectedFiles ▶️ View recording
InvalidAppHostPathWithComments_IsHealedOnRun ▶️ View recording
JavaScriptHostingApisRunFromTypeScriptAppHost ▶️ View recording
LatestCliCanStartStableChannelAppHost ▶️ View failure recording
LatestCliCanStartStableChannelTypeScriptAppHost ▶️ View recording
LegacySettingsMigration_AdjustsRelativeAppHostPath ▶️ View recording
LogsCommandShowsResourceLogs ▶️ View recording
OtelLogsReturnsStructuredLogsFromStarterApp ▶️ View recording
OtelLogsReturnsStructuredLogsFromStarterAppIsolated ▶️ View recording
PsCommandListsRunningAppHost ▶️ View recording
PsFormatJsonOutputsOnlyJsonToStdout ▶️ View recording
PublishJavaScriptPatternsGeneratesExpectedDockerComposeArtifacts ▶️ View recording
PublishWithConfigureEnvFileUpdatesEnvOutput ▶️ View recording
PublishWithDockerComposeServiceCallbackSucceeds ▶️ View recording
PublishWithoutOutputPathUsesAppHostDirectoryDefault ▶️ View recording
ResourceCommand_FailedExecution_DisplaysAppHostLogPathAndLogContainsEntries ▶️ View recording
ResourceCommand_SetAndDeleteParameterUpdatesDescribeOutput ▶️ View recording
RestoreGeneratesSdkFiles ▶️ View recording
RestoreGeneratesSdkFiles_WithConfiguredToolchain ▶️ View recording
RestoreRefreshesGeneratedSdkAfterAddingIntegration ▶️ View recording
RestoreSupportsConfigOnlyHelperPackageAndCrossPackageTypes ▶️ View recording
RunFromParentDirectory_UsesExistingConfigNearAppHost ▶️ View recording
RunReportsSyntaxErrorsForDotNetAppHost ▶️ View recording
RunReportsSyntaxErrorsForTypeScriptAppHost ▶️ View recording
SecretCrudOnDotNetAppHost ▶️ View recording
SecretCrudOnTypeScriptAppHost ▶️ View recording
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels ▶️ View recording
StartAndWaitForTypeScriptSqlServerAppHostWithNativeAssets ▶️ View recording
StartReportsSyntaxErrorsForDotNetAppHost ▶️ View recording
StartReportsSyntaxErrorsForTypeScriptAppHost ▶️ View recording
StopAllAppHostsFromAppHostDirectory ▶️ View recording
StopJavaPolyglotAppHostUsingApphostDirectory ▶️ View recording
StopNonInteractiveSingleAppHost ▶️ View recording
StopTypeScriptPolyglotAppHostUsingApphostDirectory ▶️ View recording
StopWithNoRunningAppHostExitsSuccessfully ▶️ View recording
UnAwaitedChainsCompileWithAutoResolvePromises ▶️ View recording
UpdateProjectChannelToStable_CSharpEmptyAppHost_PreservesAspireConfigChannel ▶️ View recording
UpdateProjectChannelToStable_CSharpSingleFileInit_PreservesAspireConfigChannel ▶️ View recording
UpdateProjectChannelToStable_TypeScriptSingleFileInit_PreservesAspireConfigChannel ▶️ View recording
UpdateProjectChannelToStable_TypeScript_PreviewsStablePackagesAndPreservesChannel ▶️ View recording

📹 Recordings uploaded automatically from CI run #26539425937

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.

3 participants