diff --git a/src/Aspire.Hosting/Orchestrator/ParameterProcessor.cs b/src/Aspire.Hosting/Orchestrator/ParameterProcessor.cs index e8730049eab..549868e494c 100644 --- a/src/Aspire.Hosting/Orchestrator/ParameterProcessor.cs +++ b/src/Aspire.Hosting/Orchestrator/ParameterProcessor.cs @@ -96,11 +96,14 @@ public async Task InitializeParametersAsync(DistributedApplicationModel model, b private async Task CollectDependentParameterResourcesAsync(DistributedApplicationModel model, Dictionary referencedParameters, HashSet currentDependencySet, CancellationToken cancellationToken) { - var publishExecutionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Publish); - foreach (var resource in model.Resources) { - await ProcessResourceDependenciesAsync(resource, publishExecutionContext, referencedParameters, currentDependencySet, cancellationToken).ConfigureAwait(false); + if (resource.IsExcludedFromPublish()) + { + continue; + } + + await ProcessResourceDependenciesAsync(resource, executionContext, referencedParameters, currentDependencySet, cancellationToken).ConfigureAwait(false); } } diff --git a/tests/Aspire.Hosting.Tests/Orchestrator/ParameterProcessorTests.cs b/tests/Aspire.Hosting.Tests/Orchestrator/ParameterProcessorTests.cs index a2d8f330652..79ccb9dabb0 100644 --- a/tests/Aspire.Hosting.Tests/Orchestrator/ParameterProcessorTests.cs +++ b/tests/Aspire.Hosting.Tests/Orchestrator/ParameterProcessorTests.cs @@ -679,6 +679,81 @@ public async Task InitializeParametersAsync_WithDistributedApplicationModel_Hand } } + [Fact] + public async Task InitializeParametersAsync_UsesExecutionContextOptions_DoesNotThrow() + { + // Arrange + using var builder = TestDistributedApplicationBuilder.Create(); + var param = builder.AddParameter("testParam", () => "testValue"); + + var serviceProviderAccessed = false; + builder.AddContainer("testContainer", "nginx") + .WithEnvironment(context => + { + // This should not throw InvalidOperationException + // when using the proper execution context constructor + var sp = context.ExecutionContext.ServiceProvider; + serviceProviderAccessed = sp is not null; + context.EnvironmentVariables["TEST_ENV"] = param; + }); + + using var app = builder.Build(); + var model = app.Services.GetRequiredService(); + + // Get the ParameterProcessor from the built app's service provider + // This ensures it has the proper execution context with ServiceProvider + var parameterProcessor = app.Services.GetRequiredService(); + + // Act - Should not throw InvalidOperationException about IServiceProvider not being available + await parameterProcessor.InitializeParametersAsync(model); + + // Assert + Assert.True(serviceProviderAccessed); + var parameterResource = model.Resources.OfType().Single(); + Assert.NotNull(parameterResource.WaitForValueTcs); + Assert.True(parameterResource.WaitForValueTcs.Task.IsCompletedSuccessfully); + } + + [Fact] + public async Task InitializeParametersAsync_SkipsResourcesExcludedFromPublish() + { + // Arrange + using var builder = TestDistributedApplicationBuilder.Create(); + var param = builder.AddParameter("excludedParam", () => "excludedValue"); + + var excludedContainer = builder.AddContainer("excludedContainer", "nginx") + .WithEnvironment(context => + { + context.EnvironmentVariables["EXCLUDED_ENV"] = param; + }); + + // Mark the container as excluded from publish + excludedContainer.ExcludeFromManifest(); + + using var app = builder.Build(); + var model = app.Services.GetRequiredService(); + + var parameterProcessor = CreateParameterProcessor(); + + // Act - The excluded container should be skipped during parameter collection + await parameterProcessor.InitializeParametersAsync(model); + + // Assert + // The environment callback should have been invoked during parameter collection + // because we now create a publish execution context to collect dependent parameters + // However, since we filter out excluded resources, the parameter should not be initialized + // unless it's explicitly in the model + var parameters = model.Resources.OfType().ToList(); + Assert.Single(parameters); + + var parameterResource = parameters[0]; + Assert.Equal("excludedParam", parameterResource.Name); + + // The parameter should be initialized since it's explicitly in the model + Assert.NotNull(parameterResource.WaitForValueTcs); + Assert.True(parameterResource.WaitForValueTcs.Task.IsCompletedSuccessfully); + } + private static ParameterProcessor CreateParameterProcessor( ResourceNotificationService? notificationService = null, ResourceLoggerService? loggerService = null,