Skip to content

Fix guest AppHost Ctrl+C shutdown#17642

Closed
danegsta wants to merge 12 commits into
mainfrom
danegsta/ctrl-c-regression-check
Closed

Fix guest AppHost Ctrl+C shutdown#17642
danegsta wants to merge 12 commits into
mainfrom
danegsta/ctrl-c-regression-check

Conversation

@danegsta

Copy link
Copy Markdown
Member

Description

aspire run could race graceful shutdown for guest AppHosts: cancellation stopped the CLI wait immediately and the guest launcher could force-kill the guest process tree before the process handled Ctrl+C/Ctrl+Break. On Windows, the AppHost server also needs different fallback behavior from the guest because DCP is in the server process tree and should not be killed by server fallback cleanup.

This change launches guest AppHost processes and AppHost servers in Windows process groups, adds a ProcessShutdownService process-group shutdown path that sends CTRL_BREAK_EVENT, and routes guest/server cancellation through the shared graceful-then-force shutdown flow. Guest fallback still kills the whole process tree. Server fallback kills the whole tree on Unix, but only the target server process on Windows so in-tree DCP can keep handling cleanup.

User-facing behavior

Pressing Ctrl+C during aspire run for guest AppHosts now gives the guest and AppHost server a graceful shutdown window before force termination, aligning the run path with the existing shutdown coordinator behavior.

Validation:

  • dotnet test --project tests/Aspire.Cli.Tests/Aspire.Cli.Tests.csproj --no-launch-profile -- --filter-class "*.ProcessGuestLauncherTests" --filter-class "*.ProcessShutdownServiceTests" --filter-class "*.AppHostServerSessionTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"

Fixes # (issue)

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No

Launch guest AppHost processes and AppHost servers in Windows process groups so aspire run can request graceful shutdown with CTRL_BREAK_EVENT before falling back to force termination. Route guest and server cancellation through ProcessShutdownService so guest fallbacks can kill the full tree while server fallbacks avoid killing in-tree DCP on Windows.

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

github-actions Bot commented May 29, 2026

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 -- 17642

Or

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

danegsta and others added 2 commits May 28, 2026 18:54
Treat an AppHost server that exits before shutdown coordination can read its start time as already stopped. This preserves process identity validation and avoids signaling a reused PID without a start-time guard.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve ProcessGuestLauncher changes from main by preserving the new lifecycle logging while retaining graceful process-group shutdown before force-kill fallback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danegsta danegsta marked this pull request as ready for review May 29, 2026 02:04
@danegsta danegsta requested a review from mitchdenny as a code owner May 29, 2026 02:04
Copilot AI review requested due to automatic review settings May 29, 2026 02:04

Copilot AI 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.

Pull request overview

This PR fixes a Ctrl+C shutdown race for guest AppHosts in aspire run by ensuring cancellation follows a graceful-then-force termination flow (instead of immediately force-killing), and by adding a Windows process-group shutdown path that uses CTRL_BREAK_EVENT. It also adjusts Windows fallback behavior for AppHost servers to avoid tearing down in-tree DCP during server cleanup.

Changes:

  • Launch guest AppHosts and AppHost server processes in Windows process groups, enabling targeted CTRL_BREAK_EVENT shutdown.
  • Add process-group graceful shutdown support to ProcessShutdownService/ProcessSignaler, and route guest/server cancellation through the coordinated graceful-then-force flow.
  • Add/adjust tests to validate Unix SIGTERM graceful behavior and Windows CTRL_BREAK_EVENT behavior.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/Aspire.Cli.Tests/Projects/ProcessGuestLauncherTests.cs Adds coverage for cancellation-driven graceful shutdown on Unix and Windows (process-group Ctrl+Break).
