Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 63 additions & 35 deletions src/Aspire.Cli/DotNet/DotNetCliRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -339,48 +339,76 @@ private async Task StartBackchannelAsync(IDotNetCliExecution? execution, string

string[] cliArgs = [.. cliArgsList];

var stdoutBuilder = new StringBuilder();
var existingStandardOutputCallback = options.StandardOutputCallback; // Preserve the existing callback if it exists.
options.StandardOutputCallback = (line) => {
stdoutBuilder.AppendLine(line);
existingStandardOutputCallback?.Invoke(line);
};
var existingStandardOutputCallback = options.StandardOutputCallback;
var existingStandardErrorCallback = options.StandardErrorCallback;

var stderrBuilder = new StringBuilder();
var existingStandardErrorCallback = options.StandardErrorCallback; // Preserve the existing callback if it exists.
options.StandardErrorCallback = (line) => {
stderrBuilder.AppendLine(line);
existingStandardErrorCallback?.Invoke(line);
};
// Retry when MSBuild returns success but produces no output, which can happen
// due to MSBuild server contention (e.g. when another AppHost build is running).
const int maxRetries = 3;
for (var attempt = 0; attempt < maxRetries; attempt++)
{
var stdoutBuilder = new StringBuilder();
options.StandardOutputCallback = (line) => {
stdoutBuilder.AppendLine(line);
existingStandardOutputCallback?.Invoke(line);
};

var exitCode = await ExecuteAsync(
args: cliArgs,
env: null,
projectFile: projectFile,
workingDirectory: projectFile.Directory!,
backchannelCompletionSource: null,
options: options,
cancellationToken: cancellationToken);
var stderrBuilder = new StringBuilder();
options.StandardErrorCallback = (line) => {
stderrBuilder.AppendLine(line);
existingStandardErrorCallback?.Invoke(line);
};

var stdout = stdoutBuilder.ToString();
var stderr = stderrBuilder.ToString();
var exitCode = await ExecuteAsync(
args: cliArgs,
env: null,
projectFile: projectFile,
workingDirectory: projectFile.Directory!,
backchannelCompletionSource: null,
options: options,
cancellationToken: cancellationToken);

if (exitCode != 0)
{
logger.LogError(
"Failed to get items and properties from project. Exit code was: {ExitCode}. See debug logs for more details. Stderr: {Stderr}, Stdout: {Stdout}",
exitCode,
stderr,
stdout
);
var stdout = stdoutBuilder.ToString();
var stderr = stderrBuilder.ToString();

return (exitCode, null);
}
else
{
var json = JsonDocument.Parse(stdout!);
if (exitCode != 0)
{
logger.LogError(
"Failed to get items and properties from project. Exit code was: {ExitCode}. See debug logs for more details. Stderr: {Stderr}, Stdout: {Stdout}",
exitCode,
stderr,
stdout
);

return (exitCode, null);
}

if (string.IsNullOrWhiteSpace(stdout))
{
if (attempt < maxRetries - 1)
{
logger.LogWarning(
"dotnet msbuild returned exit code 0 but produced no output (attempt {Attempt}/{MaxRetries}). Retrying after delay. Stderr: {Stderr}",
attempt + 1,
maxRetries,
stderr);
await Task.Delay(TimeSpan.FromSeconds(attempt + 1), cancellationToken).ConfigureAwait(false);
continue;
}

logger.LogWarning(
"dotnet msbuild returned exit code 0 but produced no output after {MaxRetries} attempts. Stderr: {Stderr}",
maxRetries,
stderr);
return (exitCode, null);
}

var json = JsonDocument.Parse(stdout);
return (exitCode, json);
}

// Should not be reached, but return failure as a safety net
return (1, null);
}

public async Task<int> RunAsync(FileInfo projectFile, bool watch, bool noBuild, bool noRestore, string[] args, IDictionary<string, string>? env, TaskCompletionSource<IAppHostCliBackchannel>? backchannelCompletionSource, DotNetCliRunnerInvocationOptions options, CancellationToken cancellationToken)
Expand Down
1 change: 1 addition & 0 deletions tests/Aspire.Cli.EndToEnd.Tests/MultipleAppHostTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,4 @@ public async Task DetachFormatJsonProducesValidJson()
await pendingRun;
}
}

Loading