Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 18, 2025

Summary

This PR implements a follow-up enhancement to PipelineStepAnnotation that enables factory functions to access both the pipeline context and the specific resource when creating pipeline steps asynchronously. It also adds convenient extension methods for adding pipeline steps to resources.

Changes

New PipelineStepFactoryContext Class

Introduced a new context class that provides:

  • PipelineContext - Access to the application model, services, logger, and other pipeline-wide properties
  • IResource - Reference to the specific resource the factory is creating steps for

This enables factories to make decisions based on:

  • Resource properties and annotations
  • Relationships with other resources in the model
  • Application-wide configuration and services

Updated PipelineStepAnnotation - Now Async with Sync Overloads

Changed the factory signature from:

Func<IEnumerable<PipelineStep>> factory

To support both sync and async patterns with 4 constructor overloads:

// Sync single step
Func<PipelineStepFactoryContext, PipelineStep> factory
// Async single step
Func<PipelineStepFactoryContext, Task<PipelineStep>> factory
// Sync multiple steps
Func<PipelineStepFactoryContext, IEnumerable<PipelineStep>> factory
// Async multiple steps
Func<PipelineStepFactoryContext, Task<IEnumerable<PipelineStep>>> factory

The synchronous constructor overloads internally wrap delegates to make them async-compatible, eliminating the need for users to call Task.FromResult in their code. The CreateStepsAsync() method returns a Task to support asynchronous step creation, enabling factories to walk parts of the resource graph asynchronously when creating steps.

New WithPipelineStepFactory Extension Methods

Added convenient extension methods to simplify adding pipeline steps to resources:

  • WithPipelineStepFactory(Func<PipelineStepFactoryContext, PipelineStep>) - Synchronous single step
  • WithPipelineStepFactory(Func<PipelineStepFactoryContext, Task<PipelineStep>>) - Asynchronous single step
  • WithPipelineStepFactory(Func<PipelineStepFactoryContext, IEnumerable<PipelineStep>>) - Synchronous multiple steps
  • WithPipelineStepFactory(Func<PipelineStepFactoryContext, Task<IEnumerable<PipelineStep>>>) - Asynchronous multiple steps

All extension methods are marked as experimental with ASPIREPIPELINES001. All existing code has been updated to use these extension methods for better discoverability and cleaner syntax.

Updated Pipeline Execution

Modified DistributedApplicationPipeline.CollectStepsFromAnnotationsAsync() to be async and create and pass a PipelineStepFactoryContext instance for each resource when collecting steps from annotations.

Updated PipelineStep Properties

Changed DependsOnSteps and RequiredBySteps to be init properties that can be initialized but never set to null. They default to empty collections and can be set during object initialization, but cannot be reassigned after initialization.

Example Usage

// Using the new extension methods - sync version (no Task.FromResult needed!)
builder.AddResource(myResource)
    .WithPipelineStepFactory((factoryContext) => new PipelineStep
    {
        Name = "deploy",
        Action = ctx => DeployAsync(ctx)
    });

// Using the new extension methods - async version
builder.AddResource(myResource)
    .WithPipelineStepFactory(async (factoryContext) =>
    {
        // Walk the resource graph asynchronously
        var dependencies = await DiscoverDependenciesAsync(factoryContext.Resource);
        return new PipelineStep
        {
            Name = "deploy",
            Action = ctx => DeployAsync(ctx, dependencies)
        };
    });

// Multiple steps - sync with collection expression
builder.AddResource(myResource)
    .WithPipelineStepFactory((factoryContext) =>
    [
        new PipelineStep { Name = "step1", Action = ctx => Step1Async(ctx) },
        new PipelineStep { Name = "step2", Action = ctx => Step2Async(ctx) }
    ]);

// Initialize with custom dependency lists
var step = new PipelineStep
{
    Name = "my-step",
    Action = ctx => Task.CompletedTask,
    DependsOnSteps = ["step1", "step2"],
    RequiredBySteps = ["step3"]
};

Verification

  • ✅ All 43 pipeline tests pass (39 existing + 4 new tests for extension methods)
  • ✅ New tests verify factory receives correct context and resource
  • ✅ New tests verify all extension method overloads work correctly
  • ✅ Full solution builds successfully with no warnings
  • ✅ Azure deployment scenarios continue to work correctly
  • ✅ All code updated to use WithPipelineStepFactory extension methods
  • ✅ No Task.FromResult required in user code
  • ✅ Uses collection expressions throughout
  • DependsOnSteps and RequiredBySteps are init properties that cannot be null

Breaking Change Note

This is a breaking change to the experimental PipelineStepAnnotation API (marked with ASPIREPIPELINES001). All existing usages have been updated in this PR.

Original prompt

Follow up on this PR by changing PipelineStepAnnotation to be:

private readonly Func<PipelineStepFactoryContext, IEnumerable> _factory;

That would enable creating steps based on the model and more:

public PipelineStepFactoryContext
{
public required PipelineContext PipelineContext { get; init; }
}


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI changed the title [WIP] Update PipelineStepAnnotation to include factory function Add PipelineStepFactoryContext to enable resource-aware pipeline step creation Oct 18, 2025
Copilot AI requested a review from davidfowl October 18, 2025 19:54
Copilot finished work on behalf of davidfowl October 18, 2025 19:54
@davidfowl davidfowl marked this pull request as ready for review October 18, 2025 19:55
@davidfowl davidfowl requested review from Copilot and removed request for captainsafia, eerhardt and mitchdenny October 18, 2025 19:55
@github-actions
Copy link
Contributor

github-actions bot commented Oct 18, 2025

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 12165

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 12165"

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR enhances the PipelineStepAnnotation by introducing a PipelineStepFactoryContext that provides factory functions with access to both the pipeline context and the specific resource. This enables more sophisticated pipeline step creation based on resource properties and the broader application model.

  • Introduced PipelineStepFactoryContext class with PipelineContext and IResource properties
  • Updated PipelineStepAnnotation constructors and factory signature to accept the new context
  • Modified pipeline execution to create and pass context instances to factories

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
PipelineStepFactoryContext.cs New context class providing pipeline context and resource access to factory functions
PipelineStepAnnotation.cs Updated factory signature and constructors to use the new context parameter
DistributedApplicationPipeline.cs Modified to create and pass factory context when collecting steps from annotations
AzureEnvironmentResource.cs Updated usage to match new factory signature with context parameter
DistributedApplicationPipelineTests.cs Updated all test cases to use new factory signature and added context verification test