tests/Aspire.Cli.Tests/Projects/GuestAppHostProjectTests.cs Wires a ProcessShutdownService into test project construction to match updated runtime requirements.
src/Shared/ProcessSignaler.cs Adds a process-group graceful shutdown API and Windows GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT) support.
src/Aspire.Cli/Projects/ProcessGuestLauncher.cs Starts guest processes in a Windows process group and uses coordinated graceful-then-force shutdown on cancellation.
src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs Starts the AppHost server process in a Windows process group.
src/Aspire.Cli/Projects/GuestRuntime.cs Plumbs an optional ProcessShutdownService through to the default guest launcher.
src/Aspire.Cli/Projects/GuestAppHostProject.cs Requires and threads ProcessShutdownService through guest runtime and server session startup paths.
src/Aspire.Cli/Projects/DotNetBasedAppHostServerProject.cs Starts the dotnet-based AppHost server in a Windows process group.
src/Aspire.Cli/Projects/AppHostServerSession.cs Adds optional coordinated shutdown for the server process; adjusts Windows fallback kill semantics.
src/Aspire.Cli/Processes/ProcessShutdownService.cs Introduces process-group shutdown flow and makes force-kill scope (tree vs single process) configurable per call.

Comment thread src/Aspire.Cli/Projects/ProcessGuestLauncher.cs
@danegsta

Copy link
Copy Markdown
Member Author

PR Testing Report

PR Information

CLI Version Verification

  • Expected Commit: 0584470
  • Installed Version: 13.5.0-pr.17642.g05844707
  • Status: Verified - installed version includes short SHA g05844707

Changes Analyzed

Files Changed

  • src/Aspire.Cli/Processes/ProcessShutdownService.cs - process-group shutdown and force-kill fallback coordination
  • src/Aspire.Cli/Projects/AppHostServerSession.cs - graceful server disposal path and Windows target-only fallback
  • src/Aspire.Cli/Projects/DotNetBasedAppHostServerProject.cs - Windows process-group launch for SDK-mode server
  • src/Aspire.Cli/Projects/GuestAppHostProject.cs - passes shutdown coordinator to server and guest runtime
  • src/Aspire.Cli/Projects/GuestRuntime.cs - passes shutdown coordinator to process guest launcher
  • src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs - Windows process-group launch for prebuilt server
  • src/Aspire.Cli/Projects/ProcessGuestLauncher.cs - guest process-group launch and graceful shutdown before force-kill fallback
  • src/Shared/ProcessSignaler.cs - CTRL_BREAK_EVENT process-group signaling on Windows
  • tests/Aspire.Cli.Tests/Projects/GuestAppHostProjectTests.cs - updated test construction
  • tests/Aspire.Cli.Tests/Projects/ProcessGuestLauncherTests.cs - Unix and Windows process-shutdown behavior coverage

Change Categories

  • CLI changes detected
  • Hosting integration changes
  • Dashboard changes
  • Template changes
  • Client/Component changes
  • Test changes

Test Scenarios Executed

Scenario 1: PR CLI install and version verification

Objective: Install the PR dogfood CLI and verify it matches the PR head commit.
Coverage Type: Happy path
Status: Passed

Steps:

  1. Installed PR Fix guest AppHost Ctrl+C shutdown #17642 CLI using the dogfood shell command with isolated --install-path, --skip-path, and --skip-extension.
  2. Ran the installed binary directly with --version.
  3. Compared the version output with the PR head commit.

Evidence:

  • Install log: /var/folders/97/44fp_02d4kd90s9p6sy_jwgh0000gn/T/aspire-pr-test.liF9xI1WNv/install.log
  • Install info: /var/folders/97/44fp_02d4kd90s9p6sy_jwgh0000gn/T/aspire-pr-test.liF9xI1WNv/install-info.txt

Observations:

  • Installed CLI version: 13.5.0-pr.17642.g05844707
  • Version includes the expected short SHA g05844707.

Scenario 2: Template smoke test

Objective: Verify the PR CLI can acquire templates from the PR hive and create a fresh project non-interactively.
Coverage Type: Happy path
Status: Passed

Steps:

  1. Created a fresh aspire-starter app using --source pointed at the PR hive and --version set to the installed CLI version.
  2. Used non-interactive prompt-suppression flags including --test-framework None, --use-redis-cache false, --localhost-tld false, and --suppress-agent-init.
  3. Captured generated file list.

Evidence:

  • Output: /var/folders/97/44fp_02d4kd90s9p6sy_jwgh0000gn/T/aspire-pr-test.liF9xI1WNv/scenario-template-smoke/new-output.txt
  • Generated files: /var/folders/97/44fp_02d4kd90s9p6sy_jwgh0000gn/T/aspire-pr-test.liF9xI1WNv/scenario-template-smoke/generated-files.txt

Observations:

  • Project creation completed successfully.
  • Generated AppHost, ApiService, ServiceDefaults, Web, solution, and Aspire config files.

