Skip to content
Merged
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
23 changes: 22 additions & 1 deletion src/Aspire.Cli/DotNet/DotNetSdkInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ internal sealed class DotNetSdkInstaller(IFeatures features, IConfiguration conf
}

// Check if this version meets the minimum requirement
if (SemVersion.ComparePrecedence(sdkVersion, minVersion) >= 0)
if (MeetsMinimumRequirement(sdkVersion, minVersion, minimumVersion))
{
meetsMinimum = true;
}
Expand Down Expand Up @@ -152,4 +152,25 @@ public string GetEffectiveMinimumSdkVersion()
return MinimumSdkVersion;
}
}

/// <summary>
/// Checks if an installed SDK version meets the minimum requirement.
/// For .NET 10.x requirements, allows any .NET 10.x version including prereleases.
/// </summary>
/// <param name="installedVersion">The installed SDK version.</param>
/// <param name="requiredVersion">The required minimum version (parsed).</param>
/// <param name="requiredVersionString">The required version string.</param>
/// <returns>True if the installed version meets the requirement.</returns>
private static bool MeetsMinimumRequirement(SemVersion installedVersion, SemVersion requiredVersion, string requiredVersionString)
{
// Special handling for .NET 10.0.100 requirement - allow any .NET 10.x version
if (requiredVersionString == MinimumSdkVersionSingleFileAppHost)
{
// If we require 10.0.100, accept any version that is >= 10.0.0
return installedVersion.Major >= 10;
}

// For all other requirements, use strict version comparison
return SemVersion.ComparePrecedence(installedVersion, requiredVersion) >= 0;
}
}
65 changes: 65 additions & 0 deletions tests/Aspire.Cli.Tests/DotNetSdkInstallerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Aspire.Cli.DotNet;
using Aspire.Cli.Resources;
using Microsoft.Extensions.Configuration;
using Semver;

namespace Aspire.Cli.Tests;

Expand Down Expand Up @@ -238,6 +239,70 @@ public void ErrorMessage_Format_IsCorrect()
Assert.Equal("The Aspire CLI requires .NET SDK version 9.0.302 or later. Detected: (not found).", message);
}

[Fact]
public void MeetsMinimumRequirement_AllowsDotNet10Prereleases_ForSingleFileAppHost()
{
// Test the logic we added for allowing .NET 10 prereleases
var installedVersion = SemVersion.Parse("10.0.100-preview.1.25463.5", SemVersionStyles.Strict);
var requiredVersion = SemVersion.Parse("10.0.100", SemVersionStyles.Strict);
var requiredVersionString = "10.0.100";

// Use reflection to access the private method
var method = typeof(DotNetSdkInstaller).GetMethod("MeetsMinimumRequirement",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
var result = (bool)method!.Invoke(null, new object[] { installedVersion, requiredVersion, requiredVersionString })!;

Assert.True(result);
}

[Fact]
public void MeetsMinimumRequirement_AllowsDotNet10LatestPrerelease_ForSingleFileAppHost()
{
// Test with a more recent .NET 10 prerelease
var installedVersion = SemVersion.Parse("10.1.0-preview.2.25999.99", SemVersionStyles.Strict);
var requiredVersion = SemVersion.Parse("10.0.100", SemVersionStyles.Strict);
var requiredVersionString = "10.0.100";

// Use reflection to access the private method
var method = typeof(DotNetSdkInstaller).GetMethod("MeetsMinimumRequirement",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
var result = (bool)method!.Invoke(null, new object[] { installedVersion, requiredVersion, requiredVersionString })!;

Assert.True(result);
}

[Fact]
public void MeetsMinimumRequirement_RejectsDotNet9_ForSingleFileAppHost()
{
// Test that .NET 9 is still rejected for single file apphost requirements
var installedVersion = SemVersion.Parse("9.0.999", SemVersionStyles.Strict);
var requiredVersion = SemVersion.Parse("10.0.100", SemVersionStyles.Strict);
var requiredVersionString = "10.0.100";

// Use reflection to access the private method
var method = typeof(DotNetSdkInstaller).GetMethod("MeetsMinimumRequirement",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
var result = (bool)method!.Invoke(null, new object[] { installedVersion, requiredVersion, requiredVersionString })!;

Assert.False(result);
}

[Fact]
public void MeetsMinimumRequirement_UsesStrictComparison_ForNonSingleFileAppHost()
{
// Test that other version requirements still use strict comparison
var installedVersion = SemVersion.Parse("9.0.301", SemVersionStyles.Strict);
var requiredVersion = SemVersion.Parse("9.0.302", SemVersionStyles.Strict);
var requiredVersionString = "9.0.302";

// Use reflection to access the private method
var method = typeof(DotNetSdkInstaller).GetMethod("MeetsMinimumRequirement",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
var result = (bool)method!.Invoke(null, new object[] { installedVersion, requiredVersion, requiredVersionString })!;

Assert.False(result);
}

private static IConfiguration CreateEmptyConfiguration()
{
return new ConfigurationBuilder().Build();
Expand Down
Loading