diff --git a/src/Aspire.Cli/Commands/PublishCommand.cs b/src/Aspire.Cli/Commands/PublishCommand.cs index abb14bbf386..21021e1aa97 100644 --- a/src/Aspire.Cli/Commands/PublishCommand.cs +++ b/src/Aspire.Cli/Commands/PublishCommand.cs @@ -49,11 +49,10 @@ protected override string[] GetRunArguments(string? fullyQualifiedOutputPath, st { var baseArgs = new List { "--operation", "publish", "--step", "publish" }; - var targetPath = fullyQualifiedOutputPath is not null - ? fullyQualifiedOutputPath - : Path.Combine(Environment.CurrentDirectory, "aspire-output"); - - baseArgs.AddRange(["--output-path", targetPath]); + if (fullyQualifiedOutputPath is not null) + { + baseArgs.AddRange(["--output-path", fullyQualifiedOutputPath]); + } // Add --log-level and --envionment flags if specified var logLevel = parseResult.GetValue(_logLevelOption); diff --git a/src/Aspire.Hosting.Azure/AzureEnvironmentResource.cs b/src/Aspire.Hosting.Azure/AzureEnvironmentResource.cs index 7f4948cc9f4..ea3fa797601 100644 --- a/src/Aspire.Hosting.Azure/AzureEnvironmentResource.cs +++ b/src/Aspire.Hosting.Azure/AzureEnvironmentResource.cs @@ -225,7 +225,7 @@ private Task PublishAsync(PipelineStepContext context) var azureProvisioningOptions = context.Services.GetRequiredService>(); var activityReporter = context.PipelineContext.Services.GetRequiredService(); var publishingContext = new AzurePublishingContext( - context.OutputPath ?? throw new InvalidOperationException("OutputPath is required for Azure publishing."), + context.OutputPath, azureProvisioningOptions.Value, context.Services, context.Logger, diff --git a/src/Aspire.Hosting.Docker/Aspire.Hosting.Docker.csproj b/src/Aspire.Hosting.Docker/Aspire.Hosting.Docker.csproj index c0db0e8d78a..158d1ce9192 100644 --- a/src/Aspire.Hosting.Docker/Aspire.Hosting.Docker.csproj +++ b/src/Aspire.Hosting.Docker/Aspire.Hosting.Docker.csproj @@ -12,7 +12,6 @@ - diff --git a/src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs b/src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs index 45bc3e3d213..6aa0612ec76 100644 --- a/src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs +++ b/src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs @@ -9,7 +9,6 @@ using Aspire.Hosting.Docker.Resources; using Aspire.Hosting.Pipelines; using Aspire.Hosting.Publishing; -using Aspire.Hosting.Utils; using Microsoft.Extensions.DependencyInjection; namespace Aspire.Hosting.Docker; @@ -87,7 +86,11 @@ ReferenceExpression IComputeEnvironmentResource.GetHostAddressExpression(Endpoin private Task PublishAsync(PipelineStepContext context) { - var outputPath = PublishingContextUtils.GetEnvironmentOutputPath(context, this); + // Inline logic from PublishingContextUtils.GetEnvironmentOutputPath + var outputPath = context.Model.Resources.OfType().Count() > 1 + ? Path.Combine(context.OutputPath, Name) + : context.OutputPath; + var activityReporter = context.PipelineContext.Services.GetRequiredService(); var imageBuilder = context.Services.GetRequiredService(); diff --git a/src/Aspire.Hosting.Docker/DockerComposePublishingContext.cs b/src/Aspire.Hosting.Docker/DockerComposePublishingContext.cs index cf99ed9c19f..4d04d4f2da3 100644 --- a/src/Aspire.Hosting.Docker/DockerComposePublishingContext.cs +++ b/src/Aspire.Hosting.Docker/DockerComposePublishingContext.cs @@ -36,7 +36,7 @@ internal sealed class DockerComposePublishingContext( UnixFileMode.OtherRead | UnixFileMode.OtherWrite; public readonly IResourceContainerImageBuilder ImageBuilder = imageBuilder; - public readonly string OutputPath = outputPath ?? throw new InvalidOperationException("OutputPath is required for Docker Compose publishing."); + public readonly string OutputPath = outputPath; internal async Task WriteModelAsync(DistributedApplicationModel model, DockerComposeEnvironmentResource environment) { diff --git a/src/Aspire.Hosting.Kubernetes/Aspire.Hosting.Kubernetes.csproj b/src/Aspire.Hosting.Kubernetes/Aspire.Hosting.Kubernetes.csproj index ebda667fe62..e1eb25b4199 100644 --- a/src/Aspire.Hosting.Kubernetes/Aspire.Hosting.Kubernetes.csproj +++ b/src/Aspire.Hosting.Kubernetes/Aspire.Hosting.Kubernetes.csproj @@ -15,7 +15,6 @@ - diff --git a/src/Aspire.Hosting.Kubernetes/KubernetesEnvironmentResource.cs b/src/Aspire.Hosting.Kubernetes/KubernetesEnvironmentResource.cs index 32a24c04c22..7ab2e9036bb 100644 --- a/src/Aspire.Hosting.Kubernetes/KubernetesEnvironmentResource.cs +++ b/src/Aspire.Hosting.Kubernetes/KubernetesEnvironmentResource.cs @@ -8,7 +8,6 @@ using Aspire.Hosting.ApplicationModel; using Aspire.Hosting.Kubernetes.Extensions; using Aspire.Hosting.Pipelines; -using Aspire.Hosting.Utils; namespace Aspire.Hosting.Kubernetes; @@ -112,7 +111,10 @@ ReferenceExpression IComputeEnvironmentResource.GetHostAddressExpression(Endpoin private Task PublishAsync(PipelineStepContext context) { - var outputPath = PublishingContextUtils.GetEnvironmentOutputPath(context, this); + // Inline logic from PublishingContextUtils.GetEnvironmentOutputPath + var outputPath = context.Model.Resources.OfType().Count() > 1 + ? Path.Combine(context.OutputPath, Name) + : context.OutputPath; var kubernetesContext = new KubernetesPublishingContext( context.ExecutionContext, diff --git a/src/Aspire.Hosting.Kubernetes/KubernetesPublishingContext.cs b/src/Aspire.Hosting.Kubernetes/KubernetesPublishingContext.cs index 257dc5aa703..9e6d34867fd 100644 --- a/src/Aspire.Hosting.Kubernetes/KubernetesPublishingContext.cs +++ b/src/Aspire.Hosting.Kubernetes/KubernetesPublishingContext.cs @@ -20,7 +20,7 @@ internal sealed class KubernetesPublishingContext( ILogger logger, CancellationToken cancellationToken = default) { - public readonly string OutputPath = outputPath ?? throw new InvalidOperationException("OutputPath is required for Kubernetes publishing."); + public readonly string OutputPath = outputPath; private readonly Dictionary> _helmValues = new() { diff --git a/src/Aspire.Hosting/Pipelines/PipelineContext.cs b/src/Aspire.Hosting/Pipelines/PipelineContext.cs index c5edf2ff6ad..963d2ee9018 100644 --- a/src/Aspire.Hosting/Pipelines/PipelineContext.cs +++ b/src/Aspire.Hosting/Pipelines/PipelineContext.cs @@ -3,6 +3,8 @@ using System.Diagnostics.CodeAnalysis; using Aspire.Hosting.ApplicationModel; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Aspire.Hosting.Pipelines; @@ -53,5 +55,24 @@ public sealed class PipelineContext( /// /// Gets the output path for deployment artifacts. /// - public string? OutputPath { get; } = outputPath; + public string OutputPath { get; } = outputPath ?? Path.Combine(Environment.CurrentDirectory, "aspire-output"); + + /// + /// Gets the intermediate output path for temporary build artifacts. + /// + public string IntermediateOutputPath { get; } = GetIntermediateOutputPath(serviceProvider); + + private static string GetIntermediateOutputPath(IServiceProvider serviceProvider) + { + var configuration = serviceProvider.GetRequiredService(); + var appHostSha = configuration["AppHost:PathSha256"]; + + if (!string.IsNullOrEmpty(appHostSha)) + { + return Directory.CreateTempSubdirectory($"aspire-{appHostSha}").FullName; + } + + // Fallback if AppHost:PathSha256 is not available + return Directory.CreateTempSubdirectory("aspire").FullName; + } } diff --git a/src/Aspire.Hosting/Pipelines/PipelineStepContext.cs b/src/Aspire.Hosting/Pipelines/PipelineStepContext.cs index d5653f31575..e16eade56a0 100644 --- a/src/Aspire.Hosting/Pipelines/PipelineStepContext.cs +++ b/src/Aspire.Hosting/Pipelines/PipelineStepContext.cs @@ -62,7 +62,12 @@ public sealed class PipelineStepContext /// /// Gets the output path for deployment artifacts. /// - public string? OutputPath => PipelineContext.OutputPath; + public string OutputPath => PipelineContext.OutputPath; + + /// + /// Gets the intermediate output path for temporary build artifacts. + /// + public string IntermediateOutputPath => PipelineContext.IntermediateOutputPath; } /// diff --git a/src/Shared/PublishingContextUtils.cs b/src/Shared/PublishingContextUtils.cs deleted file mode 100644 index a9e8031821b..00000000000 --- a/src/Shared/PublishingContextUtils.cs +++ /dev/null @@ -1,25 +0,0 @@ -// 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 ASPIRECOMPUTE001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. -#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 Aspire.Hosting.Pipelines; - -namespace Aspire.Hosting.Utils; - -internal static class PublishingContextUtils -{ - public static string GetEnvironmentOutputPath(PipelineStepContext context, IComputeEnvironmentResource environment) - { - if (context.Model.Resources.OfType().Count() > 1) - { - // If there are multiple compute environments, append the environment name to the output path - return Path.Combine(context.OutputPath!, environment.Name); - } - - // If there is only one compute environment, use the root output path - return context.OutputPath!; - } -}