Scenario 3: TypeScript guest AppHost shutdown on macOS

Objective: Exercise the changed guest AppHost run/shutdown path with a fresh TypeScript AppHost and verify SIGINT-triggered shutdown exits cleanly.
Coverage Type: Happy path / platform shutdown behavior
Status: Passed

Steps:

  1. Created a fresh aspire-ts-empty project using the PR hive and installed version.
  2. Ran aspire run --apphost <apphost.mts> --non-interactive --nologo with the PR CLI.
  3. Waited until startup reached a detectable AppHost state.
  4. Sent SIGINT to the running PR CLI process.
  5. Verified aspire run exited with code 0.

Evidence:

  • Project creation output: /var/folders/97/44fp_02d4kd90s9p6sy_jwgh0000gn/T/aspire-pr-test.liF9xI1WNv/scenario-ts-guest-shutdown/new-output.txt
  • Run output: /var/folders/97/44fp_02d4kd90s9p6sy_jwgh0000gn/T/aspire-pr-test.liF9xI1WNv/scenario-ts-guest-shutdown/run-output.txt
  • Exit code: /var/folders/97/44fp_02d4kd90s9p6sy_jwgh0000gn/T/aspire-pr-test.liF9xI1WNv/scenario-ts-guest-shutdown/run-exit-code.txt

Observations:

  • Startup reached the AppHost connection phase.
  • Shutdown output included Stopping Aspire..
  • Process exited successfully with code 0 after SIGINT.
  • Windows CTRL_BREAK_EVENT behavior is covered by the Windows-only test in the PR and was not executed on this macOS host.

Scenario 4: Missing AppHost path

Objective: Verify a bad --apphost path fails safely without prompting or hanging.
Coverage Type: Unhappy path
Status: Passed

Steps:

  1. Ran aspire describe --apphost <missing path> --non-interactive with the PR CLI.
  2. Captured output and exit code.

Evidence:

  • Output: /var/folders/97/44fp_02d4kd90s9p6sy_jwgh0000gn/T/aspire-pr-test.liF9xI1WNv/scenario-negative-apphost/describe-output.txt
  • Exit code: /var/folders/97/44fp_02d4kd90s9p6sy_jwgh0000gn/T/aspire-pr-test.liF9xI1WNv/scenario-negative-apphost/exit-code.txt

Expected Unhappy-Path Outcome: A non-zero exit code with a clear error that the AppHost project does not exist.

Observations:

  • Command exited with code 7.
  • Output included: The --apphost option specified a project that does not exist.

Summary

Scenario Status Notes
PR CLI install and version verification Passed Version matched PR head short SHA
Template smoke test Passed Fresh starter project created from PR hive
TypeScript guest AppHost shutdown on macOS Passed SIGINT shutdown exited with code 0
Missing AppHost path Passed Failed safely with clear error and exit code 7

Overall Result

PR VERIFIED

Recommendations

  • For full platform confidence, also run the Windows-only test or an equivalent Windows manual scenario because the CTRL_BREAK_EVENT path cannot be exercised from this macOS host.

Treat a guest AppHost process that exits before shutdown coordination can read its start time as already stopped. This preserves process identity validation and avoids signaling a reused PID without a start-time guard.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/Shared/ProcessSignaler.cs Outdated
Comment thread src/Aspire.Cli/Projects/ProcessGuestLauncher.cs Outdated
Comment thread src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs Outdated
Comment thread src/Aspire.Cli/Projects/DotNetBasedAppHostServerProject.cs Outdated

@JamesNK JamesNK May 29, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think StopProcessGroupAsync, and this, plus WaitForDrainAsync needs a timeout.

Basically, after the CT is raised, there should be 3 seconds to do clean up before an OCE is thrown out of the method.

danegsta and others added 2 commits May 28, 2026 19:27
Add a ProcessStartInfo extension for Windows process-group creation and use it for guest AppHost and AppHost server launches.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Derive the AppHost startup cancellation wait from ProcessShutdownService so aspire run allows the graceful shutdown monitor and force-kill verification windows to complete instead of timing out after five seconds.

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

@mitchdenny mitchdenny left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

One inline comment on the Windows CTRL_BREAK test.

