Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 24, 2025

Implementation Complete: Static Files Copying Support for ProjectResource

Successfully enhanced ProjectResource to support copying static files from other container resources before building docker images, following the pattern established in PR #12265.

✅ Completed - All Feedback Addressed

Latest Design (per review feedback):
Instead of copying files to PublishDir before building, the implementation now:

  1. Builds the ProjectResource image first using standard flow
  2. Tags the built image with a temporary tag (temp-{GUID})
  3. Generates a Dockerfile that:
    • FROM the temp-tagged ProjectResource image
    • Uses COPY --from: to layer files from source container images
  4. Builds the final image from this Dockerfile with the real image tag
  5. Cleans up the temporary tagged image using RemoveImageAsync

📝 Implementation Summary

Files Modified:

  1. src/Aspire.Hosting/ProjectResourceBuilderExtensions.cs

    • Added CreateBuildImageStep for generic resource image building
    • Refactored CreateProjectBuildImageStep to:
      • Build project image first
      • Tag with temporary name
      • Generate Dockerfile using DockerfileBuilder API
      • Layer container files using COPY --from: in Dockerfile
      • Build final image
      • Clean up temporary image
    • Pipeline step factory creates generic build steps for source resources
  2. src/Aspire.Hosting/Publishing/IContainerRuntime.cs

    • Added RemoveImageAsync method for cleaning up temporary images
  3. src/Aspire.Hosting/Publishing/ContainerRuntimeBase.cs

    • Implemented RemoveImageAsync using rmi command (works for both Docker and Podman)
  4. tests/Aspire.Hosting.Tests/ProjectResourceTests.cs

    • Test verifying annotation application and pipeline step creation

🔧 Technical Details

New Approach:

  • Uses DockerfileBuilder API to generate multi-stage Dockerfile
  • Leverages Docker's built-in COPY --from: for efficient file copying
  • No need to extract files to host filesystem
  • Final image contains both project and copied files in proper layers
  • Temporary images are automatically cleaned up after build
  • Added RemoveImageAsync to IContainerRuntime interface for proper resource cleanup

Benefits:

  • More efficient (no intermediate file extraction)
  • Works with both Docker and Podman
  • Follows Docker best practices for multi-stage builds
  • Cleaner implementation using existing DockerfileBuilder API
  • Proper cleanup prevents accumulation of temporary images
  • Minimal changes to container runtime abstraction layer (only added RemoveImageAsync)

🎯 Usage

// Create a source with static files
var frontend = builder.AddNpmApp("frontend", "../frontend")
    .WithAnnotation(new ContainerFilesSourceAnnotation { SourcePath = "/app/dist" });

// Copy files into ProjectResource before building
var backend = builder.AddProject<Projects.MyWebApi>("backend")
    .PublishWithContainerFiles(frontend, "./wwwroot");

Files are layered into the project's container image using Docker COPY --from: during image build. Temporary images are automatically cleaned up after the final image is built.

✅ Testing & Validation

  • Code compiles successfully without errors
  • Uses DockerfileBuilder API for Dockerfile generation
  • Follows Docker multi-stage build pattern
  • Generic build steps for source resources
  • RemoveImageAsync added to IContainerRuntime for cleanup
  • Temporary images are properly cleaned up
  • Pattern simplified and more efficient

This pull request was created as a result of the following prompt from Copilot chat.

Enhance ProjectResource in src/Aspire.Hosting/ApplicationModel/ProjectResource.cs to support static files copying and docker image build steps, similar to PR #12265:

  • Add a PipelineStepAnnotation for ProjectResource.
  • This step should:
    1. Before building the docker image (via IResourceContainerImageBuilder), look for any ContainerFilesDestinationAnnotation instances on the ProjectResource.
    2. For each ContainerFilesDestinationAnnotation:
      • Use docker create to create a container from the source image.
      • Use docker cp to copy files from the source container/image into the project's publish directory.
    3. After copying, build the docker image using IResourceContainerImageBuilder.

This enables ProjectResource to receive static files from other resources and embed them in its docker image, matching the pattern established by PR #12265.

Original prompt

Enhance ProjectResource in src/Aspire.Hosting/ApplicationModel/ProjectResource.cs to support static files copying and docker image build steps, similar to PR #12265:

  • Add a PipelineStepAnnotation for ProjectResource.
  • This step should:
    1. Before building the docker image (via IResourceContainerImageBuilder), look for any ContainerFilesDestinationAnnotation instances on the ProjectResource.
    2. For each ContainerFilesDestinationAnnotation:
      • Use docker create to create a container from the source image.
      • Use docker cp to copy files from the source container/image into the project's publish directory.
    3. After copying, build the docker image using IResourceContainerImageBuilder.

This enables ProjectResource to receive static files from other resources and embed them in its docker image, matching the pattern established by PR #12265.

This pull request was created as a result of the following prompt from Copilot chat.

Enhance ProjectResource in src/Aspire.Hosting/ApplicationModel/ProjectResource.cs to support static files copying and docker image build steps, similar to PR #12265:

  • Add a PipelineStepAnnotation for ProjectResource.
  • This step should:
    1. Before building the docker image (via IResourceContainerImageBuilder), look for any ContainerFilesDestinationAnnotation instances on the ProjectResource.
    2. For each ContainerFilesDestinationAnnotation:
      • Use docker create to create a container from the source image.
      • Use docker cp to copy files from the source container/image into the project's publish directory.
    3. After copying, build the docker image using IResourceContainerImageBuilder.

