Skip to content
Open
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
40 changes: 40 additions & 0 deletions src/Aspire.Cli/Backchannel/AppHostCliBackchannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ internal interface IAppHostCliBackchannel
Task CompletePromptResponseAsync(string promptId, PublishingPromptInputAnswer[] answers, CancellationToken cancellationToken);
Task UpdatePromptResponseAsync(string promptId, PublishingPromptInputAnswer[] answers, CancellationToken cancellationToken);
Task<GetPipelineStepsResponse> GetPipelineStepsAsync(string? step, CancellationToken cancellationToken);
Task<GetPipelineOutputsResponse> GetPipelineOutputsAsync(CancellationToken cancellationToken);
Task AuthorizePipelineExecutionAsync(CancellationToken cancellationToken);
Task<UploadFileResponse> UploadFileAsync(string filePath, string fileName, CancellationToken cancellationToken);
}

Expand Down Expand Up @@ -554,6 +556,44 @@ public async Task<GetPipelineStepsResponse> GetPipelineStepsAsync(string? step,
return response;
}

public async Task<GetPipelineOutputsResponse> GetPipelineOutputsAsync(CancellationToken cancellationToken)
{
using var activity = telemetry.StartDiagnosticActivity();
var rpc = await GetRpcTaskAsync().WaitAsync(cancellationToken).ConfigureAwait(false);

logger.LogDebug("Requesting pipeline outputs.");

var response = await rpc.InvokeWithProfilingAsync<GetPipelineOutputsResponse>(
profilingTelemetry,
"apphost",
"GetPipelineOutputsAsync",
[new GetPipelineOutputsRequest()],
cancellationToken).ConfigureAwait(false);

logger.LogDebug("Received {OutputCount} pipeline outputs.", response.Outputs.Length);
return response;
}

public async Task AuthorizePipelineExecutionAsync(CancellationToken cancellationToken)
{
using var activity = telemetry.StartDiagnosticActivity();
var rpc = await GetRpcTaskAsync().WaitAsync(cancellationToken).ConfigureAwait(false);

logger.LogDebug("Authorizing pipeline execution.");

var response = await rpc.InvokeWithProfilingAsync<AuthorizePipelineExecutionResponse>(
profilingTelemetry,
"apphost",
"AuthorizePipelineExecutionAsync",
[new AuthorizePipelineExecutionRequest()],
cancellationToken).ConfigureAwait(false);

if (!response.IsAuthorized)
{
throw new InvalidOperationException("The AppHost did not authorize pipeline execution.");
}
}

