Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/list-of-diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
| `ASPIRE011` | Error | '\[ProjectName\]' is configured to invoke the Aspire CLI through DNX, but the dnx command could not be found on PATH. | [src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.in.targets](../src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.in.targets) |
| `ASPIRE012` | Warning | '\[ProjectName\]' uses the Aspire.AppHost.Sdk, which is obsolete but remains available for staged migration. Switch the project's Sdk attribute to Microsoft.NET.Sdk, add explicit PackageReference items for Aspire.Hosting.AppHost and Aspire.Hosting.Dotnet, and migrate C# resource ProjectReference items to AddDotnetProject calls. Set SuppressAspireAppHostSdkObsoleteWarning=true (or add ASPIRE012 to $(NoWarn)) to suppress temporarily during migration. See https://aka.ms/aspire/diagnostics/aspire012 for migration guidance. | [src/Aspire.AppHost.Sdk/SDK/Sdk.in.targets](../src/Aspire.AppHost.Sdk/SDK/Sdk.in.targets) |
| `ASPIRE013` | Error | '\[ProjectName\]' requires the Aspire CLI bundle. Remove AspireUseCliBundle or set it to true, and install the matching Aspire CLI from https://get.aspire.dev. ASPIRE013 cannot be suppressed to disable the bundle. See https://aka.ms/aspire/diagnostics/aspire013 for more information. | [src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.in.targets](../src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.in.targets) |
| `ASPIRE014` | Warning | '\[ProjectName\]' is configured with an unsupported AspireCliInvocationMode value. Supported values are Path, Dnx, and DnxPinned. The default Path invocation mode will be used. | [src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.in.targets](../src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.in.targets) |

The CLI bundle is required by default for C# AppHosts and cannot be disabled (`ASPIRE013`). Non-C# AppHosts retain the suppressible `ASPIRE010` warning when the bundle is disabled. `AspireCliInvocationMode=Path` uses `aspire` from `PATH`, falling back to the `Aspire.Cli` version paired with the AppHost package through DNX when a compatible command is unavailable. `AspireCliInvocationMode=Dnx` invokes the unversioned `Aspire.Cli` package through DNX so an in-scope tool manifest is honored, or the latest package is used when no manifest applies. `AspireCliInvocationMode=DnxPinned` invokes the exact `Aspire.Cli` version paired with `Aspire.Hosting.AppHost`.
The CLI bundle is required by default for C# AppHosts and cannot be disabled (`ASPIRE013`). Non-C# AppHosts retain the suppressible `ASPIRE010` warning when the bundle is disabled. `AspireCliInvocationMode=Path` uses `aspire` from `PATH`, falling back to the `Aspire.Cli` version paired with the AppHost package through DNX when a compatible command is unavailable. `AspireCliInvocationMode=Dnx` invokes the unversioned `Aspire.Cli` package through DNX so an in-scope tool manifest is honored, or the latest package is used when no manifest applies. `AspireCliInvocationMode=DnxPinned` invokes the exact `Aspire.Cli` version paired with `Aspire.Hosting.AppHost`. Any other value produces the `ASPIRE014` warning and uses the default `Path` mode.

When automatic CLI bundle setup fails, `ASPIRE009` includes the setup command's captured standard output and standard error, including DNX package acquisition errors. If a PATH CLI setup attempt falls back to DNX and neither produces a usable bundle, output from both attempts is included with the corresponding command. Successful or recovered setup attempts keep this output at diagnostic verbosity.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@ namespace Projects%3B

<Target Name="_ResolveAspireCliInvocation"
Condition="'$(IsAspireHost)' == 'true' and '$(AspireUseCliBundle)' == 'true'">
<Warning Code="ASPIRE014"
Condition="'$(_AspireCliInvocationMode.ToLowerInvariant())' != 'path' and '$(_AspireCliInvocationMode.ToLowerInvariant())' != 'dnx' and '$(_AspireCliInvocationMode.ToLowerInvariant())' != 'dnxpinned'"
Text="$(MSBuildProjectName) is configured with AspireCliInvocationMode=$(_AspireCliInvocationMode), which is not a supported value. Supported values are Path, Dnx, and DnxPinned. The default Path invocation mode will be used." />

<ResolveAspireCliInvocation AspireCliPath="$(AspireCliPath)"
AspireCliInvocationMode="$(_AspireCliInvocationMode)"
PathEnvironmentVariable="$(Path)">
Expand Down
84 changes: 84 additions & 0 deletions tests/Aspire.Hosting.Sdk.Tests/AppHostSdkTargetsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,84 @@ public async Task ComputeRunArgumentsFailsWhenDnxModeIsConfiguredAndDnxIsMissing
Assert.Contains("Install or use the .NET SDK 10.0 or later", result.Output);
}

