File: Publishing\Publisher.cs
Web Access
Project: src\src\Aspire.Hosting\Aspire.Hosting.csproj (Aspire.Hosting)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#pragma warning disable ASPIREPUBLISHERS001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
 
using Aspire.Hosting.ApplicationModel;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
 
namespace Aspire.Hosting.Publishing;
 
internal class Publisher(
    IPublishingActivityProgressReporter progressReporter,
    ILogger<Publisher> logger,
    IOptions<PublishingOptions> options,
    DistributedApplicationExecutionContext executionContext,
    IServiceProvider serviceProvider) : IDistributedApplicationPublisher
{
    public async Task PublishAsync(DistributedApplicationModel model, CancellationToken cancellationToken)
    {
        if (options.Value.OutputPath == null)
        {
            throw new DistributedApplicationException(
                "The '--output-path [path]' option was not specified."
            );
        }
 
        var outputPath = Path.GetFullPath(options.Value.OutputPath);
 
        // Add a step to do model analysis before publishing/deploying
        var step = await progressReporter.CreateStepAsync(
            "Analyzing model.",
            cancellationToken)
            .ConfigureAwait(false);
 
        var task = await progressReporter.CreateTaskAsync(
            step,
            "Analyzing the distributed application model for publishing and deployment capabilities.",
            cancellationToken)
            .ConfigureAwait(false);
 
        var publishingResources = new List<IResource>();
        var deployingResources = new List<IResource>();
 
        foreach (var resource in model.Resources)
        {
            if (resource.HasAnnotationOfType<PublishingCallbackAnnotation>())
            {
                publishingResources.Add(resource);
            }
 
            if (resource.HasAnnotationOfType<DeployingCallbackAnnotation>())
            {
                deployingResources.Add(resource);
            }
        }
 
        (string Message, CompletionState State) taskInfo;
 
        if (options.Value.Deploy)
        {
            taskInfo = deployingResources.Count switch
            {
                0 => ("No resources in the distributed application model support deployment.", CompletionState.CompletedWithError),
                _ => ($"Found {deployingResources.Count} resources that support deployment. ({string.Join(", ", deployingResources.Select(r => r.GetType().Name))})", CompletionState.Completed)
            };
        }
        else
        {
            taskInfo = publishingResources.Count switch
            {
                0 => ("No resources in the distributed application model support publishing.", CompletionState.CompletedWithError),
                _ => ($"Found {publishingResources.Count} resources that support publishing. ({string.Join(", ", publishingResources.Select(r => r.GetType().Name))})", CompletionState.Completed)
            };
        }
 
        await progressReporter.CompleteTaskAsync(
                    task,
                    taskInfo.State,
                    taskInfo.Message,
                    cancellationToken: cancellationToken)
                    .ConfigureAwait(false);
 
        // This should be automagically handled by the progress reporter
        await progressReporter.CompleteStepAsync(
                    step,
                    "Model analysis completed.",
                    taskInfo.State,
                    cancellationToken)
                    .ConfigureAwait(false);
 
        if (taskInfo.State == CompletionState.CompletedWithError)
        {
            // If there are no resources to publish or deploy, we can exit early
            return;
        }
 
        var publishingContext = new PublishingContext(model, executionContext, serviceProvider, logger, cancellationToken, outputPath);
        await publishingContext.WriteModelAsync(model).ConfigureAwait(false);
 
        // If deployment is enabled, run deploying callbacks after publishing
        if (options.Value.Deploy)
        {
            var deployingContext = new DeployingContext(model, executionContext, serviceProvider, logger, cancellationToken, outputPath);
            await deployingContext.WriteModelAsync(model).ConfigureAwait(false);
        }
    }
}