public async Task<UploadFileResponse> UploadFileAsync(string filePath, string fileName, CancellationToken cancellationToken)
{
using var activity = telemetry.StartDiagnosticActivity();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ namespace Aspire.Cli.Backchannel;
[JsonSerializable(typeof(PipelineStepInfo[]))]
[JsonSerializable(typeof(GetPipelineStepsRequest))]
[JsonSerializable(typeof(GetPipelineStepsResponse))]
[JsonSerializable(typeof(PipelineOutputInfo))]
[JsonSerializable(typeof(PipelineOutputInfo[]))]
[JsonSerializable(typeof(PipelineOutputStepInfo))]
[JsonSerializable(typeof(PipelineOutputStepInfo[]))]
[JsonSerializable(typeof(GetPipelineOutputsRequest))]
[JsonSerializable(typeof(GetPipelineOutputsResponse))]
[JsonSerializable(typeof(AuthorizePipelineExecutionRequest))]
[JsonSerializable(typeof(AuthorizePipelineExecutionResponse))]
[JsonSerializable(typeof(UploadFileRequest))]
[JsonSerializable(typeof(UploadFileResponse))]
[JsonSerializable(typeof(FileReferenceDto[]))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

#pragma warning disable ASPIREPIPELINES001
#pragma warning disable ASPIREPIPELINES004
#pragma warning disable ASPIREAZURE001

using System.Diagnostics.CodeAnalysis;
Expand Down Expand Up @@ -71,6 +72,7 @@ public AzureAppServiceEnvironmentResource(string name, Action<AzureResourceInfra
{
Name = $"validate-appservice-config-{name}",
Description = $"Validates Azure App Service configuration for {name}.",
SupportsOutputPathRelocation = true,
Action = ctx => ValidateAppServiceConfigurationAsync(ctx, model),
RequiredBySteps = [WellKnownPipelineSteps.Publish],
DependsOnSteps = [WellKnownPipelineSteps.PublishPrereq]
Expand Down
4 changes: 2 additions & 2 deletions src/Aspire.Hosting.Azure/AzureEnvironmentResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public AzureEnvironmentResource(string name, ParameterResource location, Paramet
{
Name = $"publish-{Name}",
Description = $"Publishes the Azure environment configuration for {Name}.",
SupportsOutputPathRelocation = true,
Action = ctx => PublishAsync(ctx),
RequiredBySteps = [WellKnownPipelineSteps.Publish],
DependsOnSteps = [WellKnownPipelineSteps.PublishPrereq]
Expand Down Expand Up @@ -184,9 +185,8 @@ private static void AddToPipelineSummary(PipelineStepContext ctx, ProvisioningCo
private Task PublishAsync(PipelineStepContext context)
{
var azureProvisioningOptions = context.Services.GetRequiredService<IOptions<AzureProvisioningOptions>>();
var outputService = context.Services.GetRequiredService<IPipelineOutputService>();
var publishingContext = new AzurePublishingContext(
outputService.GetOutputDirectory(),
context.Outputs.PrimaryOutput.OutputPath,
azureProvisioningOptions.Value,
context.Services,
context.Logger,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#pragma warning disable ASPIREPIPELINES001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
#pragma warning disable ASPIREPIPELINES002
#pragma warning disable ASPIREPIPELINES003 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
#pragma warning disable ASPIREPIPELINES004
#pragma warning disable ASPIRECONTAINERRUNTIME001

using System.Diagnostics.CodeAnalysis;
Expand Down Expand Up @@ -78,6 +79,7 @@ public DockerComposeEnvironmentResource(string name) : base(name)
{
Name = $"publish-{Name}",
Description = $"Publishes the Docker Compose environment configuration for {Name}.",
SupportsOutputPathRelocation = true,
Action = ctx => PublishAsync(ctx)
};
publishStep.RequiredBy(WellKnownPipelineSteps.Publish);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ private static void ConfigureBundleContainer(IResourceBuilder<EFMigrationResourc

// Resolves the pipeline output directory the same way PipelineOutputService does, so the
// Docker build context captured at configuration time matches the path the generate step
// uses at execution time (via IPipelineOutputService.GetOutputDirectory()).
// uses at execution time (via context.Outputs.PrimaryOutput.OutputPath).
private static string ResolvePipelineOutputDirectory(IResourceBuilder<EFMigrationResource> builder)
{
var configured = builder.ApplicationBuilder.Configuration["Pipeline:OutputPath"];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

#pragma warning disable ASPIREPIPELINES001
#pragma warning disable ASPIREPIPELINES004
#pragma warning disable ASPIREDOTNETTOOL

using Aspire.Hosting.ApplicationModel;
Expand Down Expand Up @@ -244,6 +245,7 @@ internal static IEnumerable<PipelineStep> CreateMigrationPipelineStep(PipelineSt
Name = scriptStepName!,
Description = $"Generate EF Core migration SQL script for {migrationResource.Name}",
Resource = migrationResource,
SupportsOutputPathRelocation = true,
DependsOnSteps = scriptDependsOn,
RequiredBySteps = [WellKnownPipelineSteps.Publish],
Action = stepContext => ExecutePublishPipelineOperationAsync(
Expand Down Expand Up @@ -288,6 +290,7 @@ internal static IEnumerable<PipelineStep> CreateMigrationPipelineStep(PipelineSt
Name = bundleStepName!,
Description = $"Generate EF Core migration bundle for {migrationResource.Name}",
Resource = migrationResource,
SupportsOutputPathRelocation = true,
DependsOnSteps = bundleDependsOn,
RequiredBySteps = requiredBy,
Action = stepContext => ExecutePublishPipelineOperationAsync(
Expand Down Expand Up @@ -356,9 +359,6 @@ private static async Task ExecutePublishPipelineOperationAsync(
Func<EFCoreOperationExecutor, string?, Task<EFOperationResult>> executeOperation)
{
var logger = stepContext.Logger;
#pragma warning disable ASPIREPIPELINES004 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
var pipelineOutputService = stepContext.Services.GetRequiredService<IPipelineOutputService>();
#pragma warning restore ASPIREPIPELINES004 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.

using var executor = new EFCoreOperationExecutor(
migrationResource.ProjectResource,
Expand All @@ -369,7 +369,7 @@ private static async Task ExecutePublishPipelineOperationAsync(
stepContext.Services,
migrationResource.ToolResource);

var outputDir = Path.Combine(pipelineOutputService.GetOutputDirectory(), "efmigrations");
var outputDir = Path.Combine(stepContext.Outputs.PrimaryOutput.OutputPath, "efmigrations");
Directory.CreateDirectory(outputDir);

logger.LogInformation("Generating {Operation} for '{ResourceName}'...", operationName, migrationResource.Name);
Expand Down
2 changes: 2 additions & 0 deletions src/Aspire.Hosting.JavaScript/JavaScriptHostingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#pragma warning disable ASPIREDOCKERFILEBUILDER001
#pragma warning disable ASPIREPIPELINES001
#pragma warning disable ASPIREPIPELINES004
#pragma warning disable ASPIRECERTIFICATES001
#pragma warning disable ASPIREEXTENSION001
#pragma warning disable ASPIRECOMMAND001
Expand Down Expand Up @@ -1366,6 +1367,7 @@ Task WriteValidatedContainerAsync(ManifestPublishingContext context)
{
Name = validationStepName,
Description = $"Validates that JavaScript app '{resource.Name}' does not publish an ignored run script with an existing Dockerfile.",
SupportsOutputPathRelocation = true,
RequiredBySteps = [WellKnownPipelineSteps.Build, WellKnownPipelineSteps.Publish],
Resource = containerBuilder.Resource,
Action = _ =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

#pragma warning disable ASPIREPIPELINES001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
#pragma warning disable ASPIREPIPELINES004
#pragma warning disable ASPIRECOMPUTE002 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.

using System.Diagnostics.CodeAnalysis;
Expand Down Expand Up @@ -231,6 +232,7 @@ public KubernetesEnvironmentResource(string name) : base(name)
{
Name = $"publish-{Name}",
Description = $"Publishes the Kubernetes environment configuration for {Name}.",
SupportsOutputPathRelocation = true,
Action = ctx => PublishAsync(ctx)
};
// Depend on publish-prereq so that process-parameters has run before we
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ internal PipelineStep CreatePipelineStep()
{
Name = $"publish-radius-{_environment.Name}",
Description = $"Publish Radius environment '{_environment.Name}' as Bicep",
SupportsOutputPathRelocation = true,
Action = ExecuteAsync
};
step.RequiredBy(WellKnownPipelineSteps.Publish);
Expand Down
58 changes: 57 additions & 1 deletion src/Aspire.Hosting/Backchannel/AppHostRpcTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,8 @@ public Task<string[]> GetCapabilitiesAsync(CancellationToken cancellationToken)
_ = cancellationToken;
return Task.FromResult(new string[] {
"baseline.v2",
"pipeline-steps.v1"
"pipeline-steps.v1",
"pipeline-outputs.v1"
});
}
#pragma warning restore CA1822
Expand Down Expand Up @@ -323,4 +324,59 @@ public async Task<GetPipelineStepsResponse> GetPipelineStepsAsync(GetPipelineSte
}).ToArray()
};
}

public async Task<GetPipelineOutputsResponse> GetPipelineOutputsAsync(
GetPipelineOutputsRequest? request = null,
CancellationToken cancellationToken = default)
{
using var activity = profilingTelemetry.StartJsonRpcServerCall(
nameof(GetPipelineOutputsAsync),
streaming: false,
request?.TraceContext);
#pragma warning disable ASPIREPIPELINES004
var registry = serviceProvider.GetRequiredService<PipelineOutputRegistry>();
await registry.WaitForPreparationAsync(cancellationToken).ConfigureAwait(false);
var outputs = registry.GetOutputs();
var steps = registry.GetSelectedSteps();

return new GetPipelineOutputsResponse
{
AppHostDirectory = registry.AppHostDirectory,
State = registry.GetExecutionState().ToString(),
Steps = steps.Select(step => new PipelineOutputStepInfo
{
Name = step.Name,
SupportsOutputPathRelocation = registry.SupportsOutputPathRelocation(step)
}).ToArray(),
Outputs = outputs.Select(output => new PipelineOutputInfo
{
IsPrimary = output.IsPrimary,
PublisherName = output.PublisherName,
Name = output.Name,
Kind = output.Kind.ToString(),
OutputPath = output.OutputPath,
LogicalTargetPath = output.LogicalTargetPath
}).ToArray()
};
#pragma warning restore ASPIREPIPELINES004
}

public Task<AuthorizePipelineExecutionResponse> AuthorizePipelineExecutionAsync(
AuthorizePipelineExecutionRequest? request = null,
CancellationToken cancellationToken = default)
{
using var activity = profilingTelemetry.StartJsonRpcServerCall(
nameof(AuthorizePipelineExecutionAsync),
streaming: false,
request?.TraceContext);
_ = cancellationToken;

var registry = serviceProvider.GetRequiredService<PipelineOutputRegistry>();
registry.AuthorizeExecution();

return Task.FromResult(new AuthorizePipelineExecutionResponse
{
IsAuthorized = true
});
}
}
Loading
Loading