diff --git a/src/frontend/src/content/docs/deployment/custom-deployments.mdx b/src/frontend/src/content/docs/deployment/custom-deployments.mdx index 257478541..505c89730 100644 --- a/src/frontend/src/content/docs/deployment/custom-deployments.mdx +++ b/src/frontend/src/content/docs/deployment/custom-deployments.mdx @@ -86,31 +86,19 @@ The reporter API provides structured access to progress reporting with the follo ## Example: Build container images and report progress -To use these APIs, add a `PublishingCallbackAnnotation`, a `DeployingCallbackAnnotation`, or both to a resource in your app model. You can annotate custom (or built-in) resources by adding annotations to the `IResource.Annotations` collection. +To use these APIs, register pipeline steps with `WithPipelineStepFactory` (resource-level) or `builder.Pipeline.AddStep` (application-level). This lets custom resources participate in publish and deploy flows with explicit step dependencies. As a developer, you can choose to: -- Use both annotations if your resource needs to do work in both publishing and deployment. For example, build images and generate manifests during publishing, then push images or configure deployment targets during deployment. Publishing always happens before deployment, so you can keep logic for each phase separate. -- Use only `PublishingCallbackAnnotation` if your resource only needs to do something during publishing. This is common when you just need to build artifacts or images, but don't need to do anything during deployment. -- Use only `DeployingCallbackAnnotation` if your resource only needs to do something during deployment. This fits cases where you use prebuilt images and just need to deploy or configure them. +- Register a step that is `requiredBy` `WellKnownPipelineSteps.Publish` when your resource needs custom publishing behavior. +- Register a step that is `requiredBy` `WellKnownPipelineSteps.Deploy` when your resource needs custom deployment behavior. +- Register both when your resource performs work in both phases and needs ordering between those steps. -Choose one or more annotations that match your resource's responsibilities to keep your application model clear and maintainable. This separation lets you clearly define logic for each phase, but you can use both the activity reporter and the resource container image builder in either callback as needed. +For broader pipeline orchestration patterns, see [Deployment pipelines](/deployment/pipelines/). For custom resource authoring basics, see [Create custom resources](/extensibility/custom-resources/). -### Example resource with annotations +### Example resource with pipeline steps -For example, consider the `ComputeEnvironmentResource` constructor: - -```csharp title="ComputeEnvironmentResource.cs" -public ComputeEnvironmentResource(string name) : base(name) -{ - Annotations.Add(new PublishingCallbackAnnotation(PublishAsync)); - Annotations.Add(new DeployingCallbackAnnotation(DeployAsync)); -} -``` - -When instantiated, it defines both a publishing and deploying callback annotation. - -Given the example `ComputeEnvironmentResource` type, imagine you have an extension method that you expose so consumers are able to add the compute environment: +For example, consider an extension method that registers two pipeline steps for a custom `ComputeEnvironmentResource`: ```csharp title="ComputeEnvironmentResourceExtensions.cs" public static class ComputeEnvironmentResourceExtensions @@ -121,16 +109,33 @@ public static class ComputeEnvironmentResourceExtensions { var resource = new ComputeEnvironmentResource(name); - return builder.AddResource(resource); + return builder.AddResource(resource) + .WithPipelineStepFactory( + stepName: $"{name}-build-images", + callback: PublishAsync, + requiredBy: [WellKnownPipelineSteps.Publish], + description: "Build container images and write deployment artifacts.") + .WithPipelineStepFactory( + stepName: $"{name}-deploy", + callback: DeployAsync, + dependsOn: [$"{name}-build-images"], + requiredBy: [WellKnownPipelineSteps.Deploy], + description: "Deploy generated artifacts to the target environment."); } } ``` +In the same class, define `PublishAsync` and `DeployAsync` as private static methods that each accept `PipelineStepContext` (shown in the next sections). +The explicit `dependsOn` relationship ensures the deploy step waits for this resource's image-build step, even when you add other custom publish or deploy steps to the pipeline. + The preceding code: - Defines an extension method on the `IDistributedApplicationBuilder`. - Accepts a `name` for the compute environment resource, protected by the `ResourceNameAttribute`. -- Instantiates a `ComputeEnvironmentResource` given the `name` and adds it to the `builder`. +- Instantiates a `ComputeEnvironmentResource` given the `name`. +- Registers a publish step and a deploy step with explicit dependencies. +- Adds both steps through `WithPipelineStepFactory`. +- Uses the validated resource name as the prefix for stable, unambiguous step names. ### Example AppHost @@ -153,131 +158,95 @@ builder.Build().Run(); :::note -Custom resource types and callback-based deployment extensibility aren't yet available in the TypeScript AppHost SDK, so this AppHost example is currently C#-only. +Custom resource types and custom pipeline-step extensibility aren't yet available in the TypeScript AppHost SDK, so this AppHost example is currently C#-only. ::: The preceding code uses the `AddComputeEnvironment` extension method to add the `ComputeEnvironmentResource` to the application model. -### Publishing callback annotation +### Publish pipeline step -When you add the `ComputeEnvironmentResource`, it registers a `PublishingCallbackAnnotation`. The callback uses the `PublishAsync` method: +The publish step callback can build container images and generate deployment artifacts: -```csharp title="ComputeEnvironmentResource.cs" -private static async Task PublishAsync(PublishingContext context) +```csharp title="ComputeEnvironmentResourceExtensions.cs" +private static async Task PublishAsync(PipelineStepContext context) { - var reporter = context.ActivityReporter; var imageBuilder = context.Services.GetRequiredService(); + var outputService = context.Services.GetRequiredService(); + var reportingStep = context.ReportingStep; + + var projectResources = context.Model.Resources + .OfType() + .ToList(); - // Build container images for all project resources in the application - await using (var buildStep = await reporter.CreateStepAsync( - "Build container images", context.CancellationToken)) + if (projectResources.Count > 0) { - // Find all resources that need container images - var projectResources = context.Model.Resources - .OfType() - .ToList(); + var buildTask = await reportingStep.CreateTaskAsync( + $"Building {projectResources.Count} container image(s)", + context.CancellationToken); - if (projectResources.Count > 0) + var buildOptions = new ContainerBuildOptions { - // Configure how images should be built - var buildOptions = new ContainerBuildOptions - { - ImageFormat = ContainerImageFormat.Oci, - TargetPlatform = ContainerTargetPlatform.LinuxAmd64, - OutputPath = Path.Combine(context.OutputPath, "images") - }; - - var buildTask = await buildStep.CreateTaskAsync( - $"Building {projectResources.Count} container image(s)", context.CancellationToken); - - // Build all the container images - await imageBuilder.BuildImagesAsync( - projectResources, buildOptions, context.CancellationToken); - - await buildTask.SucceedAsync( - $"Built {projectResources.Count} image(s) successfully", context.CancellationToken); - } - else - { - var skipTask = await buildStep.CreateTaskAsync( - "No container images to build", context.CancellationToken); + ImageFormat = ContainerImageFormat.Oci, + TargetPlatform = ContainerTargetPlatform.LinuxAmd64, + OutputPath = outputService.GetOutputDirectory() + }; - await skipTask.SucceedAsync("Skipped - no project resources found", context.CancellationToken); - } + await imageBuilder.BuildImagesAsync(projectResources, buildOptions, context.CancellationToken); - await buildStep.SucceedAsync("Container image build completed", context.CancellationToken); + await buildTask.SucceedAsync( + $"Built {projectResources.Count} image(s) successfully", + context.CancellationToken); } - - // Generate deployment manifests - await using (var manifestStep = await reporter.CreateStepAsync( - "Generate deployment manifests", context.CancellationToken)) + else { - 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); + var skipTask = await reportingStep.CreateTaskAsync( + "No container images to build", + context.CancellationToken); + await skipTask.SucceedAsync("Skipped - no project resources found", context.CancellationToken); } - // Complete the publishing operation - await reporter.CompletePublishAsync( - completionMessage: "Publishing pipeline completed successfully", - completionState: CompletionState.Completed, - cancellationToken: context.CancellationToken); + var manifestTask = await reportingStep.CreateTaskAsync( + "Generate deployment manifests", + context.CancellationToken); + // Write deployment files... + await manifestTask.SucceedAsync("Manifests ready", context.CancellationToken); } ``` The preceding code: -- Implements a publishing pipeline that builds container images and generates deployment manifests. +- Implements a publish step that builds container images and generates deployment manifests. - Uses the `IResourceContainerImageBuilder` API to build container images. -- Reports progress and completion status using the `PipelineActivityReporter` API. +- Reports progress with `IReportingStep` tasks from `PipelineStepContext.ReportingStep`. -Your publishing callback might use `IResourceContainerImageBuilder` to build container images, while your deployment callback might use the built images and push them to a registry or deployment target. +Your publish step might use `IResourceContainerImageBuilder` to build images, while your deploy step might use those artifacts and push them to a registry or target environment. -### Deploying callback annotation +### Deploy pipeline step -Like the publishing callback, the deploying callback is registered using the `DeployingCallbackAnnotation` and calls the `DeployAsync` method: +The deploy step callback can apply deployment artifacts and report task-level progress: -```csharp title="ComputeEnvironmentResource.cs" -private static async Task DeployAsync(DeployingContext context) +```csharp title="ComputeEnvironmentResourceExtensions.cs" +private static async Task DeployAsync(PipelineStepContext context) { - var reporter = context.ActivityReporter; + var reportingStep = context.ReportingStep; - await using (var deployStep = await reporter.CreateStepAsync( - "Deploy to target environment", context.CancellationToken)) - { - var applyTask = await deployStep.CreateTaskAsync( - "Apply Kubernetes manifests", context.CancellationToken); - - // Simulate deploying to Kubernetes cluster - await Task.Delay(1_000, context.CancellationToken); + var applyTask = await reportingStep.CreateTaskAsync( + "Apply Kubernetes manifests", + context.CancellationToken); - await applyTask.SucceedAsync("All workloads deployed", context.CancellationToken); - - await deployStep.SucceedAsync("Deployment to cluster completed", context.CancellationToken); - } + // Simulate deploying to Kubernetes cluster + await Task.Delay(1_000, context.CancellationToken); - // Complete the deployment operation - await reporter.CompletePublishAsync( - completionMessage: "Deployment completed successfully", - completionState: CompletionState.Completed, - isDeploy: true, - cancellationToken: context.CancellationToken); + await applyTask.SucceedAsync("All workloads deployed", context.CancellationToken); } ``` The preceding code: - Simulates deploying workloads to a Kubernetes cluster. -- Uses the `PipelineActivityReporter` API to create and manage deployment steps and tasks. -- Reports progress and marks each deployment phase as completed. -- Completes the deployment operation with a final status update. +- Uses `PipelineStepContext.ReportingStep` to create and complete deployment tasks. - Handles cancellation through the provided `CancellationToken`. ## Best practices @@ -295,7 +264,6 @@ When using these APIs, follow these guidelines: - Encapsulate long-running logical phases in steps rather than emitting raw tasks. - Keep titles concise (under 60 characters) as the CLI truncates longer strings. -- Call `CompletePublishAsync` exactly once per publishing or deployment operation. - Treat warnings as recoverable and allow subsequent steps to proceed. - Treat errors as fatal and fail fast with clear diagnostics. - Use asynchronous, cancellation-aware operations to avoid blocking event processing. diff --git a/src/frontend/src/content/docs/deployment/pipelines.mdx b/src/frontend/src/content/docs/deployment/pipelines.mdx index 1eeb8aab4..06608baa5 100644 --- a/src/frontend/src/content/docs/deployment/pipelines.mdx +++ b/src/frontend/src/content/docs/deployment/pipelines.mdx @@ -680,8 +680,8 @@ The old publishing callback system has been removed and replaced with pipeline s **Removed APIs:** - `WithPublishingCallback` extension method -- `PublishingContext` and `PublishingCallbackAnnotation` -- `DeployingContext` and `DeployingCallbackAnnotation` +- `PublishingContext` and the legacy publish callback annotation type +- `DeployingContext` and the legacy deploy callback annotation type - `IDistributedApplicationPublisher` interface **New APIs:** diff --git a/src/frontend/src/content/docs/diagnostics/aspirepipelines001.mdx b/src/frontend/src/content/docs/diagnostics/aspirepipelines001.mdx index 43453e487..5851675e3 100644 --- a/src/frontend/src/content/docs/diagnostics/aspirepipelines001.mdx +++ b/src/frontend/src/content/docs/diagnostics/aspirepipelines001.mdx @@ -28,7 +28,7 @@ This diagnostic applies to the following pipeline infrastructure APIs: - `IReportingTask` - Interface for managing tasks within a step - `CompletionState` - Enumeration for tracking completion status - `PublishingContext` - Context for publishing operations -- `PublishingCallbackAnnotation` - Annotation for publishing callbacks +- Legacy publish callback annotation types - Deprecated annotations used for publish callbacks in older versions - Related extension methods and implementations ## To correct this error diff --git a/src/frontend/src/content/docs/diagnostics/aspirepipelines002.mdx b/src/frontend/src/content/docs/diagnostics/aspirepipelines002.mdx index 9e61a754b..752a0f888 100644 --- a/src/frontend/src/content/docs/diagnostics/aspirepipelines002.mdx +++ b/src/frontend/src/content/docs/diagnostics/aspirepipelines002.mdx @@ -28,7 +28,7 @@ This diagnostic applies to the following deployment state manager APIs: - `Deploy` property in `PublishingOptions` - `ClearCache` property in `PublishingOptions` - `Step` property in `PublishingOptions` -- `DeployingCallbackAnnotation` - Annotation for deploying callbacks +- Legacy deploy callback annotation types - Deprecated annotations used for deploy callbacks in older versions - Azure provisioning context providers - Related extension methods and implementations diff --git a/src/frontend/src/content/docs/ja/deployment/pipelines.mdx b/src/frontend/src/content/docs/ja/deployment/pipelines.mdx index 1528f152b..1d668a9ba 100644 --- a/src/frontend/src/content/docs/ja/deployment/pipelines.mdx +++ b/src/frontend/src/content/docs/ja/deployment/pipelines.mdx @@ -679,8 +679,8 @@ Aspire 13.0はより柔軟なパイプラインシステムで発行コールバ **削除された API:** - `WithPublishingCallback` 拡張メソッド -- `PublishingContext` と `PublishingCallbackAnnotation` -- `DeployingContext` と `DeployingCallbackAnnotation` +- `PublishingContext` とレガシー publish コールバック注釈型 +- `DeployingContext` とレガシー deploy コールバック注釈型 - `IDistributedApplicationPublisher` インターフェース **新しい API:** diff --git a/src/frontend/src/content/docs/ja/whats-new/aspire-13.mdx b/src/frontend/src/content/docs/ja/whats-new/aspire-13.mdx index 7ec632a41..5686183db 100644 --- a/src/frontend/src/content/docs/ja/whats-new/aspire-13.mdx +++ b/src/frontend/src/content/docs/ja/whats-new/aspire-13.mdx @@ -1477,8 +1477,8 @@ aspire add javaScript Aspire 13.0 では、以下の API が削除されました: **パブリッシング関連のインフラストラクチャ** (`aspire do` に置き換え): -- `PublishingContext` および `PublishingCallbackAnnotation` -- `DeployingContext` および `DeployingCallbackAnnotation` +- `PublishingContext` およびレガシー publish コールバック注釈型 +- `DeployingContext` およびレガシー deploy コールバック注釈型 - `WithPublishingCallback` 拡張メソッド - `IDistributedApplicationPublisher` インターフェース - `IPublishingActivityReporter`, `IPublishingStep`, `IPublishingTask` インターフェース diff --git a/src/frontend/src/content/docs/whats-new/aspire-13.mdx b/src/frontend/src/content/docs/whats-new/aspire-13.mdx index be06e3c77..4961900eb 100644 --- a/src/frontend/src/content/docs/whats-new/aspire-13.mdx +++ b/src/frontend/src/content/docs/whats-new/aspire-13.mdx @@ -1477,8 +1477,8 @@ aspire add javaScript The following APIs have been removed in Aspire 13.0: **Publishing infrastructure** (replaced by `aspire do`): -- `PublishingContext` and `PublishingCallbackAnnotation` -- `DeployingContext` and `DeployingCallbackAnnotation` +- `PublishingContext` and the legacy publish callback annotation type +- `DeployingContext` and the legacy deploy callback annotation type - `WithPublishingCallback` extension method - `IDistributedApplicationPublisher` interface - `IPublishingActivityReporter`, `IPublishingStep`, `IPublishingTask` interfaces diff --git a/src/frontend/src/content/docs/whats-new/aspire-9-4.mdx b/src/frontend/src/content/docs/whats-new/aspire-9-4.mdx index 0599f394b..2e2947b58 100644 --- a/src/frontend/src/content/docs/whats-new/aspire-9-4.mdx +++ b/src/frontend/src/content/docs/whats-new/aspire-9-4.mdx @@ -117,13 +117,13 @@ aspire exec --start-resource my-worker -- npm run build #### `aspire deploy` -The `aspire deploy` command supports extensible deployment workflows through the new [`DeployingCallbackAnnotation`](https://aspire.dev/fundamentals/annotations-overview/), enabling custom pre/post-deploy logic and richer integration with external systems during deployment operations. +The `aspire deploy` command introduced extensible deployment workflows through the new [`DeployingCallbackAnnotation`](https://aspire.dev/fundamentals/annotations-overview/), enabling custom pre/post-deploy logic and richer integration with external systems during deployment operations. **Key capabilities:** - **Custom deployment hooks** using `Aspire.Hosting.ApplicationModel.DeployingCallbackAnnotation` to execute custom logic during the `aspire deploy` command -- **Workflow activity reporting** via the `Aspire.Hosting.Publishing.IPublishingActivityReporter` to support progress notifications and prompting in commmands -- **Integration with publish** - `aspire deploy` runs `Aspire.Hosting.Publishing.PublishingCallbackAnnotations` to support deploying artifacts emitted by publish steps, if applicable +- **Workflow activity reporting** via the `Aspire.Hosting.Publishing.IPublishingActivityReporter` to support progress notifications and prompting in commands +- **Integration with publish** - `aspire deploy` can run publish-related callbacks to support deploying artifacts emitted by publish steps, if applicable The example below demonstrates using the `DeployingCallbackAnnotation` to register custom deployment behavior and showcases [CLI-based prompting](#-enhanced-publish-and-deploy-output) and progress notifications. @@ -177,7 +177,6 @@ internal static class DataSeedJobBuilderExtensions }, cancellationToken: ct); - // Use the ActivityReporter to report progress on the seeding process var reporter = ctx.ActivityReporter; @@ -187,7 +186,7 @@ internal static class DataSeedJobBuilderExtensions try { // Do some work here - await Task.Delay(3000); + await Task.Delay(3000, ct); await task.SucceedAsync("Seed data loaded", ct); await step.SucceedAsync("Data seeding completed", ct); @@ -212,7 +211,9 @@ This custom deployment logic executes as follows from the `aspire deploy` comman Now, integration owners can create sophisticated `aspire deploy` workflows. This work also provides a foundation for advanced deployment automation scenarios.