Redirect human-readable messages to stderr when --format json is used#14572
Redirect human-readable messages to stderr when --format json is used#14572mitchdenny merged 8 commits intorelease/13.2from
Conversation
When running `aspire run --detach --format json`, messages like "Finding apphosts..." and "Stopping previous instance..." were written to stdout alongside the JSON output, making it impossible to parse the JSON programmatically. Add a `UseStderrForMessages` property to IInteractionService. When enabled, ConsoleInteractionService routes all human-readable display methods through stderr while keeping DisplayRawText on stdout for structured output. The property is set in ExecuteDetachedAsync when the output format is JSON. Fixes #14423 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 14572Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 14572" |
There was a problem hiding this comment.
Pull request overview
This pull request fixes issue #14423 where human-readable messages were being written to stdout alongside JSON output when running aspire run --detach --format json, making the JSON output unparseable by automated tools.
Changes:
- Added
UseStderrForMessagesproperty toIInteractionServiceto enable routing human-readable messages to stderr - Modified
ConsoleInteractionServiceto route display methods through aMessageConsoleproperty that returns stderr when the flag is set - Set
UseStderrForMessages = trueinRunCommand.ExecuteDetachedAsyncwhen--format jsonis specified
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/Aspire.Cli/Interaction/IInteractionService.cs | Added UseStderrForMessages property with default no-op implementation |
| src/Aspire.Cli/Interaction/ConsoleInteractionService.cs | Implemented UseStderrForMessages property and routed all message display methods through MessageConsole that redirects to stderr when flag is set; DisplayRawText correctly stays on stdout |
| src/Aspire.Cli/Commands/RunCommand.cs | Sets UseStderrForMessages = true when running in detached mode with JSON format |
🎬 CLI E2E Test RecordingsThe following terminal recordings are available for commit
📹 Recordings uploaded automatically from CI run #22335500927 |
…enum - Add ConsoleOutput enum (Standard, Error) for explicit console targeting - Replace DIM UseStderrForMessages with standard DefaultConsole property - Add optional ConsoleOutput parameter to DisplayRawText for explicit targeting - Update all IInteractionService implementations Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/deployment-test |
|
🚀 Deployment tests starting on PR #14572... This will deploy to real Azure infrastructure. Results will be posted here when complete. |
|
✅ Deployment E2E Tests passed Summary: 21 passed, 0 failed, 0 cancelled Passed Tests
🎬 Terminal Recordings
|
Instead of testing multiple-apphost selection prompts, the test now validates the core PR concern: that aspire run --detach --format json produces well-formed JSON on stdout without human-readable message pollution. The test creates a single project, runs aspire run --detach first, then runs aspire run --detach --format json > output.json and validates the output file is parseable JSON with expected fields. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The WaitUntil was looking for 'The apphost is running in the background.' which doesn't exist. The actual output is the AppHost summary table followed by the shell prompt. Use WaitForSuccessPrompt directly which correctly waits for the [N OK] $ prompt. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@joperezr this should be good to go now. |
|
My review comments haven't been addressed: #14572 (review) |
Co-authored-by: James Newton-King <james@newtonking.com>
- Rename DefaultConsole to Console per review request - Change DisplayRawText parameter from ConsoleOutput to ConsoleOutput? (nullable). When null, respects the Console property setting. Renamed parameter to consoleOverride to indicate it bypasses the default. - Audit all DisplayRawText callers: all output structured data (JSON) that must always go to stdout, so explicitly pass ConsoleOutput.Standard - Fix System.Console shadowing in test file caused by Console rename Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…#14572) * Redirect human-readable messages to stderr when --format json is used When running `aspire run --detach --format json`, messages like "Finding apphosts..." and "Stopping previous instance..." were written to stdout alongside the JSON output, making it impossible to parse the JSON programmatically. Add a `UseStderrForMessages` property to IInteractionService. When enabled, ConsoleInteractionService routes all human-readable display methods through stderr while keeping DisplayRawText on stdout for structured output. The property is set in ExecuteDetachedAsync when the output format is JSON. Fixes #14423 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR feedback: replace UseStderrForMessages with ConsoleOutput enum - Add ConsoleOutput enum (Standard, Error) for explicit console targeting - Replace DIM UseStderrForMessages with standard DefaultConsole property - Add optional ConsoleOutput parameter to DisplayRawText for explicit targeting - Update all IInteractionService implementations Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use Throw for multiple apphosts when --format json; add E2E test When --format json is used with aspire run --detach, use MultipleAppHostProjectsFoundBehavior.Throw instead of Prompt to prevent interactive selection UI from polluting stdout JSON output. Users should specify --project explicitly in this case. Added MultipleAppHostTests E2E test that creates two single-file apphosts and verifies the selection prompt appears when multiple are found. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix MultipleAppHostTests: use real aspire new projects and assert on errors The previous test used stub single-file apphosts that couldn't resolve the Aspire.AppHost.Sdk, so the selection prompt appeared but the selected apphost failed to build. The test passed vacuously. Now creates two real projects via aspire new, verifies the selection prompt appears, selects one, and asserts the apphost actually starts. Adds explicit error assertions for SDK resolution and missing project failures to fail fast with descriptive messages. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Rewrite test: validate --detach --format json produces valid JSON Instead of testing multiple-apphost selection prompts, the test now validates the core PR concern: that aspire run --detach --format json produces well-formed JSON on stdout without human-readable message pollution. The test creates a single project, runs aspire run --detach first, then runs aspire run --detach --format json > output.json and validates the output file is parseable JSON with expected fields. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix detach test: wait for prompt, not nonexistent message The WaitUntil was looking for 'The apphost is running in the background.' which doesn't exist. The actual output is the AppHost summary table followed by the shell prompt. Use WaitForSuccessPrompt directly which correctly waits for the [N OK] $ prompt. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Mitch Denny <mitch@mitchdeny.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
When running
aspire run --detach --format json, human-readable messages like "Finding apphosts..." and "Stopping previous instance..." were written to stdout alongside the JSON output, making it impossible to parse the JSON programmatically.Changes
UseStderrForMessagesproperty toIInteractionServicewith a default no-op implementationConsoleInteractionServiceroutes all human-readable display methods (DisplayMessage,DisplaySuccess,DisplayError,ShowStatusAsync, etc.) through aMessageConsoleproperty that returns_errorConsole(stderr) when the flag is setDisplayRawTextremains on stdout — this is used for the structured JSON outputExecuteDetachedAsyncinRunCommandsetsUseStderrForMessages = truewhen--format jsonis specifiedBefore
After
Human-readable messages still appear on stderr so users can see progress when running interactively, but
stdout | jqnow works correctly.Fixes #14423