diff --git a/src/Aspire.Cli/Commands/AppHostLauncher.cs b/src/Aspire.Cli/Commands/AppHostLauncher.cs index 2eeb0a397d0..fac027e55db 100644 --- a/src/Aspire.Cli/Commands/AppHostLauncher.cs +++ b/src/Aspire.Cli/Commands/AppHostLauncher.cs @@ -70,6 +70,7 @@ internal static void AddLaunchOptions(Command command) /// The output format (JSON or table). /// Whether to run in isolated mode. /// Whether running inside VS Code extension. + /// Whether the AppHost is waiting for a debugger to attach. /// Global CLI args to forward to child process. /// Additional unmatched args to forward. /// Cancellation token. @@ -79,6 +80,7 @@ public async Task LaunchDetachedAsync( OutputFormat? format, bool isolated, bool isExtensionHost, + bool waitForDebugger, IEnumerable globalArgs, IEnumerable additionalArgs, CancellationToken cancellationToken) @@ -119,6 +121,16 @@ public async Task LaunchDetachedAsync( logger.LogDebug("Waiting for socket with prefix: {SocketPrefix}, Hash: {Hash}", expectedSocketPrefix, expectedHash); + // If --wait-for-debugger is active, show a message so the user knows the AppHost + // is paused. In detached mode we don't have the AppHost PID (stdout is suppressed), + // so we show a generic message without a PID. + if (waitForDebugger) + { + interactionService.DisplayMessage( + KnownEmojis.Bug, + InteractionServiceStrings.WaitingForDebuggerToAttachToAppHost); + } + // Start the child process and wait for the backchannel var launchResult = await interactionService.ShowStatusAsync( RunCommandStrings.StartingAppHostInBackground, diff --git a/src/Aspire.Cli/Commands/RunCommand.cs b/src/Aspire.Cli/Commands/RunCommand.cs index e13252936f2..a8f422c9974 100644 --- a/src/Aspire.Cli/Commands/RunCommand.cs +++ b/src/Aspire.Cli/Commands/RunCommand.cs @@ -216,6 +216,7 @@ protected override async Task ExecuteAsync(ParseResult parseResult, Cancell // The completion sources are the contract between RunCommand and IAppHostProject var buildCompletionSource = new TaskCompletionSource(); var backchannelCompletionSource = new TaskCompletionSource(); + var waitForDebugger = parseResult.GetValue(RootCommand.WaitForDebuggerOption); context = new AppHostProjectContext { @@ -224,14 +225,14 @@ protected override async Task ExecuteAsync(ParseResult parseResult, Cancell Debug = parseResult.GetValue(RootCommand.DebugOption), NoBuild = noBuild, NoRestore = noBuild, // --no-build implies --no-restore - WaitForDebugger = parseResult.GetValue(RootCommand.WaitForDebuggerOption), + WaitForDebugger = waitForDebugger, Isolated = isolated, StartDebugSession = startDebugSession, EnvironmentVariables = new Dictionary(), UnmatchedTokens = parseResult.UnmatchedTokens.ToArray(), WorkingDirectory = ExecutionContext.WorkingDirectory, BuildCompletionSource = buildCompletionSource, - BackchannelCompletionSource = backchannelCompletionSource + BackchannelCompletionSource = backchannelCompletionSource, }; // Start the project run as a pending task - we'll handle UX while it runs @@ -250,6 +251,12 @@ protected override async Task ExecuteAsync(ParseResult parseResult, Cancell return await pendingRun; } + // If --wait-for-debugger, display a message so the user knows the AppHost is paused. + if (waitForDebugger) + { + InteractionService.DisplayMessage(KnownEmojis.Bug, InteractionServiceStrings.WaitingForDebuggerToAttachToAppHost); + } + // Now wait for the backchannel to be established var backchannel = await InteractionService.ShowStatusAsync( isExtensionHost ? InteractionServiceStrings.BuildingAppHost : RunCommandStrings.ConnectingToAppHost, @@ -619,6 +626,7 @@ private Task ExecuteDetachedAsync(ParseResult parseResult, FileInfo? passed var format = parseResult.GetValue(AppHostLauncher.s_formatOption); var isolated = parseResult.GetValue(AppHostLauncher.s_isolatedOption); var noBuild = parseResult.GetValue(s_noBuildOption); + var waitForDebugger = parseResult.GetValue(RootCommand.WaitForDebuggerOption); var globalArgs = RootCommand.GetChildProcessArgs(parseResult); var additionalArgs = parseResult.UnmatchedTokens.Where(t => t != "--detach").ToList(); @@ -632,6 +640,7 @@ private Task ExecuteDetachedAsync(ParseResult parseResult, FileInfo? passed format, isolated, isExtensionHost, + waitForDebugger, globalArgs, additionalArgs, cancellationToken); diff --git a/src/Aspire.Cli/Commands/StartCommand.cs b/src/Aspire.Cli/Commands/StartCommand.cs index 8fc1be7d9f8..2d1a301dec9 100644 --- a/src/Aspire.Cli/Commands/StartCommand.cs +++ b/src/Aspire.Cli/Commands/StartCommand.cs @@ -49,6 +49,7 @@ protected override async Task ExecuteAsync(ParseResult parseResult, Cancell var noBuild = parseResult.GetValue(s_noBuildOption); var isExtensionHost = ExtensionHelper.IsExtensionHost(_interactionService, out _, out _); + var waitForDebugger = parseResult.GetValue(RootCommand.WaitForDebuggerOption); var globalArgs = RootCommand.GetChildProcessArgs(parseResult); var additionalArgs = parseResult.UnmatchedTokens.ToList(); @@ -62,6 +63,7 @@ protected override async Task ExecuteAsync(ParseResult parseResult, Cancell format, isolated, isExtensionHost, + waitForDebugger, globalArgs, additionalArgs, cancellationToken); diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.cs.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.cs.xlf index 5f3a153d2ec..91b9765d90a 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.cs.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.cs.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.de.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.de.xlf index b5af2a6c6b2..cd413572ec8 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.de.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.de.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.es.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.es.xlf index 84a4997381a..5f2f9989b03 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.es.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.es.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.fr.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.fr.xlf index 2dc89654d20..a7497624e85 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.fr.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.fr.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.it.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.it.xlf index cc5f78af5e1..12e47f5a1f4 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.it.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.it.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ja.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ja.xlf index b21ac71d779..04f492987cc 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ja.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ja.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ko.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ko.xlf index f51213533f5..903d41c9fa1 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ko.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ko.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.pl.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.pl.xlf index 0c6650af361..dd63475344f 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.pl.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.pl.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.pt-BR.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.pt-BR.xlf index 3c3d7c3f7f0..50826012f01 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.pt-BR.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.pt-BR.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ru.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ru.xlf index 77f897f7e71..de044ebb0b8 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ru.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.ru.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.tr.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.tr.xlf index 501d263bf88..db8acaa7d90 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.tr.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.tr.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.zh-Hans.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.zh-Hans.xlf index cc9ad514d33..ac2213de0e8 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.zh-Hans.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.zh-Hans.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.zh-Hant.xlf b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.zh-Hant.xlf index 32554493e18..d29c06cc2bf 100644 --- a/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.zh-Hant.xlf +++ b/src/Aspire.Cli/Resources/xlf/InteractionServiceStrings.zh-Hant.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/Aspire.Hosting/DistributedApplication.cs b/src/Aspire.Hosting/DistributedApplication.cs index af4597eb1bc..8a6f4bb559e 100644 --- a/src/Aspire.Hosting/DistributedApplication.cs +++ b/src/Aspire.Hosting/DistributedApplication.cs @@ -249,13 +249,15 @@ private static void WaitForDebugger() if (Environment.GetEnvironmentVariable(KnownConfigNames.WaitForDebugger) == "true") { var startedWaiting = DateTimeOffset.UtcNow; - TimeSpan timeout = TimeSpan.FromSeconds(30); + var timeout = TimeSpan.FromSeconds(30); if (Environment.GetEnvironmentVariable(KnownConfigNames.WaitForDebuggerTimeout) is string timeoutString && int.TryParse(timeoutString, out var timeoutSeconds)) { timeout = TimeSpan.FromSeconds(timeoutSeconds); } + Console.WriteLine($"AppHost PID: {Environment.ProcessId}"); + while (Debugger.IsAttached == false) { Console.WriteLine($"Waiting for debugger to attach to process: {Environment.ProcessId}");