Comment thread tests/Aspire.Cli.Tests/Projects/ProcessGuestLauncherTests.cs Outdated
danegsta and others added 6 commits May 28, 2026 19:49
Make ProcessShutdownService termination monitoring configurable and use a shorter timeout for aspire run owned guest/server processes while keeping the longer default for detached/start/stop paths. Relax guest launcher test startup waits for slower CI process startup.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Include the ProcessSignaler diagnostic update for process-group shutdown signaling.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use fresh timeout cancellation tokens for aspire run guest and server shutdown coordination instead of passing CancellationToken.None after command cancellation. Keep shutdown bounded while avoiding the already-canceled command token.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Let RunCommand own the shorter run shutdown budget and pass it down explicitly to guest AppHost and AppHost server shutdown coordination. Use bounded timeout tokens instead of CancellationToken.None and add AppHost server shutdown logging before the shared shutdown service takes over.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Do not gate Windows process-group CTRL_BREAK_EVENT delivery on process start-time validation. Aspire owns these child process groups directly, and skipping the event after a failed PID lookup makes the shutdown service wait as though a graceful signal was sent.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Cover TryGetRunningProcess with a real child process start-time round trip and a mismatched start-time rejection, matching how guest AppHost shutdown captures process identity.

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

Copy link
Copy Markdown
Contributor

CLI E2E Tests unknown — 107 passed, 0 failed, 2 unknown (commit 2d94bd6)

