From 714fa583620c4f580c15dc1eb9c6527b6b9b1318 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Mon, 1 Jun 2026 15:27:59 +1000 Subject: [PATCH] Remove duplicated profiles block from empty C# AppHost aspire.config.json The embedded CLI template src/Aspire.Cli/Templating/Templates/empty-apphost/aspire.config.json shipped a profiles block that duplicated apphost.run.json. The canonical template in src/Aspire.ProjectTemplates/templates/aspire-apphost-singlefile/aspire.config.json intentionally has no profiles (per Damian's design): aspire run / dotnet run apphost.cs honor apphost.run.json when present, so aspire.config.json for the C# Empty template should only carry { "appHost": { "path": "apphost.cs" } }. Updated NewCommandTests to: - assert that aspire.config.json has no profiles block and pins appHost.path = apphost.cs - assert that apphost.run.json carries the launch URLs (plain localhost and dev.localhost variants) - drop the now-unused AssertHttpsApplicationUrlMatches helper Fixes #17660 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../empty-apphost/aspire.config.json | 21 -------- .../Commands/NewCommandTests.cs | 54 +++++++++++-------- 2 files changed, 32 insertions(+), 43 deletions(-) diff --git a/src/Aspire.Cli/Templating/Templates/empty-apphost/aspire.config.json b/src/Aspire.Cli/Templating/Templates/empty-apphost/aspire.config.json index 7a05e4a348e..a777aa51fac 100644 --- a/src/Aspire.Cli/Templating/Templates/empty-apphost/aspire.config.json +++ b/src/Aspire.Cli/Templating/Templates/empty-apphost/aspire.config.json @@ -1,26 +1,5 @@ { "appHost": { "path": "apphost.cs" - }, - "profiles": { - "https": { - "applicationUrl": "https://{{hostName}}:{{httpsPort}};http://{{hostName}}:{{httpPort}}", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "DOTNET_ENVIRONMENT": "Development", - "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://{{hostName}}:{{otlpHttpsPort}}", - "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://{{hostName}}:{{resourceHttpsPort}}" - } - }, - "http": { - "applicationUrl": "http://{{hostName}}:{{httpPort}}", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "DOTNET_ENVIRONMENT": "Development", - "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://{{hostName}}:{{otlpHttpPort}}", - "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://{{hostName}}:{{resourceHttpPort}}", - "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" - } - } } } diff --git a/tests/Aspire.Cli.Tests/Commands/NewCommandTests.cs b/tests/Aspire.Cli.Tests/Commands/NewCommandTests.cs index 633bdd9ee82..6f9d8df1861 100644 --- a/tests/Aspire.Cli.Tests/Commands/NewCommandTests.cs +++ b/tests/Aspire.Cli.Tests/Commands/NewCommandTests.cs @@ -1411,7 +1411,7 @@ public async Task NewCommandWithExplicitCSharpEmptyTemplateCreatesCSharpAppHost( } [Fact] - public async Task NewCommandWithCSharpEmptyTemplateAndPlainLocalhostEmitsAppHostRunJsonMatchingAspireConfigJson() + public async Task NewCommandWithCSharpEmptyTemplateEmitsAppHostRunJsonAndAspireConfigJsonWithoutDuplicateProfiles() { using var workspace = TemporaryWorkspace.Create(outputHelper); @@ -1434,10 +1434,14 @@ public async Task NewCommandWithCSharpEmptyTemplateAndPlainLocalhostEmitsAppHost var aspireConfig = await File.ReadAllTextAsync(aspireConfigPath); var appHostRunJson = await File.ReadAllTextAsync(appHostRunJsonPath); + // Launch profile shape (applicationUrl / commandName / environmentVariables) must live in + // apphost.run.json so that `dotnet run apphost.cs` and the C# Dev Kit can pick it up. Assert.Contains("://localhost:", appHostRunJson); Assert.Contains("\"commandName\": \"Project\"", appHostRunJson); - AssertHttpsApplicationUrlMatches(aspireConfig, appHostRunJson); + // aspire.config.json must NOT carry a duplicated `profiles` block — that content belongs to + // apphost.run.json only. See https://github.com/microsoft/aspire/issues/17660. + AssertAspireConfigHasNoProfiles(aspireConfig); } [Fact] @@ -1467,26 +1471,23 @@ public async Task NewCommandWithCSharpEmptyTemplateAndLocalhostTldEmitsAppHostRu Assert.Contains("testapp.dev.localhost", appHostRunJson); Assert.DoesNotContain("://localhost", appHostRunJson); - AssertHttpsApplicationUrlMatches(aspireConfig, appHostRunJson); + // aspire.config.json must NOT carry a duplicated `profiles` block — that content belongs to + // apphost.run.json only. See https://github.com/microsoft/aspire/issues/17660. + AssertAspireConfigHasNoProfiles(aspireConfig); } - private static void AssertHttpsApplicationUrlMatches(string aspireConfigJson, string appHostRunJson) + private static void AssertAspireConfigHasNoProfiles(string aspireConfigJson) { using var aspireDoc = System.Text.Json.JsonDocument.Parse(aspireConfigJson); - using var runDoc = System.Text.Json.JsonDocument.Parse(appHostRunJson); + Assert.False( + aspireDoc.RootElement.TryGetProperty("profiles", out _), + "aspire.config.json must not contain a 'profiles' block for the empty C# template; profiles live in apphost.run.json."); - var aspireHttpsUrl = aspireDoc.RootElement - .GetProperty("profiles") - .GetProperty("https") - .GetProperty("applicationUrl") - .GetString(); - var runHttpsUrl = runDoc.RootElement - .GetProperty("profiles") - .GetProperty("https") - .GetProperty("applicationUrl") - .GetString(); - - Assert.Equal(aspireHttpsUrl, runHttpsUrl); + // Pin the expected minimal shape: aspire.config.json for the C# Empty template should only + // identify the AppHost file. See https://github.com/microsoft/aspire/issues/17660. + Assert.True(aspireDoc.RootElement.TryGetProperty("appHost", out var appHost), "aspire.config.json is missing the required 'appHost' object."); + Assert.True(appHost.TryGetProperty("path", out var path), "aspire.config.json#appHost is missing the 'path' property."); + Assert.Equal("apphost.cs", path.GetString()); } [Fact] @@ -1532,11 +1533,20 @@ public async Task NewCommandWithEmptyTemplateAndCSharpPromptsForLocalhostTldAndU Assert.Equal(CliExitCodes.Success, exitCode); Assert.True(localhostPrompted); - var runProfilePath = Path.Combine(workspace.WorkspaceRoot.FullName, "output", "aspire.config.json"); - Assert.True(File.Exists(runProfilePath)); - var runProfile = await File.ReadAllTextAsync(runProfilePath); - Assert.Contains("testapp.dev.localhost", runProfile); - Assert.DoesNotContain("://localhost", runProfile); + var outputRoot = Path.Combine(workspace.WorkspaceRoot.FullName, "output"); + + var aspireConfigPath = Path.Combine(outputRoot, "aspire.config.json"); + Assert.True(File.Exists(aspireConfigPath)); + var aspireConfig = await File.ReadAllTextAsync(aspireConfigPath); + // aspire.config.json must NOT contain the localhost-TLD URLs — those belong in + // apphost.run.json. See https://github.com/microsoft/aspire/issues/17660. + Assert.DoesNotContain("testapp.dev.localhost", aspireConfig); + + var appHostRunJsonPath = Path.Combine(outputRoot, "apphost.run.json"); + Assert.True(File.Exists(appHostRunJsonPath)); + var appHostRunJson = await File.ReadAllTextAsync(appHostRunJsonPath); + Assert.Contains("testapp.dev.localhost", appHostRunJson); + Assert.DoesNotContain("://localhost", appHostRunJson); } [Fact]