Skip to content

Display "Stopping Aspire..." immediately on CTRL+C#17652

Merged
JamesNK merged 3 commits into
mainfrom
jamesnk/cancellation-stopping-message
Jun 3, 2026
Merged

Display "Stopping Aspire..." immediately on CTRL+C#17652
JamesNK merged 3 commits into
mainfrom
jamesnk/cancellation-stopping-message

Conversation

@JamesNK
Copy link
Copy Markdown
Member

@JamesNK JamesNK commented May 29, 2026

Summary

When the user presses CTRL+C, the CLI now displays "Stopping Aspire..." after a short 200ms delay if the command handler hasn't completed yet. This gives immediate user feedback that shutdown is in progress.

Changes

Core feature

  • BaseCommand: Moved cancellation/termination handling from Program.Main into the SetAction lambda. Uses a Task.WhenAny loop with a 200ms TaskCompletionSource timer to show the stopping message if the handler is still running after cancellation.
  • ConsoleCancellationManager: Changed SetStartedHandler to accept Task instead of Task<int> since BaseCommand now manages the exit code directly.
  • CommandResult.FromExitCode: Maps CliExitCodes.Cancelled to CommandResult.Cancelled(exitCode).

Refactoring

  • CommonCommandServices: New class that consolidates the 5+ dependencies previously passed individually to every command constructor (IFeatures, ICliUpdateNotifier, CliExecutionContext, IInteractionService, AspireCliTelemetry, ConsoleCancellationManager, ILoggerFactory). All commands now take a single CommonCommandServices parameter.
  • CliStartupContext: Added CancellationManager property so it flows through DI.
  • Program.cs: Simplified the invoke path — no longer does WhenAny at the top level since BaseCommand handles it.

Test fixes

  • Added using to all BuildServiceProvider() calls in CLI tests to fix FileLoggerProvider holding file handles.
  • Added ConsoleCancellationManager registration to test DI setup.
  • Two new tests for the stopping message behavior (displayed after delay, not displayed if handler completes quickly).
  • Replaced Task.Delay(Timeout.Infinite, ct) with AsyncTestHelpers.WaitForCancellationAsync(ct) for cleaner test code.

Testing

All 3829 CLI tests pass (0 failures, 21 skipped).

Copilot AI review requested due to automatic review settings May 29, 2026 07:14
@JamesNK JamesNK added this to the 13.5 milestone May 29, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 29, 2026

🚀 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 -- 17652

Or

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

@JamesNK
Copy link
Copy Markdown
Member Author

JamesNK commented May 29, 2026

Too big/risky for 13.4.

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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@mitchdenny
Copy link
Copy Markdown
Member

A couple of things worth flagging before this merges:

1. Wait for the release/13.4 merge-back first. PR #17817 (Merge release/13.4 to main after v13.4.0 release) hasn't landed yet, and there are 18 unmerged commits on release/13.4 touching src/Aspire.Cli/ — 19 files overlap with this PR, including Program.cs, ConsoleCancellationManager.cs, and 10 command files. One of those unmerged commits is specifically cc4b7eafe Backport CLI cancellation fixes to release/13.4, which will fight directly with the cancellation-manager changes here. Every overlapping command file will also conflict because this PR rewrites their constructor signatures to take CommonCommandServices. Much easier to land #17817 first and then rebase this, vs. resolving the same conflicts twice.

2. Watch CommonCommandServices for god-object drift. The 5 originally-bundled services (IFeatures, ICliUpdateNotifier, CliExecutionContext, IInteractionService, AspireCliTelemetry) were already required by every BaseCommand ctor, so bundling them is a clear win. But folding services into one parameter object removes the natural "ugh, another ctor parameter" friction that prevents the bundle from accreting fields that only a handful of commands actually need. Might be worth a short comment on the class stating the bar for additions (e.g. "only services genuinely needed by every BaseCommand, not just by a few") so future contributors know where to draw the line.

Copy link
Copy Markdown
Member

@mitchdenny mitchdenny left a comment

Choose a reason for hiding this comment

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

@JamesNK — went ahead and merged main into your branch to resolve the conflicts from #17817. Two touches needed:

  • src/Aspire.Cli/Packaging/PackagingService.cs — using-block conflict, combined both sides (kept System.Globalization / System.Reflection / System.Security from your branch, added Aspire.Cli.Acquisition from main).
  • src/Aspire.Cli/Commands/CacheCommand.cs — auto-merged, but new code pulled from main references CliPathHelper without the Aspire.Cli.Utils using. Added it.

Aspire.Cli + Aspire.Cli.Tests build clean and the impacted tests (BaseCommandTests, PackagingServiceTests, CacheCommandTests — 100 total) pass locally. Pushed as commit 9ddf355 — please give it a look and merge when you're happy.

@JamesNK JamesNK force-pushed the jamesnk/cancellation-stopping-message branch from 9ddf355 to 20216a7 Compare June 2, 2026 09:00
@JamesNK JamesNK enabled auto-merge (squash) June 2, 2026 15:10
JamesNK added 2 commits June 3, 2026 14:52
- Introduce CommonCommandServices to consolidate BaseCommand dependencies
- Move cancellation/termination handling from Program.Main into BaseCommand
- Show 'Stopping Aspire...' after 200ms if handler hasn't completed post-cancellation
- Add CancellationManager to CliStartupContext and DI container
- Map CliExitCodes.Cancelled in CommandResult.FromExitCode
- Change ConsoleCancellationManager.SetStartedHandler to accept Task (not Task<int>)
- Add BaseCommand tests for cancellation message timing
- Add 'using' to BuildServiceProvider() calls in CLI tests to fix file handle leaks
@JamesNK JamesNK force-pushed the jamesnk/cancellation-stopping-message branch from 3a4361a to d807703 Compare June 3, 2026 06:52
@JamesNK JamesNK merged commit 72df686 into main Jun 3, 2026
981 of 990 checks passed
@JamesNK JamesNK deleted the jamesnk/cancellation-stopping-message branch June 3, 2026 09:33
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 3, 2026

CLI E2E Tests failed — 111 passed, 1 failed, 2 unknown (commit b173420)

❌ Failed Tests