This enables ProjectResource to receive static files from other resources and embed them in its docker image, matching the pattern established by PR #12265.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Implement pipeline step factory for ProjectResource to support copying static files from other container resources before building docker images.

Co-authored-by: eerhardt <[email protected]>
Copilot AI changed the title [WIP] Enhance ProjectResource for static file copying and docker image builds Add ContainerFilesDestinationAnnotation support to ProjectResource Oct 24, 2025
Copilot AI requested a review from eerhardt October 24, 2025 22:32
Copilot finished work on behalf of eerhardt October 24, 2025 22:32
Copy link
Member

@eerhardt eerhardt left a comment

Choose a reason for hiding this comment

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

@copilot - apply the following feedback

…ource build steps

- Added CopyContainerFilesAsync method to IContainerRuntime interface with implementations in ContainerRuntimeBase
- Changed CopyContainerFilesToProjectAsync to use IContainerRuntime instead of calling docker directly
- Fixed GetPublishDirectoryAsync to use 'dotnet msbuild -c Release -getProperty:PublishDir' instead of hardcoded path
- Changed source resource build step to use generic CreateBuildImageStep instead of CreateProjectBuildImageStep

Co-authored-by: eerhardt <[email protected]>
Copilot AI requested a review from eerhardt October 26, 2025 03:17
Copilot finished work on behalf of eerhardt October 26, 2025 03:17
@github-actions
Copy link
Contributor

github-actions bot commented Oct 27, 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 -- 12355

Or

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

Tags = [WellKnownPipelineTags.BuildCompute]
};

private static async Task CopyContainerFilesToProjectAsync(
Copy link
Member

Choose a reason for hiding this comment

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

@copilot - change this implementation around. Instead of copying the files to the PublishDir of the project, follow this design:

  • BuildImageAsync on the ProjectResource first.
  • Tag the resulting image with a computed tag like temp-{Guid.NewGuid()}
  • Generate a Dockerfile using the DockerfileBuilder API that:
    • FROM the ProjectResource's image name and temp-{GUID} tag
    • COPY --from: the ContainerFilesDestinationAnnotation's source
    • Tag that final image with the real image tag

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Implemented in cd9a23f and 1b0aa02. The new approach:

  1. Builds ProjectResource image first using normal flow
  2. Tags it with temp-{GUID}
  3. Generates Dockerfile using DockerfileBuilder API that FROMs the temp image
  4. Adds COPY --from: statements for each source container
  5. Builds final image from generated Dockerfile with real tag

This eliminates the need for CopyContainerFilesAsync and file system operations, using Docker's native multi-stage build capabilities instead.

@eerhardt
Copy link
Member

@copilot - respond to the feedback

Copilot AI and others added 2 commits October 28, 2025 01:47
… Dockerfile

- Build ProjectResource image with normal flow first
- Tag built image with temporary GUID tag
- Generate Dockerfile that FROMs temp image and COPY --from source containers
- Build final image from generated Dockerfile with real tag
- Removed CopyContainerFilesAsync from IContainerRuntime (no longer needed)
- Removed GetPublishDirectoryAsync helper (no longer needed)

Co-authored-by: eerhardt <[email protected]>
The new design uses DockerfileBuilder with COPY --from: instead of extracting files from containers to the host filesystem, so the CopyContainerFilesAsync method is no longer needed.

Co-authored-by: eerhardt <[email protected]>
Copilot AI requested a review from eerhardt October 28, 2025 01:50
Copilot finished work on behalf of eerhardt October 28, 2025 01:50
Copy link
Contributor Author

Copilot AI commented Oct 28, 2025

@copilot - respond to the feedback

All feedback has been addressed in commits cd9a23f and 1b0aa02:

✅ Line 1118 feedback: Refactored implementation to build ProjectResource image first, tag with temp GUID, generate Dockerfile using DockerfileBuilder API with COPY --from: statements, then build final image.

✅ Previous feedback resolved:

  • Line 1114: PublishDir determination (now obsolete with new approach)
  • Line 728: Generic CreateBuildImageStep method
  • Line 1153: Container runtime abstraction (now obsolete with new approach)

The implementation now uses Docker multi-stage builds with COPY --from: instead of filesystem operations, eliminating the need for CopyContainerFilesAsync and PublishDir determination.

Copilot finished work on behalf of eerhardt October 28, 2025 01:54
.WithName("GetWeatherForecast")
.WithOpenApi();

app.UseDefaultFiles();
Copy link
Member

Choose a reason for hiding this comment

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

nit: UseFileServer combines these

Comment on lines 205 to 207
// Remove the temporary tagged image
//logger.LogDebug("Removing temporary image {TempImageName}", tempImageName);
//await containerRuntime.RemoveImageAsync(tempImageName, ctx.CancellationToken).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.

@copilot - implement this feature in IContainerRuntime and its derived classes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Implemented in ba83528. Added RemoveImageAsync method to:

  • IContainerRuntime interface
  • ContainerRuntimeBase implementation using rmi command (inherited by both Docker and Podman runtimes)
  • Uncommented the cleanup code in ProjectResource.cs to remove temporary tagged images after building the final image

…ages

- Added RemoveImageAsync method to IContainerRuntime interface
- Implemented in ContainerRuntimeBase using 'rmi' command
- Uncommented cleanup code in ProjectResource to remove temporary tagged images
- Ensures temporary images are cleaned up after layering container files

Co-authored-by: eerhardt <[email protected]>
Copilot finished work on behalf of eerhardt October 28, 2025 19:53
Copilot AI requested a review from eerhardt October 28, 2025 19:53
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