[Theory]
[InlineData("DefinitelyInvalid")]
[InlineData("DnxPinnedd")]
public async Task ComputeRunArgumentsWarnsAndUsesPathModeWhenInvocationModeIsUnsupported(string invocationMode)
{
using var workspace = TemporaryWorkspace.Create(outputHelper);
var fakeCliDirectory = Directory.CreateDirectory(Path.Combine(workspace.Path, "fake-cli"));
var aspireCliPath = await CreateFakeAspireCliAsync(fakeCliDirectory.FullName);
await CreateFakeDnxAsync(fakeCliDirectory.FullName);
var project = await CreateRunHookProjectAsync(workspace.Path, aspireUseCliBundle: true,
$$"""
<PropertyGroup>
<AspireCliInvocationMode>{{invocationMode}}</AspireCliInvocationMode>
</PropertyGroup>
""");

var result = await RunComputeRunArgumentsAsync(project, CreatePathEnvironment(fakeCliDirectory.FullName));

Assert.True(result.ExitCode == 0, result.Output);
Assert.Contains("warning ASPIRE014", result.Output);
Assert.Contains($"AspireCliInvocationMode={invocationMode}, which is not a supported value", result.Output);
Assert.Contains("Supported values are Path, Dnx, and DnxPinned", result.Output);

var properties = await GetComputeRunArgumentsPropertiesAsync(
project,
environment: CreatePathEnvironment(fakeCliDirectory.FullName));

Assert.Equal("Aspire", properties["_AspireResolvedCliInvocationMode"]);
Assert.Equal(GetExpectedAspireRunCommand(aspireCliPath), properties["RunCommand"]);
}

[Theory]
[InlineData(null)]
[InlineData("Path")]
[InlineData("pAtH")]
[InlineData("Dnx")]
[InlineData("DnxPinned")]
public async Task ComputeRunArgumentsDoesNotWarnWhenInvocationModeIsSupported(string? invocationMode)
{
using var workspace = TemporaryWorkspace.Create(outputHelper);
var fakeCliDirectory = Directory.CreateDirectory(Path.Combine(workspace.Path, "fake-cli"));
await CreateFakeAspireCliAsync(fakeCliDirectory.FullName);
await CreateFakeDnxAsync(fakeCliDirectory.FullName);
var extraProjectXml = invocationMode is null
? null
: $$"""
<PropertyGroup>
<AspireCliInvocationMode>{{invocationMode}}</AspireCliInvocationMode>
</PropertyGroup>
""";
var project = await CreateRunHookProjectAsync(workspace.Path, aspireUseCliBundle: true, extraProjectXml);

var result = await RunComputeRunArgumentsAsync(project, CreatePathEnvironment(fakeCliDirectory.FullName));

Assert.True(result.ExitCode == 0, result.Output);
Assert.DoesNotContain("ASPIRE014", result.Output);
}

[Fact]
public async Task UnsupportedInvocationModeWarningCanBeSuppressed()
{
using var workspace = TemporaryWorkspace.Create(outputHelper);
var fakeCliDirectory = Directory.CreateDirectory(Path.Combine(workspace.Path, "fake-cli"));
await CreateFakeAspireCliAsync(fakeCliDirectory.FullName);
var project = await CreateRunHookProjectAsync(workspace.Path, aspireUseCliBundle: true,
"""
<PropertyGroup>
<AspireCliInvocationMode>DefinitelyInvalid</AspireCliInvocationMode>
<NoWarn>$(NoWarn);ASPIRE014</NoWarn>
</PropertyGroup>
""");

var result = await RunComputeRunArgumentsAsync(project, CreatePathEnvironment(fakeCliDirectory.FullName));

Assert.True(result.ExitCode == 0, result.Output);
Assert.DoesNotContain("ASPIRE014", result.Output);
}

[Fact]
public async Task ComputeRunArgumentsSkipsNonExecutableDnxOnUnix()
{
Expand Down Expand Up @@ -1640,6 +1718,12 @@ private static async Task<Dictionary<string, string>> GetComputeRunArgumentsProp
environment);
}

private static Task<DotNetResult> RunComputeRunArgumentsAsync(RunHookProject project, IDictionary<string, string> environment)
=> RunDotNetWithArgumentsAsync(
project.ProjectDirectory,
["msbuild", "-nologo", "-restore", "-t:ComputeRunArguments", project.ProjectFile],
environment);

private static async Task<Dictionary<string, string>> GetResolveAspireCliBundlePathPropertiesAsync(
RunHookProject project,
IDictionary<string, string> environment,
Expand Down
Loading