Skip to content
Merged
10 changes: 8 additions & 2 deletions src/Aspire.Hosting.Azure/AzurePublishingContext.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only

using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Pipelines;
using Microsoft.Extensions.DependencyInjection;
using Azure.Provisioning;
using Azure.Provisioning.Expressions;
using Azure.Provisioning.Primitives;
Expand Down Expand Up @@ -118,6 +121,9 @@ private async Task WriteAzureArtifactsOutputAsync(IReportingStep step, Distribut
outputDirectory.Create();
}

var fileSystemService = ServiceProvider.GetRequiredService<IFileSystemService>();
var tempDirectory = fileSystemService.TempDirectory.CreateTempSubdirectory("aspire-bicep").Path;

var bicepResourcesToPublish = model.Resources.OfType<AzureBicepResource>()
.Where(r => !r.IsExcludedFromPublish())
.ToList();
Expand Down Expand Up @@ -145,7 +151,7 @@ private async Task WriteAzureArtifactsOutputAsync(IReportingStep step, Distribut

foreach (var resource in bicepResourcesToPublish)
{
var file = resource.GetBicepTemplateFile();
var file = resource.GetBicepTemplateFile(tempDirectory);

var moduleDirectory = outputDirectory.CreateSubdirectory(resource.Name);

Expand Down Expand Up @@ -336,7 +342,7 @@ void CaptureBicepOutputsFromParameters(IResourceWithParameters resource)

var modulePath = Path.Combine(moduleDirectory.FullName, $"{resource.Name}.bicep");

var file = br.GetBicepTemplateFile();
var file = br.GetBicepTemplateFile(tempDirectory);

File.Copy(file.Path, modulePath, true);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only

using System.Collections.Immutable;
using System.Diagnostics;
using System.Text.Json.Nodes;
Expand All @@ -23,6 +26,7 @@ internal sealed class BicepProvisioner(
ISecretClientProvider secretClientProvider,
IDeploymentStateManager deploymentStateManager,
DistributedApplicationExecutionContext executionContext,
IFileSystemService fileSystemService,
ILogger<BicepProvisioner> logger) : IBicepProvisioner
{
/// <inheritdoc />
Expand Down Expand Up @@ -135,7 +139,8 @@ await notificationService.PublishUpdateAsync(resource, state => state with
])
}).ConfigureAwait(false);

var template = resource.GetBicepTemplateFile();
var tempDirectory = fileSystemService.TempDirectory.CreateTempSubdirectory("aspire-bicep").Path;
var template = resource.GetBicepTemplateFile(tempDirectory);
var path = template.Path;

// GetBicepTemplateFile may have added new well-known parameters, so we need
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only

