Skip to content

Route error-context DisplayMessage calls to stderr in BaseCommand#17231

Merged
JamesNK merged 4 commits into
mainfrom
fix/display-message-stderr-override
May 19, 2026
Merged

Route error-context DisplayMessage calls to stderr in BaseCommand#17231
JamesNK merged 4 commits into
mainfrom
fix/display-message-stderr-override

Conversation

@JamesNK
Copy link
Copy Markdown
Member

@JamesNK JamesNK commented May 19, 2026

Description

Error-context messages in BaseCommand (log file paths shown on failure, cancellation message) were written to stdout by default. This made them invisible when users redirected stdout, and they intermixed with structured output (e.g., JSON).

This PR adds an optional ConsoleOutput? consoleOverride parameter to DisplayMessage and DisplayCancellationMessage on IInteractionService. When provided, output routes to the specified stream regardless of the current Console property. When null, existing behavior is preserved.

In BaseCommand, the "see logs at" messages and the cancellation message now pass ConsoleOutput.Error so they appear on stderr alongside the error itself — consistent with the DisplayError behavior established in #17230.

Also refactors ConsoleInteractionService to use GetConsoleOutput/GetLogger helper methods, eliminating duplicated console resolution logic.

Fixes #16136

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
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
    • No

…ride

Add an optional ConsoleOutput? consoleOverride parameter to DisplayMessage
and DisplayCancellationMessage so callers can explicitly route output to a
specific stream. When null, the existing MessageConsole default is used.

In BaseCommand, the 'see logs at' messages displayed on non-zero exit codes
and the cancellation message now pass ConsoleOutput.Error so they appear on
stderr alongside the error itself.

Refactored ConsoleInteractionService to use GetConsoleOutput/GetLogger helper
methods, eliminating duplicated resolution logic.
@JamesNK JamesNK requested a review from mitchdenny as a code owner May 19, 2026 01:29
Copilot AI review requested due to automatic review settings May 19, 2026 01:29
@JamesNK JamesNK requested a review from davidfowl as a code owner May 19, 2026 01:29
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 19, 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 -- 17231

Or

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

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

Routes error-context messages (log file paths shown on non-zero exit, and the cancellation message when exit is non-success) to stderr in BaseCommand, by adding an optional ConsoleOutput? consoleOverride parameter to DisplayMessage and DisplayCancellationMessage on IInteractionService. Also refactors ConsoleInteractionService to share console/logger resolution via GetConsoleOutput/GetLogger helpers.

Changes:

  • Add optional consoleOverride parameter to IInteractionService.DisplayMessage / DisplayCancellationMessage and propagate it through ConsoleInteractionService and ExtensionInteractionService.
  • In BaseCommand, pass ConsoleOutput.Error for the "see logs at" messages on failure and for the cancellation message when exit code is non-success.
  • Update all test/mock implementations of IInteractionService to match the new signatures.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/Aspire.Cli/Interaction/IInteractionService.cs Adds optional consoleOverride parameter to two interface methods.
src/Aspire.Cli/Interaction/ConsoleInteractionService.cs Implements the override via GetConsoleOutput/GetLogger helpers.
src/Aspire.Cli/Interaction/ExtensionInteractionService.cs Forwards consoleOverride to inner console service.
src/Aspire.Cli/Commands/BaseCommand.cs Routes failure-context messages and cancellation to stderr.
tests/Aspire.Cli.Tests/TestServices/TestInteractionService.cs Updates mock signatures.
tests/Aspire.Cli.Tests/TestServices/TestExtensionInteractionService.cs Updates mock signatures.
tests/Aspire.Cli.Tests/Templating/DotNetTemplateFactoryTests.cs Updates mock signatures.
tests/Aspire.Cli.Tests/Projects/ExtensionGuestLauncherTests.cs Updates mock signatures.
tests/Aspire.Cli.Tests/Commands/UpdateCommandTests.cs Forwards consoleOverride in delegating mock.
tests/Aspire.Cli.Tests/Commands/PublishCommandPromptingIntegrationTests.cs Updates mock signatures.

@github-actions
Copy link
Copy Markdown
Contributor

Re-running the failed jobs in the CI workflow for this pull request because 2 jobs were 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.

@JamesNK JamesNK enabled auto-merge (squash) May 19, 2026 04:38
@JamesNK JamesNK merged commit 7dc08fa into main May 19, 2026
298 checks passed
@microsoft-github-policy-service microsoft-github-policy-service Bot added this to the 13.4 milestone May 19, 2026
@github-actions
Copy link
Copy Markdown
Contributor