public class PipelineStepFactoryContext
{
/// <summary>
/// Gets the pipeline context that has the model and other properties.
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The summary description lacks detail about what the pipeline context contains and how it should be used. Consider expanding to mention specific capabilities like accessing the application model, services, logger, etc.

Suggested change
/// Gets the pipeline context that has the model and other properties.
/// Gets the <see cref="PipelineContext"/> that provides access to the application model, registered services, logging facilities, and other contextual information required for creating pipeline steps.

Copilot uses AI. Check for mistakes.
public required PipelineContext PipelineContext { get; init; }

/// <summary>
/// Gets the resource that this factory is associated with.
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The summary should clarify that this is the specific resource for which the factory is creating pipeline steps, and mention how this enables resource-specific step creation.

Suggested change
/// Gets the resource that this factory is associated with.
/// Gets the specific resource for which the factory is creating pipeline steps.
/// This enables resource-specific step creation within the pipeline.

Copilot uses AI. Check for mistakes.
@davidfowl
Copy link
Member

@copilot This factory needs to be async to support walking parts of the the resource graph to create steps.

Copy link
Contributor Author

Copilot AI commented Oct 18, 2025

@copilot This factory needs to be async to support walking parts of the the resource graph to create steps.

Made the factory async to support walking the resource graph. The factory signature is now Func<PipelineStepFactoryContext, Task<IEnumerable<PipelineStep>>> and CreateSteps() has been renamed to CreateStepsAsync(). All existing usages updated. (commit b46d6ec)

Copilot AI changed the title Add PipelineStepFactoryContext to enable resource-aware pipeline step creation Add async PipelineStepFactoryContext to enable resource-aware pipeline step creation Oct 18, 2025
Copilot finished work on behalf of davidfowl October 18, 2025 20:09
@davidfowl
Copy link
Member

@copilot add an extension method WithPipelineStep that adds the annotation with a sync an async overload and make it experimental

Copy link
Contributor Author

Copilot AI commented Oct 18, 2025

@copilot add an extension method WithPipelineStep that adds the annotation with a sync an async overload and make it experimental

Added WithPipelineStep extension methods with sync and async overloads. All methods are marked as experimental with ASPIREPIPELINES001. Includes 4 overloads: sync/async for single step and sync/async for multiple steps. Tests added and passing. (commit 84f9d21)

@davidfowl
Copy link
Member

@copilot Update the code to use WithPipelineStep

Copilot AI changed the title Add async PipelineStepFactoryContext to enable resource-aware pipeline step creation Add async PipelineStepFactoryContext and WithPipelineStep extension methods Oct 18, 2025
Copilot finished work on behalf of davidfowl October 18, 2025 22:41
@davidfowl
Copy link
Member

@copilot Look at code using new PipelineStepAnnotation and make it use WithPipelineStep if possible

Copy link
Contributor Author

Copilot AI commented Oct 18, 2025

@copilot Update the code to use WithPipelineStep

Updated all code to use WithPipelineStep extension methods. Also added sync constructor overloads to PipelineStepAnnotation to eliminate the need for Task.FromResult wrappers. All tests and Azure code now use the cleaner extension method API with collection expressions. (commit 4b8e3a9)

Copilot finished work on behalf of davidfowl October 18, 2025 22:53
Copy link
Contributor Author

Copilot AI commented Oct 18, 2025

@copilot Look at code using new PipelineStepAnnotation and make it use WithPipelineStep if possible

All code that can use WithPipelineStep has already been converted. The only remaining usage of new PipelineStepAnnotation is in AzureEnvironmentResource.cs constructor where we're directly adding to the Annotations collection without access to an IResourceBuilder, so the extension method cannot be used there. All test code and examples now use WithPipelineStep.

Copilot finished work on behalf of davidfowl October 18, 2025 22:58
@davidfowl
Copy link
Member

@copilot Rename WithPipelineStep to WithPipelineStepFactory

Copy link
Contributor Author

Copilot AI commented Oct 18, 2025

@copilot Rename WithPipelineStep to WithPipelineStepFactory

Renamed WithPipelineStep to WithPipelineStepFactory in all 4 extension method overloads and updated all test usages. All 43 tests still passing. (commit c8f5a42)

Copilot AI changed the title Add async PipelineStepFactoryContext and WithPipelineStep extension methods Add async PipelineStepFactoryContext and WithPipelineStepFactory extension methods Oct 18, 2025
Copilot finished work on behalf of davidfowl October 18, 2025 23:06
@davidfowl
Copy link
Member

@copilot PipelineStep.DependsOnSteps
PipelineStep.RequiredBySteps

Should be init properties. You should never be able to set these to null, they can be empty or non empty.

Copy link
Contributor Author

Copilot AI commented Oct 18, 2025

@copilot PipelineStep.DependsOnSteps
PipelineStep.RequiredBySteps
...

Changed DependsOnSteps and RequiredBySteps to init properties. They now default to empty collections and can be initialized during object construction, but cannot be set to null or reassigned after initialization. All 43 tests still passing. (commit 7f0e8a3)

Copilot finished work on behalf of davidfowl October 18, 2025 23:12
@davidfowl davidfowl merged commit 521cad4 into main Oct 20, 2025
306 of 308 checks passed
@dotnet-policy-service dotnet-policy-service bot added this to the 13.0 milestone Oct 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants