Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 26, 2025

Decentralize build/push/deploy pipeline logic to resources and deployment targets

This PR implements the decentralization of build/push/deploy pipeline logic from centralized compute environments to individual resources and deployment targets, following up on the design from PR #12340.

Pipeline Dependency Graph

The pipeline now follows this dependency structure:

Parameter Prompt
    ↓
Default Image Tags (per environment)
    ↓
Build Steps (per compute resource)
    ↓
Registry Provisioning (ACR/etc)
    ↓
Push Steps (per deployment target)
    ↓
Deployment Target Provisioning (per app)
    ↓
Environment Provisioning
    ↓
Print Dashboard URL
    ↓
Deploy (meta-step)

Key dependency patterns:

  • Build steps depend on default image tags
  • Push steps depend on: build steps + registry provisioning
  • Deployment target provision depends on: push steps
  • Dashboard URL printing depends on: environment provisioning
  • All deploy operations required by the "deploy" meta-step

Push step creation and noop optimization:

  • Push steps are only created when RequiresImageBuildAndPush() returns true on the target resource
  • This prevents unnecessary push operations for resources that don't need image building/pushing
  • When no push steps exist, provision steps clear their RequiredBySteps to become a noop, preventing provisioning without images

Changes Made

1. Build Steps Moved to Resources

ProjectResource:

  • Added PipelineStepAnnotation that creates a build-{resourceName} step
  • Build step uses IResourceContainerImageBuilder.BuildImagesAsync with ContainerTargetPlatform.LinuxAmd64
  • Tagged with WellKnownPipelineTags.BuildCompute

ContainerResource and WithDockerfile scenarios:

  • Build pipeline step annotation is added via EnsureBuildPipelineStepAnnotation helper when Dockerfile is configured
  • Helper uses replace semantics to ensure only one PipelineStepAnnotation exists
  • Applied automatically by WithDockerfile, WithDockerfileFactory, and WithDockerfileBuilder methods
  • Same build logic and tags as ProjectResource

2. Push/Deploy Steps Moved to Deployment Targets

AzureContainerAppResource and AzureAppServiceWebSiteResource:

  • Added PipelineStepAnnotation that creates push steps
  • Push steps only created when targetResource.RequiresImageBuildAndPush() returns true
  • Gets registry from TargetResource.GetDeploymentTargetAnnotation()?.ContainerRegistry
  • Push step tagged with WellKnownPipelineTags.PushContainerImage
  • Added PipelineConfigurationAnnotation to wire up dependencies:
    • Push steps depend on build steps of the target resource
    • Push steps depend on registry provisioning
    • Provision steps depend on push steps
    • When no push steps exist, provision steps clear their RequiredBySteps to become a noop

3. Compute Environments Expand Deployment Target Steps

AzureContainerAppEnvironmentResource and AzureAppServiceEnvironmentResource:

  • Added logic to enumerate compute resources and find their deployment targets
  • For each deployment target, expand its pipeline steps
  • Create default-image-tags-{name} step to tag images with deployment timestamp
  • Create print-dashboard-url-{envName} step with environment-specific dashboard URLs
  • Wire up dependencies: dashboard URL printing depends on environment provisioning

4. New Infrastructure

  • PipelineStepsExtensions: New file with extension methods for DependsOn/RequiredBy on step collections
  • WellKnownPipelineTags.PushContainerImage: New tag for push steps
  • AzureEnvironmentResourceHelpers: Helper methods for registry login and image push operations
  • PipelineStep.RequiredBySteps: Made public to enable deployment targets to clear dependencies when provision becomes a noop

5. Removed Code

Removed from AzureEnvironmentResource:

  • 500+ lines of centralized build/push/deploy logic
  • Pipeline dependencies now flow naturally through resource annotations

Build Verification

  • ✅ All modified projects build successfully without errors or warnings
Original prompt

Take the changes from #12340 and open a new pull request


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

…ines, and AzureEnvironmentResource

