Skip to content

Move ContainerBuildOptions to resource-specific annotation#13081

Merged
captainsafia merged 5 commits intomainfrom
safia/container-img-options
Dec 1, 2025
Merged

Move ContainerBuildOptions to resource-specific annotation#13081
captainsafia merged 5 commits intomainfrom
safia/container-img-options

Conversation

@captainsafia
Copy link
Copy Markdown
Contributor

This pull request introduces a new extensibility point for configuring container build options in Aspire Hosting by adding the ContainerBuildOptionsCallbackAnnotation and its associated context. This enables more flexible and dynamic configuration of container image names, tags, and platforms for both project and container resources. The changes also update resource builders and pipeline steps to use this new mechanism, ensuring consistency and easier customization.

Extensibility for container build options:

  • Added ContainerBuildOptionsCallbackAnnotation and ContainerBuildOptionsCallbackContext in ContainerBuildOptionsCallbackAnnotation.cs, providing a callback-based mechanism for configuring container build options on resources.
  • Implemented ProcessContainerBuildOptionsCallbackAsync extension method in ResourceExtensions.cs to process all relevant annotations and accumulate container build options.

Integration with resource builders:

  • Updated WithDockerfile and WithDockerfileFactory methods in ContainerResourceBuilderExtensions.cs to automatically add default container build options annotations based on Dockerfile metadata, and introduced new WithContainerBuildOptions overloads for custom configuration. [1] [2]
  • Modified ProjectResource and its build pipeline to add a default container build options annotation that dynamically resolves image name and tag from MSBuild project properties, and updated build logic to use the new callback context. [1] [2] [3] [4]

Compatibility and suppression updates:

  • Added compatibility suppressions for new and updated container image builder methods in CompatibilitySuppressions.xml to maintain baseline compatibility. [1] [2]

Copilot AI review requested due to automatic review settings November 21, 2025 00:36
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Nov 21, 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 -- 13081

Or

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

Copy link
Copy Markdown
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 introduces a new extensibility point for configuring container build options in Aspire Hosting by migrating from a parameter-based approach to a resource-specific annotation-based callback mechanism. This enables more flexible, dynamic, and resource-specific configuration of container images.

Key Changes

  • New Annotation Architecture: Introduced ContainerBuildOptionsCallbackAnnotation and ContainerBuildOptionsCallbackContext to provide a callback-based mechanism for configuring container build options on resources
  • API Simplification: Removed ContainerBuildOptions parameter from IResourceContainerImageBuilder.BuildImageAsync and BuildImagesAsync methods, making options resource-specific rather than call-specific
  • Comprehensive Test Coverage: Added 242 new test lines covering multiple annotation behavior scenarios, async callbacks, and default annotation verification

Reviewed Changes

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

Show a summary per file
File Description
src/Aspire.Hosting/ApplicationModel/ContainerBuildOptionsCallbackAnnotation.cs New file defining the callback annotation and context for configuring container build options with async support
src/Aspire.Hosting/ApplicationModel/ResourceExtensions.cs Added ProcessContainerBuildOptionsCallbackAsync extension method to process all container build options annotations on a resource
src/Aspire.Hosting/ApplicationModel/ProjectResource.cs Added default annotation that dynamically resolves image name and tag from MSBuild project properties (ContainerRepository, ContainerImageTag)
src/Aspire.Hosting/Publishing/ResourceContainerImageBuilder.cs Refactored to resolve options from resource annotations instead of parameters, introduced ResolvedContainerBuildOptions internal class
src/Aspire.Hosting/ProjectResourceBuilderExtensions.cs Added WithContainerBuildOptions extension methods (sync and async overloads) for project resources
src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs Updated WithDockerfile and WithDockerfileFactory to add default build options annotations, added WithContainerBuildOptions extension methods for container resources
tests/Aspire.Hosting.Tests/Publishing/ResourceContainerImageBuilderTests.cs Updated all tests to use new annotation-based API, added comprehensive tests for annotation behavior including ordering, overrides, and async callbacks
tests/Aspire.Hosting.Tests/MockImageBuilder.cs Updated mock to match new interface signature without options parameter
tests/Aspire.Hosting.Docker.Tests/DockerComposeTests.cs Updated mock implementation to match new interface
tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs Updated mock implementation to match new interface
src/Aspire.Hosting/CompatibilitySuppressions.xml Added suppressions for API breaking changes (CP0002 and CP0006 diagnostics)

