Skip to content

Conversation

@davidfowl
Copy link
Member

@davidfowl davidfowl commented Feb 10, 2026

Description

When running aspire run --detach, the child CLI process inherited pipe handles from stdout/stderr redirection. This kept pipes open until the AppHost grandchild died, preventing callers using synchronous process APIs (e.g. Node.js execSync) from detecting that the CLI had exited. This is primarily a Windows issue due to how handle inheritance works.

Changes

  • Stop redirecting child stdout/stderr — removes pipe handle inheritance. The child writes to its own log file instead.
  • New --log-file hidden option — parent passes an explicit log file path to the child so the parent knows where to point on failure.
  • Unified log filename generation — new FileLoggerProvider.GenerateLogFilePath static helper used by both the normal logger and the detach child path. Format: cli_20260210T074038_12345.log (with optional suffix).
  • Better error messages — maps FailedToBuildArtifacts exit code to a friendly message. Points to the child's log file on failure.
  • Cache cleanup fix — updated log file suffix pattern to match the new naming format.

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
  • Does the change require an update in our Aspire docs?
    • Yes
    • No

Copilot AI review requested due to automatic review settings February 10, 2026 08:01
@github-actions
Copy link
Contributor

github-actions bot commented Feb 10, 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/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 14424

Or

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

Copy link
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

This PR improves aspire run --detach behavior by preventing pipe/handle inheritance issues (notably on Windows) and standardizing how CLI log files are named and discovered, so detached failures can reliably point users to the right diagnostics.

Changes:

  • Adds a hidden --log-file option and passes it from the detach parent process to the detach child process.
  • Introduces FileLoggerProvider.GenerateLogFilePath(...) and updates log file naming to cli_yyyyMMddTHHmmss_{pid}[_{suffix}].log.
  • Improves detached-mode failure UX by mapping FailedToBuildArtifacts to a friendlier localized message and pointing to the child’s log file.

Reviewed changes

Copilot reviewed 18 out of 19 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/Aspire.Cli/Program.cs Parses --log-file early and uses it to initialize file logging before host build.
src/Aspire.Cli/Diagnostics/FileLoggerProvider.cs Adds centralized log filename generation and updates default naming format.
src/Aspire.Cli/Commands/RunCommand.cs Adds hidden --log-file, stops redirecting detached child stdout/stderr, passes explicit child log path, and improves failure messaging.
src/Aspire.Cli/Commands/CacheCommand.cs Updates log filename suffix matching to align with new cli_{timestamp}_{pid}.log format.
src/Aspire.Cli/Resources/RunCommandStrings.resx Adds new AppHostFailedToBuild resource string.
src/Aspire.Cli/Resources/RunCommandStrings.Designer.cs Regenerates strongly-typed accessor for AppHostFailedToBuild.
src/Aspire.Cli/Resources/xlf/RunCommandStrings.cs.xlf Adds AppHostFailedToBuild localization entry (new).
src/Aspire.Cli/Resources/xlf/RunCommandStrings.de.xlf Adds AppHostFailedToBuild localization entry (new).
src/Aspire.Cli/Resources/xlf/RunCommandStrings.es.xlf Adds AppHostFailedToBuild localization entry (new).
src/Aspire.Cli/Resources/xlf/RunCommandStrings.fr.xlf Adds AppHostFailedToBuild localization entry (new).
src/Aspire.Cli/Resources/xlf/RunCommandStrings.it.xlf Adds AppHostFailedToBuild localization entry (new).
src/Aspire.Cli/Resources/xlf/RunCommandStrings.ja.xlf Adds AppHostFailedToBuild localization entry (new).
src/Aspire.Cli/Resources/xlf/RunCommandStrings.ko.xlf Adds AppHostFailedToBuild localization entry (new).
src/Aspire.Cli/Resources/xlf/RunCommandStrings.pl.xlf Adds AppHostFailedToBuild localization entry (new).
src/Aspire.Cli/Resources/xlf/RunCommandStrings.pt-BR.xlf Adds AppHostFailedToBuild localization entry (new).
src/Aspire.Cli/Resources/xlf/RunCommandStrings.ru.xlf Adds AppHostFailedToBuild localization entry (new).
src/Aspire.Cli/Resources/xlf/RunCommandStrings.tr.xlf Adds AppHostFailedToBuild localization entry (new).
src/Aspire.Cli/Resources/xlf/RunCommandStrings.zh-Hans.xlf Adds AppHostFailedToBuild localization entry (new).
src/Aspire.Cli/Resources/xlf/RunCommandStrings.zh-Hant.xlf Adds AppHostFailedToBuild localization entry (new).
Files not reviewed (1)
  • src/Aspire.Cli/Resources/RunCommandStrings.Designer.cs: Language not supported
Comments suppressed due to low confidence (2)