using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Eventing;
using Aspire.Hosting.Lifecycle;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Aspire.Hosting.Maui.Utilities;
Expand Down Expand Up @@ -38,7 +41,8 @@ internal sealed class MauiAndroidEnvironmentProcessedAnnotation : IResourceAnnot
internal sealed class MauiAndroidEnvironmentSubscriber(
DistributedApplicationExecutionContext executionContext,
ResourceLoggerService loggerService,
ResourceNotificationService notificationService) : IDistributedApplicationEventingSubscriber
ResourceNotificationService notificationService,
IFileSystemService fileSystemService) : IDistributedApplicationEventingSubscriber
{
public Task SubscribeAsync(IDistributedApplicationEventing eventing, DistributedApplicationExecutionContext execContext, CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -83,6 +87,7 @@ private async Task OnBeforeResourceStartedAsync(BeforeResourceStartedEvent @even
if (generatedFilePath is null)
{
generatedFilePath = await MauiEnvironmentHelper.CreateAndroidEnvironmentTargetsFileAsync(
fileSystemService,
resource,
executionContext,
logger,
Expand Down
12 changes: 8 additions & 4 deletions src/Aspire.Hosting.Maui/Utilities/MauiEnvironmentHelper.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only

using System.Globalization;
using System.Text;
using System.Xml.Linq;
Expand All @@ -22,12 +24,14 @@ internal static class MauiEnvironmentHelper
/// <summary>
/// Creates an MSBuild targets file for Android that sets environment variables.
/// </summary>
/// <param name="fileSystemService">The file system service for managing temp files.</param>
/// <param name="resource">The resource to collect environment variables from.</param>
/// <param name="executionContext">The execution context.</param>
/// <param name="logger">Logger for diagnostic output.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The path to the generated targets file, or null if no environment variables are present.</returns>
public static async Task<string?> CreateAndroidEnvironmentTargetsFileAsync(
IFileSystemService fileSystemService,
IResource resource,
DistributedApplicationExecutionContext executionContext,
ILogger logger,
Expand Down Expand Up @@ -62,8 +66,7 @@ internal static class MauiEnvironmentHelper
}

// Create a temporary targets file
var tempDirectory = Path.Combine(Path.GetTempPath(), "aspire", "maui", "android-env");
Directory.CreateDirectory(tempDirectory);
var tempDirectory = fileSystemService.TempDirectory.CreateTempSubdirectory("aspire-maui-android-env").Path;

// Prune old targets files
PruneOldTargets(tempDirectory, logger);
Expand Down Expand Up @@ -209,12 +212,14 @@ private static string EncodeSemicolons(string value, out bool wasEncoded)
/// <summary>
/// Creates an MSBuild targets file for iOS that sets environment variables.
/// </summary>
/// <param name="fileSystemService">The file system service for managing temp files.</param>
/// <param name="resource">The resource to collect environment variables from.</param>
/// <param name="executionContext">The execution context.</param>
/// <param name="logger">Logger for diagnostic output.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The path to the generated targets file, or null if no environment variables are present.</returns>
public static async Task<string?> CreateiOSEnvironmentTargetsFileAsync(
IFileSystemService fileSystemService,
IResource resource,
DistributedApplicationExecutionContext executionContext,
ILogger logger,
Expand All @@ -232,8 +237,7 @@ private static string EncodeSemicolons(string value, out bool wasEncoded)
}

// Create a temporary targets file
var tempDirectory = Path.Combine(Path.GetTempPath(), "aspire", "maui", "mlaunch-env");
Directory.CreateDirectory(tempDirectory);
var tempDirectory = fileSystemService.TempDirectory.CreateTempSubdirectory("aspire-maui-mlaunch-env").Path;

// Prune old targets files
PruneOldTargetsiOS(tempDirectory, logger);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only

using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Eventing;
using Aspire.Hosting.Lifecycle;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Aspire.Hosting.Maui.Utilities;
Expand Down Expand Up @@ -38,7 +41,8 @@ internal sealed class MauiiOSEnvironmentProcessedAnnotation : IResourceAnnotatio
internal sealed class MauiiOSEnvironmentSubscriber(
DistributedApplicationExecutionContext executionContext,
ResourceLoggerService loggerService,
ResourceNotificationService notificationService) : IDistributedApplicationEventingSubscriber
ResourceNotificationService notificationService,
IFileSystemService fileSystemService) : IDistributedApplicationEventingSubscriber
{
public Task SubscribeAsync(IDistributedApplicationEventing eventing, DistributedApplicationExecutionContext execContext, CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -83,6 +87,7 @@ private async Task OnBeforeResourceStartedAsync(BeforeResourceStartedEvent @even
if (generatedFilePath is null)
{
generatedFilePath = await MauiEnvironmentHelper.CreateiOSEnvironmentTargetsFileAsync(
fileSystemService,
resource,
executionContext,
logger,
Expand Down
9 changes: 6 additions & 3 deletions src/Aspire.Hosting.MySql/MySqlBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only

using Aspire.Hosting;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.MySql;
Expand Down Expand Up @@ -260,7 +262,8 @@ public static IResourceBuilder<T> WithPhpMyAdmin<T>(this IResourceBuilder<T> bui
}
else
{
var tempConfigFile = await WritePhpMyAdminConfiguration(mySqlInstances, ct).ConfigureAwait(false);
var fileSystemService = e.Services.GetRequiredService<IFileSystemService>();
var tempConfigFile = await WritePhpMyAdminConfiguration(fileSystemService, mySqlInstances, ct).ConfigureAwait(false);

try
{
Expand Down Expand Up @@ -374,10 +377,10 @@ public static IResourceBuilder<MySqlServerResource> WithInitFiles(this IResource
return builder.WithContainerFiles(initPath, importFullPath);
}

private static async Task<string> WritePhpMyAdminConfiguration(IEnumerable<MySqlServerResource> mySqlInstances, CancellationToken cancellationToken)
private static async Task<string> WritePhpMyAdminConfiguration(IFileSystemService fileSystemService, IEnumerable<MySqlServerResource> mySqlInstances, CancellationToken cancellationToken)
{
// This temporary file is not used by the container, it will be copied and then deleted
var filePath = Path.GetTempFileName();
var filePath = fileSystemService.TempDirectory.CreateTempFile().Path;

using var writer = new StreamWriter(filePath);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
// The .NET Foundation licenses this file to you under the MIT license.

#pragma warning disable ASPIREPIPELINES001
#pragma warning disable ASPIREUSERSECRETS001
#pragma warning disable ASPIREFILESYSTEM001

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Eventing;
using Aspire.Hosting.Pipelines;
using Aspire.Hosting.UserSecrets;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
Expand Down Expand Up @@ -244,6 +247,8 @@ private sealed class Builder(SuspendingDistributedApplicationFactory factory, Di

public IDistributedApplicationPipeline Pipeline => innerBuilder.Pipeline;

public IUserSecretsManager UserSecretsManager => innerBuilder.UserSecretsManager;

public IResourceBuilder<T> AddResource<T>(T resource) where T : IResource => innerBuilder.AddResource(resource);

public DistributedApplication Build() => BuildAsync(CancellationToken.None).Result;
Expand Down Expand Up @@ -396,6 +401,8 @@ static Assembly FindApplicationAssembly()

public IDistributedApplicationPipeline Pipeline => _innerBuilder.Pipeline;

public IUserSecretsManager UserSecretsManager => _innerBuilder.UserSecretsManager;

public IResourceBuilder<T> AddResource<T>(T resource) where T : IResource => _innerBuilder.AddResource(resource);

[MemberNotNull(nameof(_app))]
Expand Down Expand Up @@ -486,6 +493,12 @@ public interface IDistributedApplicationTestingBuilder : IDistributedApplication
/// <inheritdoc cref="IDistributedApplicationBuilder.Resources" />
new IResourceCollection Resources => ((IDistributedApplicationBuilder)this).Resources;

/// <inheritdoc cref="IDistributedApplicationBuilder.FileSystemService" />
new IFileSystemService FileSystemService => ((IDistributedApplicationBuilder)this).FileSystemService;

/// <inheritdoc cref="IDistributedApplicationBuilder.UserSecretsManager" />
new IUserSecretsManager UserSecretsManager => ((IDistributedApplicationBuilder)this).UserSecretsManager;

/// <inheritdoc cref="IDistributedApplicationBuilder.AddResource{T}(T)" />
new IResourceBuilder<T> AddResource<T>(T resource) where T : IResource => ((IDistributedApplicationBuilder)this).AddResource(resource);

Expand Down
10 changes: 8 additions & 2 deletions src/Aspire.Hosting/ApplicationModel/AspireStore.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only

using System.IO.Hashing;
using Aspire.Hosting.Utils;

Expand All @@ -11,22 +13,26 @@ internal sealed class AspireStore : IAspireStore
internal const string AspireStorePathKeyName = "Aspire:Store:Path";

private readonly string _basePath;
private readonly IFileSystemService _directoryService;

/// <summary>
/// Initializes a new instance of the <see cref="AspireStore"/> class with the specified base path.
/// </summary>
/// <param name="basePath">The base path for the store.</param>
/// <param name="directoryService">The directory service for creating temp directories.</param>
/// <returns>A new instance of <see cref="AspireStore"/>.</returns>
public AspireStore(string basePath)
public AspireStore(string basePath, IFileSystemService directoryService)
{
ArgumentNullException.ThrowIfNull(basePath);
ArgumentNullException.ThrowIfNull(directoryService);

if (!Path.IsPathRooted(basePath))
{
throw new ArgumentException($"An absolute path is required: '${basePath}'", nameof(basePath));
}

_basePath = basePath;
_directoryService = directoryService;
EnsureDirectory();
}

Expand All @@ -43,7 +49,7 @@ public string GetFileNameWithContent(string filenameTemplate, Stream contentStre
filenameTemplate = Path.GetFileName(filenameTemplate);

// Create a temporary file to write the content to.
var tempFileName = Path.GetTempFileName();
var tempFileName = _directoryService.TempDirectory.CreateTempFile().Path;

// Fast, non-cryptographic hash.
var hash = new XxHash3();
Expand Down
6 changes: 4 additions & 2 deletions src/Aspire.Hosting/ApplicationModel/ProjectResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#pragma warning disable ASPIREDOCKERFILEBUILDER001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
#pragma warning disable ASPIRECONTAINERRUNTIME001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
#pragma warning disable ASPIRECOMPUTE001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
Expand Down Expand Up @@ -137,9 +138,10 @@ private async Task BuildProjectImage(PipelineStepContext ctx)
// Add COPY --from: statements for each source
stage.AddContainerFiles(this, containerWorkingDir, logger);

// Write the Dockerfile to a temporary location
// Get the directory service to create temp Dockerfile
var projectDir = Path.GetDirectoryName(projectMetadata.ProjectPath)!;
var tempDockerfilePath = Path.GetTempFileName();
var directoryService = ctx.Services.GetRequiredService<IFileSystemService>();
var tempDockerfilePath = directoryService.TempDirectory.CreateTempFile().Path;

var builtSuccessfully = false;
try
Expand Down
18 changes: 6 additions & 12 deletions src/Aspire.Hosting/ApplicationModel/UserSecretsParameterDefault.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#pragma warning disable ASPIREUSERSECRETS001

using System.Diagnostics;
using System.Reflection;
using Aspire.Hosting.Publishing;
using Aspire.Hosting.UserSecrets;

Expand All @@ -11,27 +12,20 @@ namespace Aspire.Hosting.ApplicationModel;
/// <summary>
/// Wraps a <see cref="ParameterDefault"/> such that the default value is saved to the project's user secrets store.
/// </summary>
/// <param name="appHostAssembly">The app host assembly.</param>
/// <param name="applicationName">The application name.</param>
/// <param name="parameterName">The parameter name.</param>
/// <param name="parameterDefault">The <see cref="ParameterDefault"/> that will produce the default value when it isn't found in the project's user secrets store.</param>
/// <param name="factory">The factory to use for creating user secrets managers.</param>
internal sealed class UserSecretsParameterDefault(Assembly appHostAssembly, string applicationName, string parameterName, ParameterDefault parameterDefault, UserSecretsManagerFactory factory)
/// <param name="userSecretsManager">The user secrets manager.</param>
internal sealed class UserSecretsParameterDefault(string applicationName, string parameterName, ParameterDefault parameterDefault, IUserSecretsManager userSecretsManager)
: ParameterDefault
{
public UserSecretsParameterDefault(Assembly appHostAssembly, string applicationName, string parameterName, ParameterDefault parameterDefault)
: this(appHostAssembly, applicationName, parameterName, parameterDefault, UserSecretsManagerFactory.Instance)
{
}

/// <inheritdoc/>
public override string GetDefaultValue()
{
var value = parameterDefault.GetDefaultValue();
var configurationKey = $"Parameters:{parameterName}";

var manager = factory.GetOrCreate(appHostAssembly);
if (!manager.TrySetSecret(configurationKey, value))

if (!userSecretsManager.TrySetSecret(configurationKey, value))
{
// This is a best-effort operation, so we don't throw if it fails. Common reason for failure is that the user secrets ID is not set
// in the application's assembly. Note there's no ILogger available this early in the application lifecycle.
Expand Down
Loading
Loading