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
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; // should 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();
}
}
}
}
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