-
Notifications
You must be signed in to change notification settings - Fork 695
Add primitive Files resource to Aspire.Hosting #11276
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
Copilot
wants to merge
9
commits into
main
Choose a base branch
from
copilot/fix-8bee7e2e-6c2a-4f10-94fc-9979652a19e9
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 6 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
6aa8ed3
Initial plan
Copilot 0c46892
Implement Files resource functionality with tests
Copilot 8d99ff3
Update Files resource API per feedback - remove file list parameter a…
Copilot 3618ccb
Tweaks.
mitchdenny 5dcaf5c
Wire up Files resource WithSource to OnResourceInitialize event
Copilot 546dadb
Replace custom event with annotation-based callback for Files resource
Copilot 0c03075
Introduce ResourceFile type for IAsyncEnumerable in Files resource
Copilot e608cd3
Change FilesCallbackAnnotation to return Task instead of IAsyncEnumer…
Copilot 7d7b8df
Revert FilesCallbackAnnotation to use IAsyncEnumerable instead of Task
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>$(DefaultTargetFramework)</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<IsAspireHost>true</IsAspireHost> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<Compile Include="..\..\KnownResourceNames.cs" Link="KnownResourceNames.cs" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<AspireProjectOrPackageReference Include="Aspire.Hosting.AppHost" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
var builder = DistributedApplication.CreateBuilder(args); | ||
|
||
// Add a files resource with a source directory containing static content | ||
builder.AddFiles("static-content") | ||
.WithSource("../content"); | ||
|
||
#if !SKIP_DASHBOARD_REFERENCE | ||
// This project is only added in playground projects to support development/debugging | ||
// of the dashboard. It is not required in end developer code. Comment out this code | ||
// or build with `/p:SkipDashboardReference=true`, to test end developer | ||
// dashboard launch experience, Refer to Directory.Build.props for the path to | ||
// the dashboard binary (defaults to the Aspire.Dashboard bin output in the | ||
// artifacts dir). | ||
builder.AddProject<Projects.Aspire_Dashboard>(KnownResourceNames.AspireDashboard); | ||
#endif | ||
|
||
builder.Build().Run(); |
29 changes: 29 additions & 0 deletions
29
playground/files/Files.AppHost/Properties/launchSettings.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
{ | ||
"$schema": "http://json.schemastore.org/launchsettings.json", | ||
"profiles": { | ||
"https": { | ||
"commandName": "Project", | ||
"dotnetRunMessages": true, | ||
"launchBrowser": true, | ||
"applicationUrl": "https://localhost:17222;http://localhost:15222", | ||
"environmentVariables": { | ||
"ASPNETCORE_ENVIRONMENT": "Development", | ||
"DOTNET_ENVIRONMENT": "Development", | ||
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21222", | ||
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22222" | ||
} | ||
}, | ||
"http": { | ||
"commandName": "Project", | ||
"dotnetRunMessages": true, | ||
"launchBrowser": true, | ||
"applicationUrl": "http://localhost:15222", | ||
"environmentVariables": { | ||
"ASPNETCORE_ENVIRONMENT": "Development", | ||
"DOTNET_ENVIRONMENT": "Development", | ||
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19222", | ||
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20222" | ||
} | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>Files Resource Demo</title> | ||
<style> | ||
body { | ||
font-family: Arial, sans-serif; | ||
max-width: 800px; | ||
margin: 0 auto; | ||
padding: 20px; | ||
background-color: #f5f5f5; | ||
} | ||
.container { | ||
background-color: white; | ||
padding: 30px; | ||
border-radius: 8px; | ||
box-shadow: 0 2px 4px rgba(0,0,0,0.1); | ||
} | ||
h1 { | ||
color: #333; | ||
border-bottom: 3px solid #007acc; | ||
padding-bottom: 10px; | ||
} | ||
.highlight { | ||
background-color: #e7f3ff; | ||
padding: 15px; | ||
border-radius: 5px; | ||
margin: 20px 0; | ||
} | ||
code { | ||
background-color: #f4f4f4; | ||
padding: 2px 6px; | ||
border-radius: 3px; | ||
font-family: 'Courier New', monospace; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<div class="container"> | ||
<h1>🗂️ Aspire Files Resource Demo</h1> | ||
|
||
<p>This is a simple HTML file that demonstrates the new <strong>Files resource</strong> in Aspire.Hosting.</p> | ||
|
||
<div class="highlight"> | ||
<h3>📁 What is the Files Resource?</h3> | ||
<p>The Files resource is a lightweight primitive for representing and managing collections of files in Aspire applications. It's useful for:</p> | ||
<ul> | ||
<li>Configuration files</li> | ||
<li>Static content and assets</li> | ||
<li>Templates</li> | ||
<li>Documentation</li> | ||
<li>Any file-based resources your application needs</li> | ||
</ul> | ||
</div> | ||
|
||
<div class="highlight"> | ||
<h3>🛠️ How to Use</h3> | ||
<p>In your AppHost, you can add files resources like this:</p> | ||
<code>builder.AddFiles("static-content").WithSource("content");</code> | ||
</div> | ||
|
||
<p><em>This file is located in the <code>content/</code> directory and is referenced by the Files resource in the playground application.</em></p> | ||
</div> | ||
</body> | ||
</html> |
28 changes: 28 additions & 0 deletions
28
src/Aspire.Hosting/ApplicationModel/FilesCallbackAnnotation.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Diagnostics; | ||
|
||
namespace Aspire.Hosting.ApplicationModel; | ||
|
||
/// <summary> | ||
/// Represents an annotation that provides a callback to enumerate files asynchronously. | ||
/// </summary> | ||
[DebuggerDisplay("Type = {GetType().Name,nq}")] | ||
public sealed class FilesCallbackAnnotation : IResourceAnnotation | ||
{ | ||
/// <summary> | ||
/// Initializes a new instance of the <see cref="FilesCallbackAnnotation"/> class. | ||
/// </summary> | ||
/// <param name="callback">A callback that returns an async enumerable of file paths.</param> | ||
public FilesCallbackAnnotation(Func<CancellationToken, IAsyncEnumerable<string>> callback) | ||
{ | ||
ArgumentNullException.ThrowIfNull(callback); | ||
Callback = callback; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the callback that returns an async enumerable of file paths. | ||
/// </summary> | ||
public Func<CancellationToken, IAsyncEnumerable<string>> Callback { get; } | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
namespace Aspire.Hosting.ApplicationModel; | ||
|
||
/// <summary> | ||
/// Represents a files resource that can be used by an application. | ||
/// </summary> | ||
/// <param name="name">The name of the resource.</param> | ||
/// <param name="files">The collection of file paths associated with this resource.</param> | ||
public class FilesResource(string name, IEnumerable<string> files) : Resource(name), IResourceWithFiles, IResourceWithoutLifetime | ||
{ | ||
private readonly List<string> _files = files?.ToList() ?? []; | ||
|
||
/// <summary> | ||
/// Gets the collection of file paths associated with this resource. | ||
/// </summary> | ||
public IEnumerable<string> Files => _files; | ||
|
||
/// <summary> | ||
/// Adds a file path to the resource. | ||
/// </summary> | ||
/// <param name="filePath">The file path to add.</param> | ||
public void AddFile(string filePath) | ||
{ | ||
ArgumentNullException.ThrowIfNull(filePath); | ||
_files.Add(filePath); | ||
} | ||
|
||
/// <summary> | ||
/// Adds multiple file paths to the resource. | ||
/// </summary> | ||
/// <param name="filePaths">The file paths to add.</param> | ||
public void AddFiles(IEnumerable<string> filePaths) | ||
{ | ||
ArgumentNullException.ThrowIfNull(filePaths); | ||
_files.AddRange(filePaths); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
namespace Aspire.Hosting.ApplicationModel; | ||
|
||
/// <summary> | ||
/// Represents a resource that can work with files. | ||
/// </summary> | ||
public interface IResourceWithFiles | ||
{ | ||
/// <summary> | ||
/// Gets the collection of file paths associated with this resource. | ||
/// </summary> | ||
IEnumerable<string> Files { get; } | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using Aspire.Hosting.ApplicationModel; | ||
using System.Runtime.CompilerServices; | ||
|
||
namespace Aspire.Hosting; | ||
|
||
/// <summary> | ||
/// Provides extension methods for adding files resources to an application. | ||
/// </summary> | ||
public static class FilesResourceBuilderExtensions | ||
{ | ||
/// <summary> | ||
/// Adds a files resource to the application. | ||
/// </summary> | ||
/// <param name="builder">The distributed application builder.</param> | ||
/// <param name="name">The name of the files resource.</param> | ||
/// <returns>A resource builder for the files resource.</returns> | ||
public static IResourceBuilder<FilesResource> AddFiles(this IDistributedApplicationBuilder builder, [ResourceName] string name) | ||
{ | ||
ArgumentNullException.ThrowIfNull(builder); | ||
ArgumentNullException.ThrowIfNull(name); | ||
|
||
var filesResource = new FilesResource(name, []); | ||
return builder.AddResource(filesResource); | ||
} | ||
|
||
/// <summary> | ||
/// Adds a source directory or file to an existing files resource. | ||
/// </summary> | ||
/// <param name="builder">The resource builder for the files resource.</param> | ||
/// <param name="source">The source path (directory or file) to associate with this resource.</param> | ||
/// <returns>The resource builder for the files resource.</returns> | ||
public static IResourceBuilder<FilesResource> WithSource(this IResourceBuilder<FilesResource> builder, string source) | ||
{ | ||
ArgumentNullException.ThrowIfNull(builder); | ||
ArgumentNullException.ThrowIfNull(source); | ||
|
||
// Add the source immediately to maintain existing behavior | ||
builder.Resource.AddFile(source); | ||
|
||
// Add callback annotation that will enumerate files from the source | ||
builder.WithAnnotation(new FilesCallbackAnnotation(cancellationToken => EnumerateFilesAsync(source, cancellationToken))); | ||
|
||
return builder; | ||
} | ||
|
||
private static async IAsyncEnumerable<string> EnumerateFilesAsync(string source, [EnumeratorCancellation] CancellationToken cancellationToken = default) | ||
{ | ||
// If the source is a directory, enumerate all files in it | ||
if (Directory.Exists(source)) | ||
{ | ||
yield return source; | ||
|
||
var files = Directory.GetFiles(source, "*", SearchOption.AllDirectories); | ||
foreach (var file in files) | ||
{ | ||
cancellationToken.ThrowIfCancellationRequested(); | ||
yield return file; | ||
} | ||
} | ||
else if (File.Exists(source)) | ||
{ | ||
// If it's a file, just return it | ||
yield return source; | ||
} | ||
|
||
await Task.CompletedTask.ConfigureAwait(false); // Satisfy async enumerable requirements | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot introduce a new type called ResourceFile which is used instead of the string as the generic type for IAsyncEnumerable.
It should have two properties. FullPath and RelativePath.
The relative path should be the normalised path relative to the root of the entire set of files.
For example if AddFiles specifies a path it would be the path relative to that starting point. The full path would be the full path on the local system.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've introduced the
ResourceFile
type and updated theIAsyncEnumerable
to use it in commit 0c03075. TheResourceFile
class has bothFullPath
andRelativePath
properties. For directories, the relative path calculation is based on the source directory as the root, and file paths are normalized using forward slashes for consistency.