- Test Detail
DeployK8sWithPostgres Recording · Job · CLI logs
View all recordings
- Test Detail
AddPackageInteractiveWhileAppHostRunningDetached Recording · Job · CLI logs
AddPackageWhileAppHostRunningDetached Recording · Job · CLI logs
AgentCommands_AllHelpOutputs_AreCorrect Recording · Job · CLI logs
AgentInitCommand_DefaultSelection_InstallsDefaultSkills Recording · Job · CLI logs
AgentInitCommand_MigratesDeprecatedConfig Recording · Job · CLI logs
AgentInit_NonInteractive_BundleOnlySkillsNotInCatalog Recording · Job · CLI logs
AgentMcpListStructuredLogsReturnsLogsFromStarterApp Recording · Job · CLI logs
AgentMcpListStructuredLogsReturnsLogsFromStarterApp_DevLocalhost Recording · Job · CLI logs
AgentMcpListStructuredLogsReturnsLogsFromStarterApp_Isolated Recording · Job · CLI logs
AllPublishMethodsBuildDockerImages Recording · Job · CLI logs
AspireAddAndStartWorkAgainstLegacyAppHostTs Recording · Job · CLI logs
AspireAddPackageVersionToDirectoryPackagesProps Recording · Job · CLI logs
AspireInitSingleFileAppHostRunsViaDotnetRunAppHost Recording · Job · CLI logs
AspireInit_ExistingAppHostDir_RecreatesNuGetConfigKeepsFiles Recording · Job · CLI logs
AspireInit_SolutionFile_BuildsAgainstChannelHive Recording · Job · CLI logs
AspireStartUpdatesStaleTypeScriptAppHostPath Recording · Job · CLI logs
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps Recording · Job · CLI logs
AspireUpdateRemovesOrphanAppHostPackageVersionWhenSdkAlreadyCurrent Recording · Job · CLI logs
Banner_DisplayedOnFirstRun Recording · Job · CLI logs
Banner_DisplayedWithExplicitFlag Recording · Job · CLI logs
Banner_NotDisplayedWithNoLogoFlag Recording · Job · CLI logs
CertificatesClean_RemovesCertificates Recording · Job · CLI logs
CertificatesTrust_WithNoCert_CreatesAndTrustsCertificate Recording · Job · CLI logs
CertificatesTrust_WithUntrustedCert_TrustsCertificate Recording · Job · CLI logs
ConfigSetGet_CreatesNestedJsonFormat Recording · Job · CLI logs
CreateAndRunAspireStarterProject Recording · Job · CLI logs
CreateAndRunAspireStarterProjectWithBundle Recording · Job · CLI logs
CreateAndRunEmptyAppHostProject Recording · Job · CLI logs
CreateAndRunJavaEmptyAppHostProject Recording · Job · CLI logs
CreateAndRunJsReactProject Recording · Job · CLI logs
CreateAndRunPolyglotAppHostWithDevLocalhostUrls Recording · Job · CLI logs
CreateAndRunPythonReactProject Recording · Job · CLI logs
CreateAndRunTypeScriptEmptyAppHostProject Recording · Job · CLI logs
CreateAndRunTypeScriptStarterProject Recording · Job · CLI logs
CreateJavaAppHostWithViteApp Recording · Job · CLI logs
CreateTypeScriptAppHostWithViteApp_UsesConfiguredToolchain Recording · Job · CLI logs
DashboardRunWithAgentMcpListTracesReturnsNoTraces Recording · Job · CLI logs
DashboardRunWithAgentMcpListTracesReturnsNoTraces_DevLocalhost Recording · Job · CLI logs
DashboardRunWithOtelTracesReturnsNoTraces Recording · Job · CLI logs
DashboardRunWithOtelTracesReturnsNoTraces_DevLocalhost Recording · Job · CLI logs
DeployK8sBasicApiService Recording · Job · CLI logs
DeployK8sWithExternalHelmChart Recording · Job · CLI logs
DeployK8sWithGarnet Recording · Job · CLI logs
DeployK8sWithMongoDB Recording · Job · CLI logs
DeployK8sWithMySql Recording · Job · CLI logs
DeployK8sWithPostgres Recording · Job · CLI logs
DeployK8sWithRabbitMQ Recording · Job · CLI logs
DeployK8sWithRedis Recording · Job · CLI logs
DeployK8sWithSqlServer Recording · Job · CLI logs
DeployK8sWithValkey Recording · Job · CLI logs
DeployTypeScriptAppToKubernetes Recording · Job · CLI logs
DescribeCommandResolvesReplicaNames Recording · Job · CLI logs
DescribeCommandShowsRunningResources Recording · Job · CLI logs
DetachFormatJsonProducesValidJson Recording · Job · CLI logs
DetachFormatJsonProducesValidJsonWhenRestartingExistingInstance Recording · Job · CLI logs
DoPublishAndDeployListStepsWork Recording · Job · CLI logs
DocsCommand_RendersInteractiveMarkdownFromLocalSource Recording · Job · CLI logs
DoctorCommand_DetectsDeprecatedAgentConfig Recording · Job · CLI logs
DoctorCommand_TypeScriptAppHostReportsMissingConfiguredToolchain Recording · Job · CLI logs
DoctorCommand_WithSslCertDir_ShowsTrusted Recording · Job · CLI logs
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted Recording · Job · CLI logs
DotNetRunFileBasedAppHostUsesAspireCliBundle Recording · Job · CLI logs
DotNetRunProjectAppHostUsesAspireCliBundle Recording · Job · CLI logs
GatewayWithoutExternalEndpoint_FailsPublishWithGuidance Recording · Job · CLI logs
GeneratedAspireDevScript_StartsWatchMode_WithConfiguredToolchain Recording · Job · CLI logs
GlobalMigration_HandlesCommentsAndTrailingCommas Recording · Job · CLI logs
GlobalMigration_HandlesMalformedLegacyJson Recording · Job · CLI logs
GlobalMigration_PreservesAllValueTypes Recording · Job · CLI logs
GlobalMigration_SkipsWhenNewConfigExists Recording · Job · CLI logs
GlobalSettings_MigratedFromLegacyFormat Recording · Job · CLI logs
IngressWithoutExternalEndpoint_FailsPublishWithGuidance Recording · Job · CLI logs
InitTypeScriptAppHost_AugmentsExistingViteRepoInWorkspaceSubdirectory Recording · Job · CLI logs
InteractiveCSharpInitCreatesExpectedFiles Recording · Job · CLI logs
InvalidAppHostPathWithComments_IsHealedOnRun Recording · Job · CLI logs
JavaScriptHostingApisRunFromTypeScriptAppHost Recording · Job · CLI logs
LatestCliCanStartStableChannelAppHost Recording · Job · CLI logs
LatestCliCanStartStableChannelTypeScriptAppHost Recording · Job · CLI logs
LegacySettingsMigration_AdjustsRelativeAppHostPath Recording · Job · CLI logs
LogsCommandShowsResourceLogs Recording · Job · CLI logs
OtelLogsReturnsStructuredLogsFromStarterApp Recording · Job · CLI logs
OtelLogsReturnsStructuredLogsFromStarterAppIsolated Recording · Job · CLI logs
PsCommandListsRunningAppHost Recording · Job · CLI logs
PsFormatJsonOutputsOnlyJsonToStdout Recording · Job · CLI logs
PublishJavaScriptPatternsGeneratesExpectedDockerComposeArtifacts Recording · Job · CLI logs
PublishWithConfigureEnvFileUpdatesEnvOutput Recording · Job · CLI logs
PublishWithDockerComposeServiceCallbackSucceeds Recording · Job · CLI logs
PublishWithoutOutputPathUsesAppHostDirectoryDefault Recording · Job · CLI logs
ResourceCommand_FailedExec_ShowsLogPathAndLogHasEntries Recording · Job · CLI logs
ResourceCommand_SetAndDeleteParameterUpdatesDescribeOutput Recording · Job · CLI logs
RestoreGeneratesSdkFiles Recording · Job · CLI logs
RestoreGeneratesSdkFiles_WithConfiguredToolchain Recording · Job · CLI logs
RestoreRefreshesGeneratedSdkAfterAddingIntegration Recording · Job · CLI logs
RestoreSupportsConfigOnlyHelperPackageAndCrossPackageTypes Recording · Job · CLI logs
RunFromParentDirectory_UsesExistingConfigNearAppHost Recording · Job · CLI logs
RunReportsSyntaxErrorsForDotNetAppHost Recording · Job · CLI logs
RunReportsSyntaxErrorsForTypeScriptAppHost Recording · Job · CLI logs
SecretCrudOnDotNetAppHost Recording · Job · CLI logs
SecretCrudOnTypeScriptAppHost Recording · Job · CLI logs
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels Recording · Job · CLI logs
StartAndWaitForTypeScriptSqlServerAppHostWithNativeAssets Recording · Job · CLI logs
StartReportsSyntaxErrorsForDotNetAppHost Recording · Job · CLI logs
StartReportsSyntaxErrorsForTypeScriptAppHost Recording · Job · CLI logs
StopAllAppHostsFromAppHostDirectory Recording · Job · CLI logs
StopJavaPolyglotAppHostUsingApphostDirectory Recording · Job · CLI logs
StopNonInteractiveSingleAppHost Recording · Job · CLI logs
StopTypeScriptPolyglotAppHostUsingApphostDirectory Recording · Job · CLI logs
StopWithNoRunningAppHostExitsSuccessfully Recording · Job · CLI logs
TypeScriptAppHostRunDoesNotDeadlockWhenLazyOptionsInvokeAsyncCallback Recording · Job · CLI logs
TypeScriptAppHostWithVite_AllowsDifferentGuestPkgManager Recording · Job · CLI logs
UnAwaitedChainsCompileWithAutoResolvePromises Recording · Job · CLI logs
UpdateToStable_CSharpEmptyAppHost_KeepsConfigChannel Recording · Job · CLI logs
UpdateToStable_CSharpSingleFileInit_KeepsConfigChannel Recording · Job · CLI logs
UpdateToStable_TypeScriptSingleFileInit_KeepsConfigChannel Recording · Job · CLI logs
UpdateToStable_TypeScript_PreviewsStablePkgsAndKeepsChannel Recording · Job · CLI logs