src/Aspire.Cli/Commands/CacheCommand.cs:121

  • cache clear skips deleting the current log file by checking a filename suffix based on Environment.ProcessId, but the CLI can now run with an explicit --log-file path (and detached child logs also include an extra suffix). This can cause the current session’s log file to be deleted while still in use. Consider skipping deletion by comparing against ExecutionContext.LogFilePath (full path) instead of relying on the filename pattern.
                // Also clear the logs directory (skip current process's log file)
                var logsDirectory = ExecutionContext.LogsDirectory;
                // Log files are named cli_{timestamp}_{pid}.log, so we need to check the suffix
                var currentLogFileSuffix = $"_{Environment.ProcessId}.log";
                if (logsDirectory.Exists)
                {
                    foreach (var file in logsDirectory.GetFiles("*", SearchOption.AllDirectories))
                    {
                        // Skip the current process's log file to avoid deleting it while in use
                        if (file.Name.EndsWith(currentLogFileSuffix, StringComparison.OrdinalIgnoreCase))
                        {

src/Aspire.Cli/Commands/RunCommand.cs:665

  • Detached mode now generates/passes a childLogFile and (on failure) points users to that file, but the success paths still report _fileLoggerProvider.LogFilePath (parent session log) in JSON output and the rendered summary. Since the child process is the one that continues running and writing logs, consider returning/displaying the child log file path consistently to avoid sending users/tools to a log that won’t contain the relevant AppHost build/runtime output.
        // Build the arguments for the child CLI process
        // Tell the child where to write its log so we can find it on failure.
        var childLogFile = GenerateChildLogFilePath();

        var args = new List<string>
        {
            "run",
            "--non-interactive",
            "--project",
            effectiveAppHostFile.FullName,
            "--log-file",
            childLogFile
        };

Comment on lines +898 to +901
return Diagnostics.FileLoggerProvider.GenerateLogFilePath(
ExecutionContext.LogsDirectory.FullName,
_timeProvider,
suffix: "detach-child");
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

GenerateChildLogFilePath() calls FileLoggerProvider.GenerateLogFilePath(), which embeds Environment.ProcessId. Since this method runs in the parent process, the child log file name ends up containing the parent PID, which is misleading and breaks the “pid in filename == process writing the log” convention used elsewhere. Consider adjusting the helper to accept an explicit PID (or use a different uniqueness token for precomputed paths) so detached child log files can reflect the child process ID.

This issue also appears on line 653 of the same file.

Suggested change
return Diagnostics.FileLoggerProvider.GenerateLogFilePath(
ExecutionContext.LogsDirectory.FullName,
_timeProvider,
suffix: "detach-child");
var logsDirectory = ExecutionContext.LogsDirectory.FullName;
var now = _timeProvider.GetUtcNow();
var timestamp = now.ToString("yyyyMMddHHmmssfff", CultureInfo.InvariantCulture);
var uniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
var fileName = $"aspire-detach-child-{timestamp}-{uniqueId}.log";
return Path.Combine(logsDirectory, fileName);

Copilot uses AI. Check for mistakes.
Comment on lines +822 to +828
// Show a friendly message based on well-known exit codes from the child
var errorMessage = childExitCode switch
{
ExitCodeConstants.FailedToBuildArtifacts => RunCommandStrings.AppHostFailedToBuild,
_ => string.Format(CultureInfo.CurrentCulture, RunCommandStrings.AppHostExitedWithCode, childExitCode)
};
_interactionService.DisplayError(errorMessage);
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

Changes to detached execution introduced new behaviors that are currently untested (e.g., --log-file propagation, exit-code-to-message mapping for FailedToBuildArtifacts, and ensuring the correct log file path is surfaced on failure). There are existing RunCommandTests, but none cover --detach; adding focused unit/integration tests for these paths would help prevent regressions on Windows and in programmatic consumers.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Contributor

github-actions bot commented Feb 10, 2026

🎬 CLI E2E Test Recordings

The following terminal recordings are available for commit ba11a15:

Test Recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View Recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View Recording
Banner_DisplayedOnFirstRun ▶️ View Recording
Banner_DisplayedWithExplicitFlag ▶️ View Recording
CreateAndDeployToDockerCompose ▶️ View Recording
CreateAndDeployToDockerComposeInteractive ▶️ View Recording
CreateAndPublishToKubernetes ▶️ View Recording
CreateAndRunAspireStarterProject ▶️ View Recording
CreateAndRunAspireStarterProjectWithBundle ▶️ View Recording
CreateAndRunJsReactProject ▶️ View Recording
CreateAndRunPythonReactProject ▶️ View Recording
CreateEmptyAppHostProject ▶️ View Recording
CreateStartAndStopAspireProject ▶️ View Recording
CreateTypeScriptAppHostWithViteApp ▶️ View Recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View Recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View Recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View Recording
LogsCommandShowsResourceLogs ▶️ View Recording
PsCommandListsRunningAppHost ▶️ View Recording
ResourcesCommandShowsRunningResources ▶️ View Recording

📹 Recordings uploaded automatically from CI run #21858978808

@davidfowl davidfowl force-pushed the davidfowl/detach-more branch from 7642ece to f98890f Compare February 10, 2026 08:56
@davidfowl davidfowl force-pushed the davidfowl/detach-more branch from f98890f to ba11a15 Compare February 10, 2026 09:21
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.

1 participant