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

Review Feedback Implemented:

  1. Line 1114: Now uses dotnet msbuild -c Release -getProperty:PublishDir {projectPath} to get the PublishDir
  2. Line 728: Source resource build step now uses generic CreateBuildImageStep (not specific to ProjectResource)
  3. Line 1153: Refactored to use IContainerRuntime interface with new CopyContainerFilesAsync method

📝 Implementation Summary

Files Modified:

  1. src/Aspire.Hosting/Publishing/IContainerRuntime.cs

    • Added CopyContainerFilesAsync method signature
  2. src/Aspire.Hosting/Publishing/ContainerRuntimeBase.cs

    • Implemented CopyContainerFilesAsync with docker create/cp/rm logic
    • Works for both Docker and Podman runtimes
  3. src/Aspire.Hosting/ProjectResourceBuilderExtensions.cs

    • Added CreateBuildImageStep for generic resource image building
    • Added GetPublishDirectoryAsync to dynamically get publish directory using dotnet msbuild
    • Updated CopyContainerFilesToProjectAsync to use IContainerRuntime
    • Pipeline step factory now creates generic build steps for source resources
  4. tests/Aspire.Hosting.Tests/ProjectResourceTests.cs

    • Test verifying annotation application and pipeline step creation

🔧 Technical Details

IContainerRuntime.CopyContainerFilesAsync:

  • Creates temporary container from source image
  • Copies files using container runtime cp command
  • Cleans up temporary container even on failure
  • Supports both Docker and Podman

GetPublishDirectoryAsync:

  • Executes dotnet msbuild -c Release -getProperty:PublishDir {projectPath}
  • Captures output to determine actual publish directory
  • Handles relative and absolute paths
  • Comprehensive error handling and logging

Pipeline Steps:

  • Generic CreateBuildImageStep for any resource type
  • Project-specific CreateProjectBuildImageStep includes file copying
  • Proper dependency ordering ensures source images built first

🎯 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");

✅ Testing & Validation

  • Code compiles successfully without errors
  • Uses IContainerRuntime abstraction (supports Docker and Podman)
  • PublishDir determined dynamically from project configuration
  • Generic build steps for source resources
  • Pattern matches Python implementation
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 <8291187+eerhardt@users.noreply.github.com>
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

}

var projectPath = projectMetadata.ProjectPath;
var publishDir = Path.Combine(Path.GetDirectoryName(projectPath)!, "bin", "Release", "publish");
Copy link
Member

Choose a reason for hiding this comment

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

@copilot - use dotnet msbuild -c Release -getProperty:PublishDir {projectPath} to get the PublishDir.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated in e342d5d. Now uses dotnet msbuild -c Release -getProperty:PublishDir {projectPath} to dynamically determine the publish directory instead of hardcoding the path.

foreach (var containerFile in containerFilesAnnotations)
{
var source = containerFile.Source;
var staticFileBuildStep = CreateProjectBuildImageStep($"{factoryContext.Resource.Name}-{source.Name}-build-compute", source);
Copy link
Member

Choose a reason for hiding this comment

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

@copilot - this shouldn't be CreateProjectBuildImageStep because we don't know what kind of resource it is. So instead, the step to build the source should look like

private static PipelineStep CreateBuildImageBuildStep(string stepName, IResource resource) =>
new()
{
Name = stepName,
Action = async ctx =>
{
var containerImageBuilder = ctx.Services.GetRequiredService<IResourceContainerImageBuilder>();
await containerImageBuilder.BuildImageAsync(
resource,
new ContainerBuildOptions
{
TargetPlatform = ContainerTargetPlatform.LinuxAmd64
},
ctx.CancellationToken).ConfigureAwait(false);
},
Tags = [WellKnownPipelineTags.BuildCompute]
};

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 e342d5d. Created generic CreateBuildImageStep method that works for any resource type, following the pattern in PythonAppResourceBuilderExtensions.cs line 574-590.

var containerName = $"temp-{resource.Name}-{Guid.NewGuid():N}";

logger.LogDebug("Creating temporary container {ContainerName} from image {ImageName}", containerName, imageName);
var createSpec = new ProcessSpec("docker")
Copy link
Member

Choose a reason for hiding this comment

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

Don't call into docker directly. Instead refactor all this code to be a new method on the IContainerRuntime interface called "CopyContainerFiles". With implementations for both DockerContainerRuntime and PodmanContainerRuntime.

Then get an IContainerRuntime instance like the following code:

private IContainerRuntime? _containerRuntime;
private IContainerRuntime ContainerRuntime => _containerRuntime ??= dcpOptions.Value.ContainerRuntime switch
{
string rt => serviceProvider.GetRequiredKeyedService<IContainerRuntime>(rt),
null => serviceProvider.GetRequiredKeyedService<IContainerRuntime>("docker")
};

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Refactored in e342d5d. Added CopyContainerFilesAsync method to IContainerRuntime interface with implementation in ContainerRuntimeBase. The method handles create/cp/rm operations and works for both Docker and Podman. Updated code to get IContainerRuntime instance from DcpOptions as shown in ResourceContainerImageBuilder.cs.

…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 <8291187+eerhardt@users.noreply.github.com>
Copilot AI requested a review from eerhardt October 26, 2025 03:17
Copilot finished work on behalf of eerhardt October 26, 2025 03:17
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