-
Notifications
You must be signed in to change notification settings - Fork 230
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
Improvements to RunWithHttpsDevCert extensions #667
base: main
Are you sure you want to change the base?
Changes from 5 commits
772bf41
61e8e77
88d1363
21a3172
b31c3e0
f6f0c1b
898321e
f7d0f93
0e30ab5
bc59278
78eacd0
fa88fca
6f2bda3
c692ad4
c14cacb
25feb3c
e23cec6
cbd2813
fc132fe
29a126d
ef10a2c
90a1322
b53e2ec
b47d9c8
995a77e
bf36d18
e328cff
c35823c
33fa711
dfc257b
073f519
974bc2d
15376bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<Project> | ||
<Target Name="EmbedAppHostIntermediateOutputPath" BeforeTargets="GetAssemblyAttributes"> | ||
<ItemGroup> | ||
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> | ||
<_Parameter1>apphostprojectbaseintermediateoutputpath</_Parameter1> | ||
<_Parameter2>$(BaseIntermediateOutputPath)</_Parameter2> | ||
</AssemblyAttribute> | ||
</ItemGroup> | ||
</Target> | ||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
using System.Diagnostics; | ||
using System.Reflection; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Hosting; | ||
using Microsoft.Extensions.Logging; | ||
|
@@ -73,13 +74,10 @@ public static IResourceBuilder<TResource> RunWithHttpsDevCertificate<TResource>( | |
|
||
private static async Task<(bool, string CertFilePath, string CertKeyFilPath)> TryExportDevCertificateAsync(IDistributedApplicationBuilder builder, ILogger logger) | ||
{ | ||
// Exports the ASP.NET Core HTTPS development certificate & private key to PEM files using 'dotnet dev-certs https' to a temporary | ||
// directory and returns the path. | ||
// TODO: Check if we're running on a platform that already has the cert and key exported to a file (e.g. macOS) and just use those instead. | ||
var appNameHash = builder.Configuration["AppHost:Sha256"]![..10]; | ||
var tempDir = Path.Combine(Path.GetTempPath(), $"aspire.{appNameHash}"); | ||
var certExportPath = Path.Combine(tempDir, "dev-cert.pem"); | ||
var certKeyExportPath = Path.Combine(tempDir, "dev-cert.key"); | ||
// Exports the ASP.NET Core HTTPS development certificate & private key to PEM files using 'dotnet dev-certs https' to a directory and returns the path. | ||
var certDir = GetOrCreateAppHostCertDirectory(builder); | ||
var certExportPath = Path.Combine(certDir, "dev-cert.pem"); | ||
var certKeyExportPath = Path.Combine(certDir, "dev-cert.key"); | ||
|
||
if (File.Exists(certExportPath) && File.Exists(certKeyExportPath)) | ||
{ | ||
|
@@ -100,10 +98,10 @@ public static IResourceBuilder<TResource> RunWithHttpsDevCertificate<TResource>( | |
File.Delete(certKeyExportPath); | ||
} | ||
|
||
if (!Directory.Exists(tempDir)) | ||
if (!Directory.Exists(certDir)) | ||
{ | ||
logger.LogTrace("Creating directory to export dev cert to '{ExportDir}'", tempDir); | ||
Directory.CreateDirectory(tempDir); | ||
logger.LogTrace("Creating directory to export dev cert to '{ExportDir}'", certDir); | ||
Directory.CreateDirectory(certDir); | ||
} | ||
|
||
string[] args = ["dev-certs", "https", "--export-path", $"\"{certExportPath}\"", "--format", "Pem", "--no-password"]; | ||
|
@@ -182,4 +180,56 @@ static async Task ConsumeOutput(TextReader reader, Action<string> callback) | |
} | ||
} | ||
} | ||
|
||
private static string GetOrCreateAppHostCertDirectory(IDistributedApplicationBuilder builder) | ||
{ | ||
// TODO: Check if we're running on a platform that already has the cert and key exported to a file (e.g. macOS) and just use those instead. | ||
// macOS: Path.Combine( | ||
// Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".aspnet", "dev-certs", "https"), | ||
// "aspnetcore-localhost-{certificate.Thumbprint}.pfx"); | ||
// linux: CurrentUser/Root store | ||
|
||
// Create a directory in the project's /obj dir or TMP to store the exported certificate and key | ||
var assemblyMetadata = builder.AppHostAssembly?.GetCustomAttributes<AssemblyMetadataAttribute>(); | ||
var projectDir = GetMetadataValue(assemblyMetadata, "AppHostProjectPath"); | ||
var objDir = GetMetadataValue(assemblyMetadata, "AppHostProjectBaseIntermediateOutputPath"); | ||
Comment on lines
+283
to
+284
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I half wonder if we should just stamp the full path into the file during the build and then we just need to read the 1 value out and use it. |
||
var dirPath = projectDir is not null && objDir is not null | ||
DamianEdwards marked this conversation as resolved.
Show resolved
Hide resolved
|
||
? Path.Combine(projectDir, objDir, "aspire") | ||
: Directory.CreateTempSubdirectory(GetAppHostSpecificTempDirPrefix(builder)).FullName; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we even mess with the temp directory stuff if the OutputPath isn't found in the assembly? Can we just throw and make the code simpler? |
||
|
||
if (!Directory.Exists(dirPath)) | ||
DamianEdwards marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
// Create the directory | ||
Directory.CreateDirectory(dirPath); | ||
} | ||
|
||
return dirPath; | ||
} | ||
|
||
private static string? GetMetadataValue(IEnumerable<AssemblyMetadataAttribute>? assemblyMetadata, string key) => | ||
assemblyMetadata?.FirstOrDefault(a => string.Equals(a.Key, key, StringComparison.OrdinalIgnoreCase))?.Value; | ||
|
||
private static string GetAppHostSpecificTempDirPrefix(IDistributedApplicationBuilder builder) | ||
{ | ||
var appName = Sanitize(builder.Environment.ApplicationName).ToLowerInvariant(); | ||
var appNameHash = builder.Configuration["AppHost:Sha256"]![..10].ToLowerInvariant(); | ||
return $"aspire.{appName}.{appNameHash}"; | ||
} | ||
|
||
private static readonly char[] _invalidPathChars = Path.GetInvalidPathChars(); | ||
DamianEdwards marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
private static string Sanitize(string name) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. assuming this is not hot and you're already using Linq here how about making this a one liner return new string(name.Select(c => Path.GetInvalidPathChars().Contains(c) ? '_' : c).ToArray()); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It coulde be LINQ, but this is derived from the existing method we have in Aspire.Hosting for volume name sanitization, so I'm inclined to just keep it this way for now. |
||
{ | ||
return string.Create(name.Length, name, static (s, name) => | ||
{ | ||
var nameSpan = name.AsSpan(); | ||
DamianEdwards marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
for (var i = 0; i < nameSpan.Length; i++) | ||
{ | ||
var c = nameSpan[i]; | ||
|
||
s[i] = Array.IndexOf(_invalidPathChars, c) == -1 ? c : '_'; | ||
DamianEdwards marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
}); | ||
} | ||
} |
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.
It wouldn't hurt to stick a comment in here in case someone stumbles in and asks what this is for.