Co-authored-by: davidfowl <[email protected]>
/// <param name="entrypoint">An optional container entrypoint.</param>
public class ContainerResource(string name, string? entrypoint = null)
: Resource(name), IResourceWithEnvironment, IResourceWithArgs, IResourceWithEndpoints, IResourceWithWaitSupport, IResourceWithProbes,
public class ContainerResource : Resource, IResourceWithEnvironment, IResourceWithArgs, IResourceWithEndpoints, IResourceWithWaitSupport, IResourceWithProbes,
Copy link
Member

Choose a reason for hiding this comment

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

We can undo these changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reverted in commit 2cafa9f

/// <summary>
/// Extension methods for pipeline steps.
/// </summary>
public static class PipelineStepsExtensions
Copy link
Member

@davidfowl davidfowl Oct 26, 2025

Choose a reason for hiding this comment

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

Move this class to a new file

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved to new file PipelineStepsExtensions.cs in commit 2cafa9f

Tags = [WellKnownPipelineTags.BuildCompute]
};

return await Task.FromResult<IEnumerable<PipelineStep>>([buildStep]).ConfigureAwait(false);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
return await Task.FromResult<IEnumerable<PipelineStep>>([buildStep]).ConfigureAwait(false);
return [buildStep];

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in commit 2cafa9f

