Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 21, 2025

Description

This PR implements AppHostEnvironment as a public sealed class to centralize AppHost configuration access, based on PR #13018 but with key modifications per requirements.

Successfully implemented AppHostEnvironment as a public sealed class (not an interface) to centralize AppHost configuration access.

Key Changes:

  • No IAppHostEnvironment interface - just a public AppHostEnvironment sealed class that can be constructed directly
  • Unified ProjectName - Removed DashboardApplicationName property to eliminate confusion, unified on ProjectName which automatically trims ".AppHost" suffix (case-insensitive)
  • Exposed underlying services - Added public Configuration and HostEnvironment properties for direct access to IConfiguration and IHostEnvironment

Implementation Details:

AppHostEnvironment Class:

  • Created as a public sealed class with properties for all AppHost configuration values
  • ProjectName property automatically trims ".AppHost" suffix if present (case-insensitive)
  • Exposes Configuration and HostEnvironment properties for consumers who need direct access to underlying services
  • Properties for ProjectDirectory, FullPath, hash values, API keys, and other AppHost-specific configuration

Integration:

  • Added AppHostEnvironment property to IDistributedApplicationBuilder interface
  • Initialized in DistributedApplicationBuilder constructor and registered in DI
  • Updated 25+ production files to use AppHostEnvironment instead of directly accessing IConfiguration

Test Infrastructure:

  • Registered AppHostEnvironment in TestServiceProvider so it's available in all test scenarios
  • Updated test helpers and test files to work with the new infrastructure

Simplified Dashboard Logic:

  • Updated DashboardService to use ProjectName directly (suffix trimming now handled by AppHostEnvironment)
  • Removed ComputeApplicationName method and ApplicationNameRegex (no longer needed)

Files Changed (36 total):

New files (1):

  • src/Aspire.Hosting/AppHostEnvironment.cs - Public sealed class

Production code updated (26 files):

  • Core hosting: DistributedApplicationBuilder, IDistributedApplicationBuilder, DistributedApplicationLifecycle, VolumeNameGenerator, ParameterResourceBuilderExtensions, ResourceLoggerForwarderService, ProjectResourceBuilderExtensions, OtlpConfigurationExtensions, VersionCheckService, FileDeploymentStateManager
  • DCP: DcpNameGenerator, DcpExecutor
  • Dashboard: DashboardOptions, DashboardService (simplified), DashboardEventHandlers
  • Azure: BaseProvisioningContextProvider, PublishModeProvisioningContextProvider, RunModeProvisioningContextProvider, AzureEnvironmentResourceExtensions, AzureFunctionsProjectResourceExtensions
  • Testing: DistributedApplicationTestingBuilder

Test infrastructure updated (10 files):

  • Test helpers: TestServiceProvider (registers AppHostEnvironment), TestAppHostEnvironment, ProvisioningTestHelpers
  • Test files: DashboardServiceTests, ResourceLoggerForwarderServiceTests, DashboardLifecycleHookTests, DeploymentStateManagerTests, VersionCheckServiceTests, DcpExecutorTests, ProvisioningContextProviderTests, AzureDeployerTests

Difference from PR #13018:

The original PR used an IAppHostEnvironment interface with an internal AppHostEnvironment implementation. This PR makes AppHostEnvironment a public sealed class directly, eliminating the interface layer as requested. Additionally:

  • Exposes Configuration and HostEnvironment as public properties for direct access
  • Uses a single ProjectName property that automatically trims ".AppHost" suffix (unified, no separate DashboardApplicationName)

Fixes # (related to PR #13018)

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

Make a new PR with these contents #13018 but don't make it an interface, just make it a class and make that class public.


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

Copilot AI changed the title [WIP] Convert interface to public class Add AppHostEnvironment as public sealed class Nov 21, 2025
Copilot AI requested a review from davidfowl November 21, 2025 04:39
@github-actions
Copy link
Contributor

github-actions bot commented Nov 21, 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 -- 13085

Or

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

@davidfowl davidfowl marked this pull request as ready for review November 21, 2025 05:27
Copilot AI review requested due to automatic review settings November 21, 2025 05:27
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 introduces AppHostEnvironment as a public sealed class to centralize access to AppHost configuration values, replacing direct IConfiguration["AppHost:*"] access throughout the codebase with strongly-typed properties.

Key Changes:

  • Introduces AppHostEnvironment class with properties for project name, directories, hashes, API keys, and other AppHost-specific configuration
  • Updates IDistributedApplicationBuilder to expose AppHostEnvironment property
  • Refactors 25+ production files to use strongly-typed properties instead of configuration string keys

Reviewed Changes

Copilot reviewed 32 out of 32 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/Aspire.Hosting/AppHostEnvironment.cs New public sealed class centralizing AppHost configuration access
src/Aspire.Hosting/IDistributedApplicationBuilder.cs Adds AppHostEnvironment property with default implementation
src/Aspire.Hosting/DistributedApplicationBuilder.cs Initializes AppHostEnvironment and registers in DI
src/Aspire.Hosting/VolumeNameGenerator.cs Uses ProjectName and DefaultHash instead of configuration keys
src/Aspire.Hosting/VersionChecking/VersionCheckService.cs Uses ProjectDirectory from AppHostEnvironment
src/Aspire.Hosting/ResourceLoggerForwarderService.cs Uses ProjectName for logger category names
src/Aspire.Hosting/ProjectResourceBuilderExtensions.cs Uses DefaultLaunchProfileName property
src/Aspire.Hosting/Pipelines/Internal/FileDeploymentStateManager.cs Uses FullPathHash for deployment state
src/Aspire.Hosting/ParameterResourceBuilderExtensions.cs Uses ProjectName for user secrets
src/Aspire.Hosting/OtlpConfigurationExtensions.cs Uses OtlpApiKey property
src/Aspire.Hosting/DistributedApplicationLifecycle.cs Uses ProjectDirectory for logging
src/Aspire.Hosting/Dcp/DcpNameGenerator.cs Uses DefaultHash for name generation
src/Aspire.Hosting/Dcp/DcpExecutor.cs Uses ContainerHostname property
src/Aspire.Hosting/Dashboard/DashboardService.cs Uses DashboardApplicationName property
src/Aspire.Hosting/Dashboard/DashboardOptions.cs Uses OtlpApiKey property
src/Aspire.Hosting/Dashboard/DashboardEventHandlers.cs Uses ResourceServiceAuthMode and ResourceServiceApiKey
src/Aspire.Hosting.Testing/DistributedApplicationTestingBuilder.cs Exposes AppHostEnvironment property
src/Aspire.Hosting.Azure/Provisioning/Internal/BaseProvisioningContextProvider.cs Uses AppHostEnvironment instead of IHostEnvironment
src/Aspire.Hosting.Azure/Provisioning/Internal/RunModeProvisioningContextProvider.cs Uses ProjectName for resource group names
src/Aspire.Hosting.Azure/Provisioning/Internal/PublishModeProvisioningContextProvider.cs Uses ProjectName for resource group names
src/Aspire.Hosting.Azure/AzureEnvironmentResourceExtensions.cs Uses ProjectNameHash for stable naming
src/Aspire.Hosting.Azure.Functions/AzureFunctionsProjectResourceExtensions.cs Uses ProjectNameHash for storage names
tests/Aspire.Hosting.Tests/Utils/TestAppHostEnvironment.cs New test helper factory for creating AppHostEnvironment instances
tests/Aspire.Hosting.Tests/VersionChecking/VersionCheckServiceTests.cs Updated to use TestAppHostEnvironment.Create
tests/Aspire.Hosting.Tests/Publishing/DeploymentStateManagerTests.cs Updated to construct AppHostEnvironment
tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs Updated to construct AppHostEnvironment
tests/Aspire.Hosting.Tests/Dashboard/DashboardServiceTests.cs Updated to use TestAppHostEnvironment.Create
tests/Aspire.Hosting.Tests/Dashboard/DashboardLifecycleHookTests.cs Updated to use TestAppHostEnvironment.Create
tests/Aspire.Hosting.Testing.Tests/ResourceLoggerForwarderServiceTests.cs Updated to construct AppHostEnvironment
tests/Aspire.Hosting.Azure.Tests/ProvisioningTestHelpers.cs Added CreateAppHostEnvironment helper method
tests/Aspire.Hosting.Azure.Tests/ProvisioningContextProviderTests.cs Updated to use CreateAppHostEnvironment
tests/Aspire.Hosting.Azure.Tests/AzureDeployerTests.cs Updated to use CreateAppHostEnvironment

Comment on lines 38 to 83
/// Gets the directory of the project where the app host is located.
/// </summary>
public string ProjectDirectory => _configuration["AppHost:Directory"]!;
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

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

The null-forgiving operator (!) is used here, but ProjectDirectory can legitimately be null if the configuration value is not set. Consider making this property nullable (string?) to properly represent the possibility of a missing value, or document that this value is guaranteed to be present.

Suggested change
/// Gets the directory of the project where the app host is located.
/// </summary>
public string ProjectDirectory => _configuration["AppHost:Directory"]!;
/// Gets the directory of the project where the app host is located, or <c>null</c> if not set.
/// </summary>
public string? ProjectDirectory => _configuration["AppHost:Directory"];

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added documentation clarifying that ProjectDirectory is guaranteed to be set by the build system at runtime. The null-forgiving operator is correct for this property. (4a76bff)

Comment on lines 53 to 102
/// Gets the SHA256 hash of the app host.
/// </summary>
/// <remarks>
/// For backward compatibility, this uses mode-dependent logic:
/// - Publish mode: ProjectNameSha (stable across paths)
/// - Run mode: FullPathHash (disambiguates by path)
/// </remarks>
public string DefaultHash => _configuration["AppHost:Sha256"]!;
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

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

The null-forgiving operator (!) is used here, but DefaultHash can legitimately be null if the configuration value is not set. Consider making this property nullable (string?) to properly represent the possibility of a missing value, or document that this value is guaranteed to be present.

Suggested change
/// Gets the SHA256 hash of the app host.
/// </summary>
/// <remarks>
/// For backward compatibility, this uses mode-dependent logic:
/// - Publish mode: ProjectNameSha (stable across paths)
/// - Run mode: FullPathHash (disambiguates by path)
/// </remarks>
public string DefaultHash => _configuration["AppHost:Sha256"]!;
/// Gets the SHA256 hash of the app host, if available.
/// </summary>
/// <remarks>
/// For backward compatibility, this uses mode-dependent logic:
/// <list type="bullet">
/// <item>Publish mode: ProjectNameSha (stable across paths)</item>
/// <item>Run mode: FullPathHash (disambiguates by path)</item>
/// </list>
/// This property may be <c>null</c> if the configuration value <c>AppHost:Sha256</c> is not set.
/// </remarks>
public string? DefaultHash => _configuration["AppHost:Sha256"];

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added documentation clarifying that DefaultHash is guaranteed to be set by the build system at runtime. The null-forgiving operator is correct for this property. (4a76bff)

/// This is the project name used in multiple places throughout the application,
/// including for generating resource names and configuration keys.
/// </remarks>
public string ProjectName => _configuration["AppHost:DashboardApplicationName"] ?? _hostEnvironment.ApplicationName;
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

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

The ProjectName and DashboardApplicationName properties (lines 35 and 50) have identical implementations. This duplication suggests they serve different semantic purposes but are currently implemented the same way. Consider either consolidating them if they're truly the same, or adding distinct remarks explaining when to use each property.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added documentation explaining that DashboardApplicationName is provided separately for semantic clarity when specifically referring to the dashboard display name, though it returns the same value as ProjectName. (4a76bff)

Copy link
Member

Choose a reason for hiding this comment

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

@davidfowl - I agree with the first Copilot that it is confusing to have 2 properties that are the same thing.

Copy link
Member

Choose a reason for hiding this comment

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

@copilot lets unify on ProjectName then, this should also trim the "apphost" suffix from the name if it exists.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unified on ProjectName by removing DashboardApplicationName. The ProjectName property now automatically trims the ".AppHost" suffix (case-insensitive) if present. Also simplified DashboardService to use ProjectName directly and removed the now-unused regex logic. (b66498b)

Comment on lines 7 to 8
#pragma warning disable IDE0005 // IConfiguration is used in this file

Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

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

The pragma disable comment 'IConfiguration is used in this file' is misleading. IConfiguration IS imported and used in this file (line 36 and field _configuration on line 65). This pragma should either be removed if the using is necessary, or the comment should be updated to explain why the warning is being suppressed despite the using statement being present.

Suggested change
#pragma warning disable IDE0005 // IConfiguration is used in this file

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated the pragma comment to be more accurate. The IConfiguration using directive is necessary as the field _configuration is actively used throughout the file. (4a76bff)

Comment on lines +111 to +114
/// <summary>
/// Creates a test AppHost environment.
/// </summary>
public static AppHostEnvironment CreateAppHostEnvironment()
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

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

According to the XML documentation standards, internal methods should have minimal documentation. The <summary> tag here is sufficient, but if there were more verbose documentation (like <remarks> or <example>), it should be removed for internal APIs.

Copilot generated this review using guidance from repository custom instructions.
@davidfowl
Copy link
Member

@copilot Look at the test failures on this PR, they are related to:

System.InvalidOperationException : No service for type 'Aspire.Hosting.AppHostEnvironment' has been registered. at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Aspire.Hosting.OtlpConfigurationExtensions.<>c__DisplayClass2_0.<b__0>d.MoveNext() in //src/Aspire.Hosting/OtlpConfigurationExtensions.cs:line 81
--- End of stack trace from previous location ---
at Aspire.Hosting.ApplicationModel.ResourceExtensions.ProcessEnvironmentVariableValuesAsync(IResource resource, DistributedApplicationExecutionContext executionContext, Action`4 processValue, ILogger logger, CancellationToken cancellationToken) in /
/src/Aspire.Hosting/ApplicationModel/ResourceExtensions.cs:line 342
at Aspire.Hosting.Tests.Utils.EnvironmentVariableEvaluator.GetEnvironmentVariablesAsync(IResource resource, DistributedApplicationOperation applicationOperation, IServiceProvider serviceProvider) in /home/runner/work/aspire/aspire/tests/Aspire.Hosting.Tests/Utils/EnvironmentVariableEvaluator.cs:line 22
at Aspire.Hosting.Tests.MauiPlatformExtensionsTests.AddiOSSimulator_WithEnvironment_EnvironmentVariablesAreSet() in /home/runner/work/aspire/aspire/tests/Aspire.Hosting.Maui.Tests/MauiPlatformExtensionsTests.cs:line 508
--- End of stack trace from previous location ---

Copy link
Contributor Author

Copilot AI commented Nov 21, 2025

@copilot Look at the test failures on this PR, they are related to:

System.InvalidOperationException : No service for type 'Aspire.Hosting.AppHostEnvironment' has been registered. at Microsoft.Exte...

Fixed the test failure by changing GetRequiredService<AppHostEnvironment>() to GetService<AppHostEnvironment>() with fallback to IConfiguration["AppHost:OtlpApiKey"] when AppHostEnvironment is not registered in the service provider (e.g., in test scenarios). This maintains backward compatibility while allowing tests to continue working. (4a76bff)

@davidfowl
Copy link
Member

@copilot That's the wrong fix, the right fix is to make sure that AppHostEnvironment is registered in tests.

Copy link
Contributor Author

Copilot AI commented Nov 21, 2025

@copilot That's the wrong fix, the right fix is to make sure that AppHostEnvironment is registered in tests.

Fixed by registering AppHostEnvironment in TestServiceProvider constructor. Reverted the fallback approach in OtlpConfigurationExtensions to use GetRequiredService as intended. (b719571)

Copilot AI and others added 2 commits December 8, 2025 01:37
Create AppHostEnvironment as public class and update core hosting files

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

Update Azure provisioning and test files to use AppHostEnvironment

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

Address code review feedback: Add documentation and fix test failure

- Added documentation explaining that configuration values are guaranteed by build system
- Added semantic distinction between ProjectName and DashboardApplicationName
- Fixed test failure by making AppHostEnvironment retrieval safe with fallback to IConfiguration
- Updated pragma comment to be more accurate

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

Fix test infrastructure: Register AppHostEnvironment in TestServiceProvider

- Reverted fallback approach in OtlpConfigurationExtensions (now uses GetRequiredService)
- Added AppHostEnvironment registration in TestServiceProvider constructor
- Tests now properly have AppHostEnvironment available in their service provider

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

Expose Configuration and HostEnvironment as public properties

Added public Configuration and HostEnvironment properties to AppHostEnvironment class so consumers can access the underlying services directly without needing to inject multiple DI services.

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

Unify on ProjectName and remove DashboardApplicationName

- Removed DashboardApplicationName property (unified on ProjectName)
- Updated ProjectName to automatically trim ".AppHost" suffix (case-insensitive)
- Simplified DashboardService to use ProjectName directly
- Removed unused ApplicationNameRegex and System.Text.RegularExpressions using

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

Rename ProjectName to ApplicationName for consistency across the codebase
…plicationName

- Add DisplayName property to AppHostEnvironment that returns a display-friendly
  application name (strips .AppHost suffix for traditional app hosts, uses
  directory name for single-file app hosts)
- Rename DashboardApplicationName to SingleFileAppHostName in DistributedApplicationOptions
  to better reflect its purpose
- Update DashboardService to use DisplayName for the dashboard display
- ApplicationName now returns the full name from IHostEnvironment without modification

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@davidfowl davidfowl force-pushed the copilot/make-class-public-instead-of-interface branch from b66498b to 0f2d970 Compare December 8, 2025 09:59
/// </summary>
/// <remarks>
/// For backward compatibility, this uses mode-dependent logic:
/// - Publish mode: ProjectNameSha (stable across paths)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/// - Publish mode: ProjectNameSha (stable across paths)
/// - Publish mode: ProjectNameHash (stable across paths)

/// Gets the SHA256 hash of the app host.
/// </summary>
/// <remarks>
/// For backward compatibility, this uses mode-dependent logic:
Copy link
Member

Choose a reason for hiding this comment

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

What does "For backward compatibility" mean here?

Copy link
Member

Choose a reason for hiding this comment

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

Do we really need this one? It is only used in 2 places internally. If this is for "backward compat", maybe it shouldn't be public?

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.

3 participants