Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 2 additions & 0 deletions playground/publishers/Publishers.AppHost/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
.WithEnvironment("P3", param3)
.WithReference(backend).WaitFor(backend);

builder.AddDockerfile("mycontainer", "qots");

#if !SKIP_DASHBOARD_REFERENCE
// This project is only added in playground projects to support development/debugging
// of the dashboard. It is not required in end developer code. Comment out this code
Expand Down
12 changes: 12 additions & 0 deletions playground/publishers/Publishers.AppHost/qots/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Stage 1: Build the Go program
ARG GO_VERSION=1.23
FROM mcr.microsoft.com/oss/go/microsoft/golang:${GO_VERSION} AS builder
WORKDIR /app
COPY . .
RUN go build qots.go

# Stage 2: Run the Go program
FROM mcr.microsoft.com/cbl-mariner/base/core:2.0
WORKDIR /app
COPY --from=builder /app/qots .
CMD ["./qots"]
28 changes: 28 additions & 0 deletions playground/publishers/Publishers.AppHost/qots/qots.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package main

import (
"fmt"
"math/rand"
"runtime"
"time"
)

func main() {
fmt.Println("Go runtime version:", runtime.Version())

quotes := []string{
"With great power comes great responsibility. - Spider-Man",
"I'm Batman. - Batman",
"I am Iron Man. - Iron Man",
"Why so serious? - The Joker",
"I'm always angry. - The Hulk",
}

rand.Seed(time.Now().UnixNano())

for {
quote := quotes[rand.Intn(len(quotes))]
fmt.Println(quote)
time.Sleep(time.Second)
}
}
51 changes: 41 additions & 10 deletions src/Aspire.Cli/Commands/PublishCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ protected override async Task<int> ExecuteAsync(ParseResult parseResult, Cancell
.HighlightStyle(Style.Parse("darkmagenta"))
.AddChoices(publishers!);

publisher = AnsiConsole.Prompt(publisherPrompt);
publisher = await AnsiConsole.PromptAsync(publisherPrompt, cancellationToken);
}

AnsiConsole.MarkupLine($":hammer_and_wrench: Generating artifacts for '{publisher}' publisher...");
Expand All @@ -144,7 +144,7 @@ protected override async Task<int> ExecuteAsync(ParseResult parseResult, Cancell

var backchannelCompletionSource = new TaskCompletionSource<AppHostBackchannel>();

var launchingAppHostTask = context.AddTask("Launching apphost");
var launchingAppHostTask = context.AddTask(":play_button: Launching apphost");
launchingAppHostTask.IsIndeterminate();
launchingAppHostTask.StartTask();

Expand All @@ -159,6 +159,7 @@ protected override async Task<int> ExecuteAsync(ParseResult parseResult, Cancell

var backchannel = await backchannelCompletionSource.Task.ConfigureAwait(false);

launchingAppHostTask.Description = $":check_mark: Launching apphost";
launchingAppHostTask.Value = 100;
launchingAppHostTask.StopTask();

Expand All @@ -176,37 +177,67 @@ protected override async Task<int> ExecuteAsync(ParseResult parseResult, Cancell
progressTasks.Add(publishingActivity.Id, progressTask);
}

progressTask.Description = $"{publishingActivity.StatusText}";
progressTask.Description = $":play_button: {publishingActivity.StatusText}";

if (publishingActivity.IsComplete)
if (publishingActivity.IsComplete && !publishingActivity.IsError)
{
progressTask.Description = $":check_mark: {publishingActivity.StatusText}";
progressTask.Value = 100;
progressTask.StopTask();
}
else if (publishingActivity.IsError)
{
progressTask.Value = 100;
progressTask.StopTask();
progressTask.Description = $"[red bold]:cross_mark: {publishingActivity.StatusText}[/]";
progressTask.Value = 0;
break;
}
else
{
// Keep going man!
}
}

await backchannel.RequestStopAsync(cancellationToken).ConfigureAwait(false);

// When we are running in publish mode we don't want the app host to
// stop itself while we might still be streaming data back across
// the RPC backchannel. So we need to take responsibility for stopping
// the app host. If the CLI exits/crashes without explicitly stopping
// the app host the orphan detector in the app host will kick in.
await backchannel.RequestStopAsync(cancellationToken).ConfigureAwait(false);
return await pendingRun;
if (progressTasks.Any(kvp => !kvp.Value.IsFinished))
{
// Depending on the failure the publisher may return a zero
// exit code.
await backchannel.RequestStopAsync(cancellationToken).ConfigureAwait(false);
var exitCode = await pendingRun;

// If we are in the state where we've detected an error because there
// is an incomplete task then we stop the app host, but depending on
// where/how the failure occured, we might still get a zero exit
// code. If we get a non-zero exit code we want to return that
// as it might be useful for diagnostic purposes, however if we don't
// get a non-zero exit code we want to return our built-in exit code
// for failed artifact build.
return exitCode == 0 ? ExitCodeConstants.FailedToBuildArtifacts : exitCode;
}
else
{
// If we are here then all the tasks are finished and we can
// stop the app host.
await backchannel.RequestStopAsync(cancellationToken).ConfigureAwait(false);
var exitCode = await pendingRun;
return exitCode; // shoudl be zero for orderly shutdown but we pass it along anyway.
}
});

if (exitCode != 0)
{
AnsiConsole.MarkupLine($"[red bold]:thumbs_down: The build failed with exit code {exitCode}. For more information run with --debug switch.[/]");
AnsiConsole.MarkupLine($"[red bold]:thumbs_down: Publishing artifacts failed with exit code {exitCode}. For more information run with --debug switch.[/]");
return ExitCodeConstants.FailedToBuildArtifacts;
}
else
{
AnsiConsole.MarkupLine($"[green bold]:thumbs_up: The build completed successfully to: {fullyQualifiedOutputPath}[/]");
AnsiConsole.MarkupLine($"[green bold]:thumbs_up: Successfully published artifacts to: {fullyQualifiedOutputPath}[/]");
return ExitCodeConstants.Success;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ private bool TryGetContainerImageName(IResource resourceInstance, out string? co
$"{resourceInstance.Name}:latest");

containerImageName = $"${{{imageEnvName}}}";
return false;
return true;
}

return resourceInstance.TryGetContainerImageName(out containerImageName);
Expand Down
5 changes: 5 additions & 0 deletions src/Aspire.Hosting/DistributedApplicationRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ await eventing.PublishAsync<AfterPublishEvent>(
logger.LogError(ex, "Failed to publish the distributed application.");
publishingActivity.IsError = true;
await activityReporter.UpdateActivityAsync(publishingActivity, stoppingToken).ConfigureAwait(false);

if (!backchannelService.IsBackchannelExpected)
{
lifetime.StopApplication();
}
Comment on lines +71 to +74
Copy link
Member

Choose a reason for hiding this comment

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

Can't we use a finally?

}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ private async Task<string> BuildProjectContainerImageAsync(IResource resource, C
publishingActivity.IsComplete = true;
await activityReporter.UpdateActivityAsync(publishingActivity, cancellationToken).ConfigureAwait(false);

logger.LogError(
logger.LogDebug(
".NET CLI completed with exit code: {ExitCode}",
process.ExitCode);

Expand Down
Loading