CLI E2E Tests unknown — 90 passed, 0 failed, 1 unknown (commit 8165c3a)

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
AspireAddPackageVersionToDirectoryPackagesProps ▶️ View recording
AspireInitSingleFileAppHostRunsViaDotnetRunAppHost ▶️ View recording
AspireInitWithExistingAppHostDirRecreatesMissingNuGetConfigAndPreservesFiles ▶️ View recording
AspireInitWithSolutionFileGeneratesAppHostThatBuildsAgainstChannelHive ▶️ 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 recording
CreateAndRunJavaEmptyAppHostProject ▶️ View recording
CreateAndRunJsReactProject ▶️ View recording
CreateAndRunPythonReactProject ▶️ View recording
CreateAndRunTypeScriptEmptyAppHostProject ▶️ View recording
CreateAndRunTypeScriptStarterProject ▶️ View recording
CreateJavaAppHostWithViteApp ▶️ View recording
CreateTypeScriptAppHostWithViteApp_UsesConfiguredToolchain ▶️ View recording
DashboardRunWithOtelTracesReturnsNoTraces ▶️ 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
DoListStepsShowsPipelineSteps ▶️ View recording
DocsCommand_RendersInteractiveMarkdownFromLocalSource ▶️ View recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View recording
DoctorCommand_TypeScriptAppHostReportsMissingConfiguredToolchain ▶️ View recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View recording
GlobalMigration_HandlesCommentsAndTrailingCommas ▶️ View recording
GlobalMigration_HandlesMalformedLegacyJson ▶️ View recording
GlobalMigration_PreservesAllValueTypes ▶️ View recording
GlobalMigration_SkipsWhenNewConfigExists ▶️ View recording
GlobalSettings_MigratedFromLegacyFormat ▶️ View recording
InitTypeScriptAppHost_AugmentsExistingViteRepoAtRoot ▶️ View recording
InteractiveCSharpInitCreatesExpectedFiles ▶️ View recording
InvalidAppHostPathWithComments_IsHealedOnRun ▶️ View recording
LatestCliCanStartStableChannelAppHost ▶️ View recording
LatestCliCanStartStableChannelTypeScriptAppHost ▶️ View recording
LegacySettingsMigration_AdjustsRelativeAppHostPath ▶️ View recording
LogLevelTrace_ProducesTraceEntriesInCliLogFile ▶️ View recording
LogsCommandShowsResourceLogs ▶️ View recording
OtelLogsReturnsStructuredLogsFromStarterApp ▶️ View recording
OtelLogsReturnsStructuredLogsFromStarterAppIsolated ▶️ View recording
PsCommandListsRunningAppHost ▶️ View recording
PsFormatJsonOutputsOnlyJsonToStdout ▶️ View recording
PublishWithConfigureEnvFileUpdatesEnvOutput ▶️ View recording
PublishWithDockerComposeServiceCallbackSucceeds ▶️ View recording
PublishWithoutOutputPathUsesAppHostDirectoryDefault ▶️ View recording
ResourceCommand_FailedExecution_DisplaysAppHostLogPathAndLogContainsEntries ▶️ View recording
ResourceCommand_FailsWhenInteractionServiceIsRequired ▶️ 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
StopNonInteractiveSingleAppHost ▶️ View recording
StopWithNoRunningAppHostExitsSuccessfully ▶️ View recording
UnAwaitedChainsCompileWithAutoResolvePromises ▶️ View recording
UpdateProjectChannelToStable_TypeScript_PicksUpStablePackages ▶️ View recording

📹 Recordings uploaded automatically from CI run #26076550107

@aspire-repo-bot
Copy link
Copy Markdown
Contributor

✅ No documentation update needed.

docs_optional → internal_refactor

No signals triggered (signal_count == 0). The PR is an internal bug fix that routes error-context messages (e.g., "see logs at..." and cancellation messages) from stdout to stderr in BaseCommand — fixing #16136. No new public API, CLI options, flags, or user-visible strings were added. The PR checklist explicitly confirms "Did you add public API? No." All 13 changed files are internal CLI implementation files under src/Aspire.Cli/. This is a pure internal behavioral correction with no documentation surface to update.

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.

Aspire CLI doesn't consistently report errors to stderr

3 participants