📹 Recordings uploaded automatically from CI run #26869467008

aspire-repo-bot Bot added a commit to microsoft/aspire.dev that referenced this pull request Jun 3, 2026
Update the 'Stopping the AppHost' section of aspire-run.mdx to note
that the CLI displays a '🛑 Stopping Aspire...' message shortly after
the user presses Ctrl+C, confirming that shutdown is in progress.

Documents the user-facing behavior added by microsoft/aspire#17652.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@aspire-repo-bot
Copy link
Copy Markdown
Contributor

Pull request created: #1201

Generated by PR Documentation Check · sonnet46 3.5M

@aspire-repo-bot
Copy link
Copy Markdown
Contributor

📝 Documentation has been drafted in microsoft/aspire.dev#1201 targeting release/13.4.

Updated src/frontend/src/content/docs/reference/cli/commands/aspire-run.mdx to mention that 🛑 Stopping Aspire... is displayed shortly after Ctrl+C is pressed (behavior introduced by this PR). The existing "Stopping the AppHost" section described the Ctrl+C interaction but did not mention this prompt feedback message.

Note

This draft PR needs human review before merging.

mitchdenny added a commit that referenced this pull request Jun 4, 2026
Track the BaseCommand ctor consolidation from #17652 (merged to main
2026-06-03), which replaced the individual IFeatures + ICliUpdateNotifier +
CliExecutionContext + IInteractionService + AspireCliTelemetry parameters
with a single CommonCommandServices bag.

TerminalCommand, TerminalAttachCommand, and TerminalPsCommand now take
CommonCommandServices and forward it to base(...). Per-command-specific
deps (IAuxiliaryBackchannelMonitor, IProjectLocator, ILogger<T>) are still
direct parameters resolved by DI. _interactionService field and
AppHostConnectionResolver construction now pull from services.* instead of
the deleted parameters.

Also drop now-unused usings (Configuration, Telemetry, Utils on the parent
TerminalCommand; Configuration + Telemetry on the two leaves; the
TerminalPsCommand keeps Utils for the AddBoldColumn extension method).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants