Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 10, 2025

Description

For Docker Compose scenarios, bind mount source paths need to stay relative to the compose file rather than being resolved to absolute paths. This adds RelativeSource and BasePath properties to ContainerMountAnnotation that store the original relative path and app host directory, allowing consumers to use the relative path for scenarios like Docker Compose while maintaining backward compatibility with the existing Source property.

Changes

  • ContainerMountAnnotation: New constructor overload with relativeSource and basePath parameters; new RelativeSource property to store the original relative path and BasePath property to store the base path (app host directory). The existing Source property remains unchanged as the resolved absolute path for backward compatibility.
  • WithBindMount: Now sets Source to the resolved absolute path, RelativeSource to the original relative path, and BasePath to the app host directory for relative paths. For absolute paths, RelativeSource and BasePath are null.
  • DockerComposeEnvironmentContext: Updated to use RelativeSource when available, falling back to Source if RelativeSource is null. This keeps paths relative to the compose file for Docker Compose scenarios.

Usage

// When using WithBindMount with a relative path:
builder.AddContainer("mycontainer", "myimage")
    .WithBindMount("./certs", "/app/certs");

// The annotation will have:
// - Source: "/full/path/to/apphost/certs" (resolved absolute path)
// - RelativeSource: "./certs" (original relative path)
// - BasePath: "/full/path/to/apphost" (app host directory)

// When using an absolute path:
builder.AddContainer("mycontainer", "myimage")
    .WithBindMount("/absolute/path", "/app/certs");

// The annotation will have:
// - Source: "/absolute/path" (unchanged)
// - RelativeSource: null
// - BasePath: null

Existing consumers that use Source will continue to work without changes. Docker Compose uses RelativeSource when available to keep paths relative to the compose file, falling back to Source for absolute paths.

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No
  • Does the change require an update in our Aspire docs?
Original prompt

To make sure that bind mounts in docker compose scenarios can stay relative to the compose file, we should have an option on WithBindMount that does not resolve the source, but passes it through directly as is


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

- Add new `ContainerMountAnnotation` constructor with `isSourceRelative` parameter
- Add `IsSourceRelative` property to track when paths are intentionally relative
- Add new `WithBindMount` overload that accepts `Action<BindMountOptions>`
- Create `BindMountOptions` class with `ResolveSourcePath` and `IsReadOnly` properties
- Add tests for new functionality

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
Copilot AI changed the title [WIP] Add option for bind mounts to stay relative in docker-compose Add WithBindMount option to keep source path unresolved Dec 10, 2025
Copilot AI requested a review from davidfowl December 10, 2025 02:11
@github-actions
Copy link
Contributor

github-actions bot commented Dec 10, 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 -- 13443

Or

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

@davidfowl davidfowl marked this pull request as ready for review December 10, 2025 02:28
Copilot AI review requested due to automatic review settings December 10, 2025 02:28
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 adds a new WithBindMount overload with an options callback that allows control over source path resolution behavior. This is specifically designed for Docker Compose scenarios where bind mount paths should remain relative to the compose file rather than being resolved to absolute paths.

  • Introduces BindMountOptions class with ResolveSourcePath (default: true) and IsReadOnly properties
  • Adds IsSourceRelative property to ContainerMountAnnotation to track whether paths are intentionally relative
  • Provides new WithBindMount overload accepting Action<BindMountOptions> for configuration

Reviewed changes

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

Show a summary per file
File Description
src/Aspire.Hosting/ApplicationModel/BindMountOptions.cs New options class for configuring bind mount behavior with path resolution and read-only settings
src/Aspire.Hosting/ApplicationModel/ContainerMountAnnotation.cs Added constructor overload with isSourceRelative parameter and corresponding property to track unresolved paths
src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs New WithBindMount overload with options callback that conditionally resolves paths based on settings
tests/Aspire.Hosting.Containers.Tests/ContainerMountAnnotationTests.cs Tests for new constructor overload validating relative path handling and parameter combinations
tests/Aspire.Hosting.Containers.Tests/ContainerResourceTests.cs Integration tests verifying the new WithBindMount overload behavior with various path resolution scenarios

/// paths should remain relative to the compose file location rather than being resolved to absolute paths.
/// </para>
/// </remarks>
public bool ResolveSourcePath { get; set; } = true;
Copy link
Member

Choose a reason for hiding this comment

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

Can we add a work item to make the default false in the next major Aspire release? That feels like it'd be the less surprising behavior in most cases, even though it'd be a breaking change.

@davidfowl
Copy link
Member

@copilot Instead of bool isSourceRelative on ContainerMountAnnotation, we should store the BasePath to the source as a property on ContainerMountAnnotation. This way the source field is always relative to the base path. Consumers can choose to use both the base path and the source DcpExecutor.BuildContainerMounts, while others will ignore the base path (like docker compose assumes relative to the compose file).

Per reviewer feedback, instead of bool isSourceRelative, the
ContainerMountAnnotation now stores a BasePath property. The Source
field is always stored as-is, and consumers can use BasePath + Source
to resolve the full path, or ignore BasePath for Docker Compose
scenarios where paths should remain relative to the compose file.

Changes:
- ContainerMountAnnotation: Added BasePath property, new constructor
- ContainerResourceBuilderExtensions: Store source and basePath separately
- DcpExecutor.BuildContainerMounts: Resolve path using BasePath
- ManifestPublishingContext: Resolve path using BasePath
- Updated tests to use BasePath property

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 10, 2025

@copilot Instead of bool isSourceRelative on ContainerMountAnnotation, we should store the BasePath to the source as a property on ContainerMountAnnotation. This way the source field is always relativ...

Implemented in 3b9d6d4. Changed the design to store BasePath instead of bool isSourceRelative:

  • Source is now always stored as-is (relative or absolute)
  • BasePath stores the app host directory when resolution is needed
  • DcpExecutor.BuildContainerMounts and ManifestPublishingContext now combine BasePath + Source to resolve full paths
  • Docker Compose can ignore BasePath since paths are relative to the compose file

@davidfowl
Copy link
Member

@copilot We no longer need BindMountOptions

Replace Action<BindMountOptions> overload with simpler parameters:
WithBindMount(source, target, isReadOnly, resolveSourcePath)

This simplifies the API while maintaining the same functionality.
When resolveSourcePath is false, no BasePath is stored, so Docker
Compose can use relative paths directly.

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 10, 2025

@copilot We no longer need BindMountOptions

Removed in 02456a7. Simplified the API to WithBindMount(source, target, isReadOnly, resolveSourcePath) instead of using Action<BindMountOptions>.

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
@davidfowl
Copy link
Member

@copilot Bring back Action and use put
IsReadOnly
BasePath
on it

Per feedback, WithBindMount now always sets BasePath for relative paths.
No new overload is needed - consumers can use BasePath to resolve paths
or ignore it (like Docker Compose which uses paths relative to compose file).

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 10, 2025

@copilot Bring back Action and use put
IsReadOnly
BasePath ...

Implemented in cdd4c72. Simplified the approach - WithBindMount now always sets BasePath for relative paths. Removed the extra overload. Consumers can use BasePath to resolve paths or ignore it (like Docker Compose).

Copilot AI changed the title Add WithBindMount option to keep source path unresolved Add BasePath to ContainerMountAnnotation for bind mount source resolution Dec 10, 2025
@davidfowl davidfowl added the breaking-change Issue or PR that represents a breaking API or functional change over a prerelease. label Dec 10, 2025
@davidfowl
Copy link
Member

@copilot To make this less breaking, leave Source alone and introduce a nwe RelativeSource and BasePath.

Per feedback, this makes the change less breaking:
- Source remains the resolved absolute path (backward compatible)
- RelativeSource stores the original relative path as specified by user
- BasePath stores the base path (app host directory) for the relative source

Consumers can use RelativeSource and BasePath for Docker Compose scenarios
where paths should remain relative to the compose file.

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 10, 2025

@copilot To make this less breaking, leave Source alone and introduce a nwe RelativeSource and BasePath.

Implemented in 565f1ab. Now Source remains the resolved absolute path (backward compatible), with new RelativeSource and BasePath properties for Docker Compose scenarios.

@davidfowl davidfowl removed the breaking-change Issue or PR that represents a breaking API or functional change over a prerelease. label Dec 10, 2025
Copilot AI changed the title Add BasePath to ContainerMountAnnotation for bind mount source resolution Add RelativeSource and BasePath to ContainerMountAnnotation for bind mount source resolution Dec 10, 2025
@davidfowl
Copy link
Member

@copilot Update docker compose to use the RelativeSource and fallback to Source

Docker Compose now uses RelativeSource (the original relative path) when
available, falling back to Source (resolved absolute path) if RelativeSource
is null. This keeps paths relative to the compose file for Docker Compose
scenarios.

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
Copilot AI requested a review from captainsafia as a code owner December 10, 2025 09:22
Copy link
Contributor Author

Copilot AI commented Dec 10, 2025

@copilot Update docker compose to use the RelativeSource and fallback to Source

Implemented in e50808f. Docker Compose now uses RelativeSource when available, falling back to Source if RelativeSource is null.

/// <param name="isReadOnly">A value indicating whether the mount is read-only.</param>
/// <param name="relativeSource">The original relative source path before resolution. This is the path as specified by the user.</param>
/// <param name="basePath">The base path that <paramref name="relativeSource"/> is relative to. Typically the app host directory.</param>
public ContainerMountAnnotation(string? source, string target, ContainerMountType type, bool isReadOnly, string? relativeSource, string? basePath)
Copy link
Member

Choose a reason for hiding this comment

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

(nit) should the new properties just be get; init; and then we don't need a whole new constructor copying code?

Copy link
Member

Choose a reason for hiding this comment

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

Yep


/// <summary>
/// Gets the base path that <see cref="RelativeSource"/> is relative to. Typically the app host directory.
/// Consumers can use <see cref="RelativeSource"/> with <see cref="BasePath"/> for scenarios like Docker Compose
Copy link
Member

Choose a reason for hiding this comment

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

Consumers can use with for scenarios like Docker Compose

This feels like it should go in a remarks instead of a summary.

var name = mount.Source;
// For Docker Compose, use RelativeSource if available (keeps paths relative to compose file),
// otherwise fallback to Source (resolved absolute path)
var sourceForCompose = mount.RelativeSource ?? mount.Source;
Copy link
Member

Choose a reason for hiding this comment

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

Why is this "forCompose"?

It might be simpler if we just have:

var source = mount.RelativeSource ?? mount.Source;
var name = source;

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.

:shipit:

else
{
// Relative path - resolve to absolute and store relative source with base path
basePath = builder.ApplicationBuilder.AppHostDirectory;
Copy link
Member

Choose a reason for hiding this comment

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

Feels like tracking the run time specific base path isn't necessary; at run time this would be implicit and at deploy time it wouldn't be used.

Copy link
Member

Choose a reason for hiding this comment

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

We need it to calculate the absolute path here, but we don't really need to track it in the annotation.

Copy link
Member

Choose a reason for hiding this comment

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

I see, so we just need a way to flow the AppHostDirectory to other places in the code.

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.

4 participants