public ProjectResource(string name) : base(name)
{
// Add pipeline step annotation to create a build step for this project
Annotations.Add(new PipelineStepAnnotation(async (factoryContext) =>
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Annotations.Add(new PipelineStepAnnotation(async (factoryContext) =>
Annotations.Add(new PipelineStepAnnotation((factoryContext) =>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in commit 2cafa9f

Comment on lines +43 to +44
<InternalsVisibleTo Include="Aspire.Hosting.Azure.AppContainers" />
<InternalsVisibleTo Include="Aspire.Hosting.Azure.AppService" />
Copy link
Member

Choose a reason for hiding this comment

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

Avoid internals visible to for Aspire.Hosting.Azure.AppContainers and Aspire.Hosting.Azure.AppService and instead use that file as shared source across those 2 projects.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The shared source approach causes build errors because IProcessRunner is internal to Aspire.Hosting and inaccessible when the file is shared. Keeping InternalsVisibleTo is the correct approach here since AzureEnvironmentResourceHelpers needs access to internal types from Aspire.Hosting.

Copilot AI changed the title [WIP] Open a new pull request based on PR #12340 Decentralize build/push/deploy pipeline logic to resources and deployment targets Oct 26, 2025
Copilot finished work on behalf of davidfowl October 26, 2025 01:45
Copilot AI requested a review from davidfowl October 26, 2025 01:45
@davidfowl
Copy link
Member

@copilot React to the PR comments

- Revert ContainerResource to primary constructor syntax
- Move PipelineStepsExtensions to separate file
- Remove async wrapper from ProjectResource lambda
- Remove blank lines after constructors in environment resources
- Add RequiredBy("deploy") to provisionStep in AzureEnvironmentResource
- Remove unused DefaultImageTags method from AzureEnvironmentResource

Co-authored-by: davidfowl <[email protected]>
Copilot finished work on behalf of davidfowl October 26, 2025 01:59
@github-actions
Copy link
Contributor

github-actions bot commented Oct 26, 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 -- 12385

Or

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

Tags = [WellKnownPipelineTags.ProvisionInfrastructure]
};
provisionStep.DependsOn(createContextStep);
provisionStep.RequiredBy("deploy");
Copy link
Member

Choose a reason for hiding this comment

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

Use WellKnownPipelineSteps.Deploy

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in commit 3f659b7

Name = $"print-dashboard-url-{name}",
Action = ctx => PrintDashboardUrlAsync(ctx),
};
printDashboardUrlStep.RequiredBy("deploy");
Copy link
Member

Choose a reason for hiding this comment

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

Use WellKnownPipelineSteps.Deploy

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in commit 3f659b7

@davidfowl
Copy link
Member

@copilot Implement the review feedback and update the comment with the dependency graph

- Use WellKnownPipelineSteps.Deploy constant instead of string "deploy" in all environment resources
- Add dependency: printDashboardUrlStep now depends on provisioning of environment resources
- This ensures dashboard URL is printed only after the environment is fully provisioned

Co-authored-by: davidfowl <[email protected]>
Copy link
Contributor Author

Copilot AI commented Oct 26, 2025

@copilot Implement the review feedback and update the comment with the dependency graph

Implemented in commit 3f659b7. The PR description has been updated with a comprehensive dependency graph showing the pipeline flow from parameter prompt through build, push, provision, and deploy steps.

Copilot AI requested a review from davidfowl October 26, 2025 02:44
Copilot finished work on behalf of davidfowl October 26, 2025 02:44
@davidfowl
Copy link
Member

@copilot Only create the push step if RequiresImageBuildAndPush is true on the TargetResource in AzureAppServiceWebSiteResource and AzureContainerAppResource

- Add check for targetResource.RequiresImageBuildAndPush() before creating push steps
- Applied to both AzureContainerAppResource and AzureAppServiceWebSiteResource
- Prevents creating unnecessary push steps for resources that don't need image building/pushing

Co-authored-by: davidfowl <[email protected]>
Copy link
Contributor Author

Copilot AI commented Oct 26, 2025

@copilot Only create the push step if RequiresImageBuildAndPush is true on the TargetResource in AzureAppServiceWebSiteResource and AzureContainerAppResource

Implemented in commit 94e29c8. Both AzureContainerAppResource and AzureAppServiceWebSiteResource now check targetResource.RequiresImageBuildAndPush() before creating push steps, preventing unnecessary push operations for resources that don't need image building/pushing.

Copilot finished work on behalf of davidfowl October 26, 2025 03:19
@davidfowl
Copy link
Member

@copilot Actually we may not need to check RequiresImageBuildAndPush because we can handle it in compute environment resource as it is responsible for creating steps for the deployment target annotations

Copy link
Contributor Author

Copilot AI commented Oct 26, 2025

@copilot Actually we may not need to check RequiresImageBuildAndPush because we can handle it in compute environment resource as it is responsible for creating steps for the deployment target annotati...

The current approach has the deployment target decide whether to create push steps based on its target resource's needs. This keeps the logic encapsulated - each deployment target knows best whether it needs pushing. The environment resource just expands whatever steps the deployment target provides. This separation of concerns makes the code more maintainable. However, if you prefer to move this check to the environment level, I can refactor it.

Copilot finished work on behalf of davidfowl October 26, 2025 04:47
@davidfowl
Copy link
Member

@copilot We need to add code in the deployment target (AzureContainerAppResource and AzureAppServiceWebSiteResource) to clear provisionSteps RequiredBySteps. We don't want to provision compute resources without pushing. If there are no push steps, the provision step for the deployment target is a noop.

- Made RequiredBySteps public in PipelineStep to allow access from Azure projects
- Added logic in AzureContainerAppResource and AzureAppServiceWebSiteResource to clear RequiredBySteps on provision steps when no push steps exist
- This prevents provisioning compute resources without pushing images
- When there are no push steps, provision becomes a noop as intended

Co-authored-by: davidfowl <[email protected]>
Copy link
Contributor Author

Copilot AI commented Oct 26, 2025

@copilot We need to add code in the deployment target (AzureContainerAppResource and AzureAppServiceWebSiteResource) to clear provisionSteps RequiredBySteps. We don't want to provision compute resourc...

Implemented in commit f2d0438. Both deployment target resources now clear RequiredBySteps on their provision steps when no push steps exist. This makes provision a noop when there's no image to deploy. Also exposed RequiredBySteps as public API in PipelineStep to allow access from Azure projects.

Copilot finished work on behalf of davidfowl October 26, 2025 05:06
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.

2 participants