Comment on lines +22 to +26
/// <summary>
/// Initializes a new instance of <see cref="ContainerBuildOptionsCallbackAnnotation"/> with a synchronous callback.
/// </summary>
/// <param name="callback">The synchronous callback action to configure container build options.</param>
public ContainerBuildOptionsCallbackAnnotation(Action<ContainerBuildOptionsCallbackContext> callback)
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

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

The XML documentation for this constructor should include a <remarks> tag explaining that the synchronous callback is converted to an async callback internally. According to the XML documentation guidelines, constructors with specific behavior should document those details.

Example:

/// <summary>
/// Initializes a new instance of <see cref="ContainerBuildOptionsCallbackAnnotation"/> with a synchronous callback.
/// </summary>
/// <param name="callback">The synchronous callback action to configure container build options.</param>
/// <remarks>
/// The synchronous callback is automatically wrapped in a <see cref="Task"/> for compatibility with the async callback infrastructure.
/// </remarks>

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +575 to +580
.WithAnnotation(defaultContainerBuildOptions)
.EnsureBuildPipelineStepAnnotation();
}

return builder.WithAnnotation(annotation, ResourceAnnotationMutationBehavior.Replace)
.WithAnnotation(defaultContainerBuildOptions)
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

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

[nitpick] Inconsistent mutation behavior specification for ContainerBuildOptionsCallbackAnnotation. In WithDockerfileFactory (lines 713, 718), the mutation behavior is explicitly set to ResourceAnnotationMutationBehavior.Append, but here it's omitted (which defaults to Append). For consistency and clarity, the mutation behavior should be explicitly specified in both places.

Suggested fix:

.WithAnnotation(defaultContainerBuildOptions, ResourceAnnotationMutationBehavior.Append)
Suggested change
.WithAnnotation(defaultContainerBuildOptions)
.EnsureBuildPipelineStepAnnotation();
}
return builder.WithAnnotation(annotation, ResourceAnnotationMutationBehavior.Replace)
.WithAnnotation(defaultContainerBuildOptions)
.WithAnnotation(defaultContainerBuildOptions, ResourceAnnotationMutationBehavior.Append)
.EnsureBuildPipelineStepAnnotation();
}
return builder.WithAnnotation(annotation, ResourceAnnotationMutationBehavior.Replace)
.WithAnnotation(defaultContainerBuildOptions, ResourceAnnotationMutationBehavior.Append)

Copilot uses AI. Check for mistakes.
@captainsafia
Copy link
Copy Markdown
Contributor Author

So, two things that are not great about this API:

  • Because WithContainerBuildOptions targets IComputeResource, you can include it on resources that are container-images that don't build, which means it effectively no-ops. I haven't gone through the exercise of reworking this.
  • Now that you can set build options-per-resource, you need to make sure you are holding it right in certain cases. For example, if you are using the PublishWithContainerFiles API you'll want to make sure the TargetPlatform on the build image and the image that hosts the static assets match.

@captainsafia captainsafia force-pushed the safia/container-img-options branch from 6fbd3b9 to e21b271 Compare November 25, 2025 00:03
/// <summary>
/// Gets the resource being built.
/// </summary>
public IResource Resource { get; }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can I make a "build options" across all resources? For example if I want to build all images to a .tar file and not to the local registry?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, you'll have to apply the annotation to all the resources with the defaults you want. I can see this being something that would be done as part of the BuildPrereq step.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would it be better if we had a "global options" and each resource could override the global options?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Would it be better if we had a "global options" and each resource could override the global options?

This sort of exists at the moment because we populate each resource with the annotation in its constructor (see here).

We could also consider codifying this behavior in some sort of global IOptions instance.

@davidfowl
Copy link
Copy Markdown
Contributor

Where is the usage example?

@captainsafia
Copy link
Copy Markdown
Contributor Author

Where is the usage example?

The annotation is currently optional but you can override it with something like:

builder.AddDockerfile("go-app", "./goapp")
    .WithHttpEndpoint(port: 8080, targetPort: 8080)
    .WithAnnotation(new ContainerRegistryReferenceAnnotation(registry.Resource))
    .WithContainerBuildOptions(ctx =>
    {
        ctx.LocalImageName = $"{Environment.UserName}-go-app";
    })

@captainsafia captainsafia merged commit 513baea into main Dec 1, 2025
295 checks passed
@captainsafia captainsafia deleted the safia/container-img-options branch December 1, 2025 21:33
@dotnet-policy-service dotnet-policy-service bot added this to the 13.1 milestone Dec 1, 2025
@github-actions github-actions bot locked and limited conversation to collaborators Jan 1, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants