-
Notifications
You must be signed in to change notification settings - Fork 167
Description
Proposed topic or title
Progress reporting for .NET Aspire
publishing and deployment
Location in table of contents
Fundamentals > Publishing and Deployment > Building container images
Reason for the article
Clear, structured output during long-running aspire publish
and aspire deploy
commands reduces uncertainty and surfaces failures early.
PublishingActivityProgressReporter
(injected as IPublishingActivityReporter
) enables publishers and deployers to emit:
- Steps — top-level phases such as Build images or Deploy workloads.
- Tasks — discrete units of work nested under a step.
- Completion states —
Completed
,Warning
, orError
, each rendered by the CLI with ✅, ⚠, or ✗.
The API guarantees ordered emissions, enforces completion, and streams updates to the console and dashboard in real time. This article standardizes usage across first- and third-party extensions.
Article abstract
The document should outline the following details WRT to the lifecycle and contract of PublishingActivityProgressReporter
.
- Acquisition — retrieved from
PublishingContext.ActivityReporter
orDeployingContext.ActivityReporter
. - Step creation —
CreateStepAsync(title, ct)
returns anIPublishingActivityStep
; disposal finalises the step object. - Task creation —
IPublishingActivityStep.CreateTaskAsync(title, ct)
returns anIPublishingActivityTask
. - State transitions —
SucceedAsync
,WarnAsync
,FailAsync
; each accepts a summary message and propagates cancellation tokens. - Completion —
CompletePublishAsync(message, state, isDeploy, ct)
marks the entire operation, flushes buffered events, and sets the final exit code used by the CLI.
End-to-end example: Publishing and deploy callbacks
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Publishing;
using Microsoft.Extensions.Logging;
public sealed class DemoEnvironmentResource : Resource
{
public DemoEnvironmentResource(string name) : base(name)
{
Annotations.Add(new PublishingCallbackAnnotation(PublishAsync));
Annotations.Add(new DeployingCallbackAnnotation(DeployAsync));
}
// ---------- PUBLISH PIPELINE ----------
private static async Task PublishAsync(PublishingContext context)
{
var reporter = context.ActivityReporter;
// STEP: Build container images
await using (var buildStep = await reporter.CreateStepAsync(
"Build container images", context.CancellationToken))
{
var apiTask = await buildStep.CreateTaskAsync(
"service-api image", context.CancellationToken);
// dotnet publish -t:PublishContainer …
await apiTask.SucceedAsync("service-api built", context.CancellationToken);
var uiTask = await buildStep.CreateTaskAsync(
"service-ui image", context.CancellationToken);
await uiTask.SucceedAsync("service-ui built", context.CancellationToken);
await buildStep.SucceedAsync(
"All images produced", context.CancellationToken);
}
// STEP: Generate deployment manifests
await using (var manifestStep = await reporter.CreateStepAsync(
"Generate manifests", context.CancellationToken))
{
var bicepTask = await manifestStep.CreateTaskAsync(
"Write main.bicep", context.CancellationToken);
// Write file to context.OutputPath …
await bicepTask.SucceedAsync(
$"main.bicep at {context.OutputPath}", context.CancellationToken);
await manifestStep.SucceedAsync("Manifests ready", context.CancellationToken);
}
// Finalise publishing
await reporter.CompletePublishAsync(
completionMessage: "Publish pipeline completed",
completionState: CompletionState.Completed,
cancellationToken: context.CancellationToken);
}
// ---------- DEPLOY PIPELINE ----------
private static async Task DeployAsync(DeployingContext context)
{
var reporter = context.ActivityReporter;
await using (var deployStep = await reporter.CreateStepAsync(
"Apply Kubernetes resources", context.CancellationToken))
{
var apiTask = await deployStep.CreateTaskAsync(
"kubectl apply api.yaml", context.CancellationToken);
// kubectl apply -f api.yaml …
await apiTask.SucceedAsync("api workload applied", context.CancellationToken);
var uiTask = await deployStep.CreateTaskAsync(
"kubectl apply ui.yaml", context.CancellationToken);
await uiTask.SucceedAsync("ui workload applied", context.CancellationToken);
await deployStep.SucceedAsync("Cluster updated", context.CancellationToken);
}
await reporter.CompletePublishAsync(
completionMessage: "Deployment completed",
completionState: CompletionState.Completed,
isDeploy: true,
cancellationToken: context.CancellationToken);
}
}
Behavior summary
Aspect | Description |
---|---|
Step hierarchy | Steps form a strict tree; tasks belong to a single step. Nested steps are unsupported, ensuring linear top-level phases. |
Ordering | Creation and completion events preserve call order; the reporter serialises updates to avoid interleaving. |
State machine | Each step/task starts in Running and transitions exactly once to Completed, Warning, or Error. Attempting multiple transitions throws. |
Cancellation | All APIs accept CancellationToken and propagate OperationCanceledException to the CLI. |
CLI integration | The Aspire CLI subscribes to reporter events and renders status glyphs, elapsed time, and coloured output; failures set the CLI process exit code. |
Concurrency | Thread-safe implementation supports parallel task creation under the same step; completion ordering remains deterministic. |
Disposal contract | Awaiting disposal of an IPublishingActivityStep automatically completes the step if unfinished, preventing orphaned phases. |
Implementation guidance
- Always encapsulate long-running logical phases in a step; avoid emitting raw tasks without a parent step.
- Keep titles concise (< 60 characters); the CLI truncates longer strings.
- Call
CompletePublishAsync
exactly once per publishing or deployment operation. - Treat warnings as recoverable; allow subsequent steps to proceed. Treat errors as fatal; fail fast and surface diagnostics.
- Prefer asynchronous, cancellation-aware IO to avoid blocking reporter event processing.
Relevant searches
"PublishingActivityProgressReporter API", "IPublishingActivityReporter CreateStepAsync",
"PublishingActivityProgressReporter CompletePublishAsync", "Aspire progress CLI output"