View all recordings
Status Test Recording Job Artifacts
AddPackageInteractiveWhileAppHostRunningDetached Recording #78435201470 Logs
AddPackageWhileAppHostRunningDetached Recording #78435201470 Logs
AgentCommands_AllHelpOutputs_AreCorrect Recording #78435201582 Logs
AgentInitCommand_DefaultSelection_InstallsDefaultSkills Recording #78435201582 Logs
AgentInitCommand_MigratesDeprecatedConfig Recording #78435201582 Logs
AgentMcpListStructuredLogsReturnsLogsFromStarterApp Recording #78435201502 Logs
AgentMcpListStructuredLogsReturnsLogsFromStarterApp_DevLocalhost Recording #78435201502 Logs
AgentMcpListStructuredLogsReturnsLogsFromStarterApp_Isolated Recording #78435201502 Logs
AllPublishMethodsBuildDockerImages Recording #78435201463 Logs
AspireAddAndStartWorkAgainstLegacyAppHostTs Recording #78435201686 Logs
AspireAddPackageVersionToDirectoryPackagesProps Recording #78435201458 Logs
AspireInitSingleFileAppHostRunsViaDotnetRunAppHost Recording #78435201466 Logs
AspireInitWithExistingAppHostDirRecreatesMissingNuGetConfigAndPreservesFiles Recording #78435201719 Logs
AspireInitWithSolutionFileGeneratesAppHostThatBuildsAgainstChannelHive Recording #78435201719 Logs
AspireStartUpdatesStaleTypeScriptAppHostPath Recording #78435201756 Logs
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps Recording #78435201458 Logs
AspireUpdateRemovesOrphanAppHostPackageVersionWhenSdkAlreadyCurrent Recording #78435201458 Logs
Banner_DisplayedOnFirstRun Recording #78435201705 Logs
Banner_DisplayedWithExplicitFlag Recording #78435201705 Logs
Banner_NotDisplayedWithNoLogoFlag Recording #78435201705 Logs
CertificatesClean_RemovesCertificates Recording #78435201399 Logs
CertificatesTrust_WithNoCert_CreatesAndTrustsCertificate Recording #78435201399 Logs
CertificatesTrust_WithUntrustedCert_TrustsCertificate Recording #78435201399 Logs
ConfigSetGet_CreatesNestedJsonFormat Recording #78435201454 Logs
CreateAndRunAspireStarterProject Recording #78435201467 Logs
CreateAndRunAspireStarterProjectWithBundle Recording #78435201478 Logs
CreateAndRunEmptyAppHostProject Recording #78435201473 Logs
CreateAndRunJavaEmptyAppHostProject Recording #78435201699 Logs
CreateAndRunJsReactProject Recording #78435201762 Logs
CreateAndRunPythonReactProject Recording #78435201629 Logs
CreateAndRunTypeScriptEmptyAppHostProject Recording #78435201740 Logs
CreateAndRunTypeScriptStarterProject Recording #78435201437 Logs
CreateJavaAppHostWithViteApp Recording #78435201720 Logs
CreateTypeScriptAppHostWithViteApp_AllowsGuestAppPackageManagerToDiffer Recording #78435201493 Logs
CreateTypeScriptAppHostWithViteApp_UsesConfiguredToolchain Recording #78435201493 Logs
DashboardRunWithAgentMcpListTracesReturnsNoTraces Recording #78435201448 Logs
DashboardRunWithAgentMcpListTracesReturnsNoTraces_DevLocalhost Recording #78435201448 Logs
DashboardRunWithOtelTracesReturnsNoTraces Recording #78435201448 Logs
DashboardRunWithOtelTracesReturnsNoTraces_DevLocalhost Recording #78435201448 Logs
DeployK8sBasicApiService Recording #78435201524 Logs
DeployK8sWithExternalHelmChart Recording #78435201480 Logs
DeployK8sWithGarnet Recording #78435201504 Logs
DeployK8sWithMongoDB Recording #78435201722 Logs
DeployK8sWithMySql Recording #78435201556 Logs
DeployK8sWithPostgres Recording #78435201511 Logs
DeployK8sWithRabbitMQ Recording #78435201406 Logs
DeployK8sWithRedis Recording #78435201697 Logs
DeployK8sWithSqlServer Recording #78435201488 Logs
DeployK8sWithValkey Recording #78435201436 Logs
DeployTypeScriptAppToKubernetes Recording #78435201410 Logs
DescribeCommandResolvesReplicaNames Recording #78435201519 Logs
DescribeCommandShowsRunningResources Recording #78435201519 Logs
DetachFormatJsonProducesValidJson Recording #78435201474 Logs
DetachFormatJsonProducesValidJsonWhenRestartingExistingInstance Recording #78435201474 Logs
DoPublishAndDeployListStepsWork Recording #78435201708 Logs
DocsCommand_RendersInteractiveMarkdownFromLocalSource Recording #78435201559 Logs
DoctorCommand_DetectsDeprecatedAgentConfig Recording #78435201582 Logs
DoctorCommand_TypeScriptAppHostReportsMissingConfiguredToolchain Recording #78435201444 Logs
DoctorCommand_WithSslCertDir_ShowsTrusted Recording #78435201444 Logs
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted Recording #78435201444 Logs
GatewayWithoutExternalEndpoint_FailsPublishWithGuidance Recording #78435201442 Logs
GeneratedAspireDevScript_StartsWatchMode_WithConfiguredToolchain Recording #78435201493 Logs
GlobalMigration_HandlesCommentsAndTrailingCommas Recording #78435201454 Logs
GlobalMigration_HandlesMalformedLegacyJson Recording #78435201454 Logs
GlobalMigration_PreservesAllValueTypes Recording #78435201454 Logs
GlobalMigration_SkipsWhenNewConfigExists Recording #78435201454 Logs
GlobalSettings_MigratedFromLegacyFormat Recording #78435201454 Logs
IngressWithoutExternalEndpoint_FailsPublishWithGuidance Recording #78435201442 Logs
InitTypeScriptAppHost_AugmentsExistingViteRepoInWorkspaceSubdirectory Recording #78435201493 Logs
InteractiveCSharpInitCreatesExpectedFiles Recording #78435201683 Logs
InvalidAppHostPathWithComments_IsHealedOnRun Recording #78435201605 Logs
JavaScriptHostingApisRunFromTypeScriptAppHost Recording #78435201463 Logs
LatestCliCanStartStableChannelAppHost Recording #78435201467 Logs
LatestCliCanStartStableChannelTypeScriptAppHost Recording #78435201467 Logs
LegacySettingsMigration_AdjustsRelativeAppHostPath Recording #78435201756 Logs
LogsCommandShowsResourceLogs Recording #78435201728 Logs
OtelLogsReturnsStructuredLogsFromStarterApp Recording #78435201459 Logs
OtelLogsReturnsStructuredLogsFromStarterAppIsolated Recording #78435201459 Logs
PsCommandListsRunningAppHost Recording #78435201498 Logs
PsFormatJsonOutputsOnlyJsonToStdout Recording #78435201498 Logs
PublishJavaScriptPatternsGeneratesExpectedDockerComposeArtifacts Recording #78435201751 Logs
PublishWithConfigureEnvFileUpdatesEnvOutput Recording #78435201751 Logs
PublishWithDockerComposeServiceCallbackSucceeds Recording #78435201751 Logs
PublishWithoutOutputPathUsesAppHostDirectoryDefault Recording #78435201751 Logs
ResourceCommand_FailedExecution_DisplaysAppHostLogPathAndLogContainsEntries Recording #78435201725 Logs
ResourceCommand_SetAndDeleteParameterUpdatesDescribeOutput Recording #78435201725 Logs
RestoreGeneratesSdkFiles Recording #78435201435 Logs
RestoreGeneratesSdkFiles_WithConfiguredToolchain Recording #78435201391 Logs
RestoreRefreshesGeneratedSdkAfterAddingIntegration Recording #78435201391 Logs
RestoreSupportsConfigOnlyHelperPackageAndCrossPackageTypes Recording #78435201657 Logs
RunFromParentDirectory_UsesExistingConfigNearAppHost Recording #78435201481 Logs
RunReportsSyntaxErrorsForDotNetAppHost Recording #78435201635 Logs
RunReportsSyntaxErrorsForTypeScriptAppHost Recording #78435201635 Logs
SecretCrudOnDotNetAppHost Recording #78435201737 Logs
SecretCrudOnTypeScriptAppHost Recording #78435201489 Logs
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels Recording #78435201682 Logs
StartAndWaitForTypeScriptSqlServerAppHostWithNativeAssets Recording #78435201654 Logs
StartReportsSyntaxErrorsForDotNetAppHost Recording #78435201635 Logs
StartReportsSyntaxErrorsForTypeScriptAppHost Recording #78435201635 Logs
StopAllAppHostsFromAppHostDirectory Recording #78435201700 Logs
StopJavaPolyglotAppHostUsingApphostDirectory Recording #78435201645 Logs
StopNonInteractiveSingleAppHost Recording #78435201700 Logs
StopTypeScriptPolyglotAppHostUsingApphostDirectory Recording #78435201475 Logs
StopWithNoRunningAppHostExitsSuccessfully Recording #78435201470 Logs
UnAwaitedChainsCompileWithAutoResolvePromises Recording #78435201391 Logs
UpdateProjectChannelToStable_CSharpEmptyAppHost_PreservesAspireConfigChannel Recording #78435201693 Logs
UpdateProjectChannelToStable_CSharpSingleFileInit_PreservesAspireConfigChannel Recording #78435201693 Logs
UpdateProjectChannelToStable_TypeScriptSingleFileInit_PreservesAspireConfigChannel Recording #78435201693 Logs
UpdateProjectChannelToStable_TypeScript_PreviewsStablePackagesAndPreservesChannel Recording #78435201693 Logs

📹 Recordings uploaded automatically from CI run #26616580186

@radical

radical commented Jun 3, 2026

Copy link
Copy Markdown
Member

Test failure looks relevant.

Aspire.Cli.Tests.Projects.ProcessGuestLauncherTests.LaunchAsync_CancellationUsesWindowsCtrlBreakBeforeForceKill
Assert.Equal() Failure: Values differ
Expected: 0
Actual:   -1
   at Aspire.Cli.Tests.Projects.ProcessGuestLauncherTests.LaunchAsync_CancellationUsesWindowsCtrlBreakBeforeForceKill() in D:\a\aspire\aspire\tests\Aspire.Cli.Tests\Projects\ProcessGuestLauncherTests.cs:line 104
--- End of stack trace from previous location ---

### StdOut
Temporary workspace created at: C:\Users\runneradmin\AppData\Local\Temp\Aspire.Cli.Tests\TemporaryWorkspaces\2bcab3c4-71ed-451c-bb4c-a9c21bb15a7e
Disposing temporary workspace at: C:\Users\runneradmin\AppData\Local\Temp\Aspire.Cli.Tests\T

@radical radical added the needs-author-action An issue or pull request that requires more info or actions from the author. label Jun 3, 2026
Comment on lines +22 to 24
TimeSpan? processTerminationTimeout = null,
TimeSpan? processShutdownTimeout = null,
Func<Task>? afterLaunchAsync = null);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

CancellationToken should be the last parameter.

@JamesNK

JamesNK commented Jun 3, 2026

Copy link
Copy Markdown
Member

My CTRL+C PR merged. Rebase/merge latest main.

@IEvangelist

Copy link
Copy Markdown
Member

PR Testing Report

PR Information

CLI Version Verification

  • Expected Commit: 2d94bd6
  • Installed Version: 13.5.0-pr.17642.g2d94bd6d
  • Status: Verified

Changes Analyzed

Files Changed

  • src/Aspire.Cli/Commands/RunCommand.cs
  • src/Aspire.Cli/Processes/ProcessShutdownService.cs
  • src/Aspire.Cli/Projects/AppHostProjectContext.cs
  • src/Aspire.Cli/Projects/AppHostServerSession.cs
  • src/Aspire.Cli/Projects/DotNetBasedAppHostServerProject.cs
  • src/Aspire.Cli/Projects/ExtensionGuestLauncher.cs
  • src/Aspire.Cli/Projects/GuestAppHostProject.cs
  • src/Aspire.Cli/Projects/GuestRuntime.cs
  • src/Aspire.Cli/Projects/IGuestProcessLauncher.cs
  • src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs
  • src/Aspire.Cli/Projects/ProcessGuestLauncher.cs
  • src/Aspire.Cli/Utils/ProcessStartInfoExtensions.cs
  • src/Shared/ProcessSignaler.cs
  • tests/Aspire.Cli.Tests/Processes/ProcessShutdownServiceTests.cs
  • tests/Aspire.Cli.Tests/Projects/GuestAppHostProjectTests.cs
  • tests/Aspire.Cli.Tests/Projects/GuestRuntimeTests.cs
  • tests/Aspire.Cli.Tests/Projects/ProcessGuestLauncherTests.cs

Change Categories

  • CLI changes detected - guest AppHost/server process-group shutdown, cancellation, and timeout handling
  • Hosting integration changes
  • Dashboard changes
  • Template changes
  • Client/Component changes
  • Test changes detected - process shutdown and guest launcher/runtime coverage

Test Scenarios Executed

Scenario 1: Guest AppHost Ctrl+C shutdown

Objective: Verify a foreground TypeScript guest AppHost exits promptly when interrupted with Ctrl+C and leaves no related processes behind.
Coverage Type: Happy path
Status: Passed

Steps:

  1. Created a fresh aspire-ts-starter project from the PR hive.
  2. Ran aspire run --apphost apphost.mts with the PR CLI.
  3. Waited for the dashboard prompt, then sent Ctrl+C through the terminal session.
  4. Checked for lingering aspire, dotnet, node, npm, or cmd processes scoped to the scenario project root.

Evidence:

  • Terminal output showed the dashboard URL and returned to the PowerShell prompt after Ctrl+C.
  • Sanitized command/process summary: C:\Users\dapine\.copilot\session-state\9c942971-40eb-42c9-a38a-93cf9008f972\files\pr-17642-report\guest-ctrl-c-summary.txt

Observations:

  • No related processes remained after Ctrl+C.

Scenario 2: Guest AppHost start/stop shutdown

Objective: Verify detached TypeScript guest AppHost lifecycle works with aspire start, wait, describe, and stop.
Coverage Type: Happy path
Status: Passed

Steps:

  1. Created a fresh aspire-ts-starter project from the PR hive.
  2. Started with aspire start --isolated --apphost apphost.mts.
  3. Waited for app and frontend to reach up.
  4. Captured resource status with aspire describe and stopped with aspire stop --apphost apphost.mts.
  5. Checked that related processes were gone after shutdown completed.

Evidence:

  • aspire describe confirmed expected resources reached running states; raw JSON was not included because it contains environment values.
  • Sanitized command/process summary: C:\Users\dapine\.copilot\session-state\9c942971-40eb-42c9-a38a-93cf9008f972\files\pr-17642-report\guest-start-stop-summary.txt

Observations:

  • Stop returned success and resources were cleaned up after shutdown completed.

Scenario 3: .NET AppHost smoke regression

Objective: Verify normal C#/.NET AppHost execution still works after the CLI process-group changes.
Coverage Type: Happy path
Status: Passed

Steps:

  1. Created a fresh aspire-starter project from the PR hive.
  2. Started with aspire start --isolated --apphost <csproj>.
  3. Waited for apiservice and webfrontend to reach up.
  4. Captured resource status with aspire describe and stopped with aspire stop --apphost <csproj>.
  5. Checked that related processes were gone after shutdown completed.

Evidence:

  • aspire describe confirmed expected resources reached running/healthy states; raw JSON was not included because it contains environment values.
  • Sanitized command/process summary: C:\Users\dapine\.copilot\session-state\9c942971-40eb-42c9-a38a-93cf9008f972\files\pr-17642-report\dotnet-smoke-summary.txt

Observations:

  • Normal .NET AppHost start/stop behavior was not regressed.

Scenario 4: Invalid guest AppHost startup cleanup

Objective: Verify a guest AppHost startup failure is reported clearly and the temporary server/guest processes are cleaned up.
Coverage Type: Unhappy path
Status: Passed

Steps:

  1. Created a fresh aspire-ts-empty project from the PR hive.
  2. Injected an intentional exception into apphost.mts.
  3. Ran aspire run --apphost apphost.mts.
  4. Verified non-zero exit code and expected error text.
  5. Checked for lingering related processes.

Evidence:

  • Output contained Intentional startup failure for PR shutdown cleanup validation and exit code 2.
  • Sanitized command/process summary: C:\Users\dapine\.copilot\session-state\9c942971-40eb-42c9-a38a-93cf9008f972\files\pr-17642-report\invalid-guest-startup-summary.txt

Observations:

  • The CLI surfaced the startup failure and cleaned up related processes.

Expected Unhappy-Path Outcome: Non-zero exit code with the intentional startup error; no lingering related processes.


Scenario 5: Shutdown-resisting guest force cleanup

Objective: Verify a guest AppHost that ignores shutdown signals is still force-cleaned within the shutdown budget.
Coverage Type: Boundary/unhappy path
Status: Passed

Steps:

  1. Created a fresh aspire-ts-empty project from the PR hive.
  2. Installed signal handlers that ignore SIGBREAK, SIGINT, and SIGTERM and keep the event loop alive.
  3. Ran aspire run --apphost apphost.mts with the PR CLI.
  4. Sent Ctrl+C through the terminal session.
  5. Checked for lingering related processes.

Evidence:

  • Terminal output showed the dashboard URL and returned to the PowerShell prompt after Ctrl+C.
  • Sanitized command/process summary: C:\Users\dapine\.copilot\session-state\9c942971-40eb-42c9-a38a-93cf9008f972\files\pr-17642-report\shutdown-resisting-guest-summary.txt

Observations:

  • The CLI returned to the shell and no related guest processes remained.

Expected Unhappy-Path Outcome: Ctrl+C returns to the shell and no related guest processes remain even though the guest ignored graceful signals.


Summary

Scenario Status Notes
Guest AppHost Ctrl+C shutdown Passed No related processes remained after Ctrl+C.
Guest AppHost start/stop shutdown Passed Start/wait/describe/stop commands succeeded; app resource processes were gone after shutdown completed.
.NET AppHost smoke regression Passed Start/wait/describe/stop commands succeeded; app resource processes were gone after shutdown completed.
Invalid guest AppHost startup cleanup Passed Expected startup error and exit code 2; no related processes remained.
Shutdown-resisting guest force cleanup Passed Ctrl+C returned to shell and no related processes remained.

Overall Result

PR VERIFIED

Recommendations

  • No issues found in the tested guest AppHost shutdown scenarios.

Artifact Notes

  • Raw command logs that may include environment data were not included in this shareable report. Sanitized summaries were written to the session artifact folder.


if (OperatingSystem.IsWindows())
{
RequestGracefulShutdownWindowsProcessGroup(pid, logger);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This Windows path bypasses the stale-PID protection that the Unix branch and ForceKill get from TryGetRunningProcess(pid, expectedStartTime, logger). If the target exits and the PID/process-group id is reused, we can send Ctrl+Break to an unrelated process group. Please validate the PID/start-time pair before the OS-specific branch so Windows also returns without signaling when the process is gone or the start time no longer matches, and add a regression test for the mismatched-start-time process-group case.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I do feel like the complexity of this service is getting more and more involved.

@danegsta

danegsta commented Jun 8, 2026

Copy link
Copy Markdown
Member Author

#17814 effectively supersedes this work.

@danegsta danegsta closed this Jun 8, 2026
@microsoft-github-policy-service microsoft-github-policy-service Bot added this to the 13.5 milestone Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-cli needs-author-action An issue or pull request that requires more info or actions from the author.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants