chore: Fixes Build Tool .NET SDK Download (#2395)

This commit is contained in:
Kamron Batman 2026-03-28 22:33:02 -07:00 committed by GitHub
parent 26c1e399ba
commit 6f80a91fbf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 92 additions and 80 deletions

View file

@ -9,18 +9,25 @@ namespace BuildTool.Prerequisites;
public static partial class DotNetSdkManager
{
private const string DotNetDownloadUrl = "https://dotnet.microsoft.com/download";
private const string InstallScriptUrlWindows = "https://dot.net/v1/dotnet-install.ps1";
private const string InstallScriptUrlUnix = "https://dot.net/v1/dotnet-install.sh";
private const string DotNetLinuxInstallUrl = "https://learn.microsoft.com/en-us/dotnet/core/install/linux";
/// <summary>
/// Returns the .NET download page URL for the required SDK's major.minor channel.
/// </summary>
private static string GetDownloadUrl(Version version) =>
$"https://dotnet.microsoft.com/download/dotnet/{version.Major}.{version.Minor}";
/// <summary>
/// Returns the direct installer download URL for a specific SDK version + platform.
/// Windows uses .exe, macOS uses .pkg.
/// </summary>
private static string GetInstallerUrl(Version version, string rid, string extension) =>
$"https://builds.dotnet.microsoft.com/dotnet/Sdk/{version}/dotnet-sdk-{version}-{rid}.{extension}";
public static PrerequisiteResult CheckSdk(string repoRoot)
{
var requiredVersionStr = ReadRequiredVersion(repoRoot) ?? "10.0.201";
if (!Version.TryParse(requiredVersionStr, out var requiredVersion))
{
requiredVersion = new Version(10, 0, 201);
}
var requiredVersion = ReadRequiredVersion(repoRoot);
var downloadUrl = GetDownloadUrl(requiredVersion);
// Check if dotnet is on PATH
var result = ProcessRunner.RunCaptured("dotnet", "--list-sdks");
@ -30,8 +37,8 @@ public static partial class DotNetSdkManager
{
Name = ".NET SDK",
Passed = false,
Details = $".NET SDK {requiredVersionStr}+ is not installed",
DownloadUrl = DotNetDownloadUrl
Details = $".NET SDK {requiredVersion}+ is not installed",
DownloadUrl = downloadUrl
};
}
@ -67,9 +74,9 @@ public static partial class DotNetSdkManager
Name = ".NET SDK",
Passed = false,
Details = bestVersionStr is not null
? $".NET SDK {bestVersionStr} found, but {requiredVersionStr}+ is required"
: $".NET SDK {requiredVersionStr}+ is required but no SDK was found",
DownloadUrl = DotNetDownloadUrl
? $".NET SDK {bestVersionStr} found, but {requiredVersion}+ is required"
: $".NET SDK {requiredVersion}+ is required but no SDK was found",
DownloadUrl = downloadUrl
};
}
@ -81,108 +88,113 @@ public static partial class DotNetSdkManager
};
}
public static bool OfferInstall(PlatformInfo platform, bool interactive)
public static bool OfferInstall(PlatformInfo platform, string repoRoot, bool interactive)
{
var requiredVersion = ReadRequiredVersion(repoRoot);
var downloadUrl = GetDownloadUrl(requiredVersion);
if (!interactive)
{
AnsiConsole.MarkupLine("[red]Error:[/] .NET SDK is not installed. Download from:");
AnsiConsole.MarkupLine($"[link]{DotNetDownloadUrl}[/]");
AnsiConsole.MarkupLine("[red]Error:[/] .NET SDK is not installed.");
AnsiConsole.MarkupLine($" Download from: [link]{downloadUrl}[/]");
return false;
}
if (platform.IsWindows)
{
return OfferWindowsInstall(platform, requiredVersion, downloadUrl);
}
if (platform.IsMacOS)
{
return OfferMacOsInstall(platform, requiredVersion, downloadUrl);
}
// Linux — just show instructions
AnsiConsole.MarkupLine($"\n[yellow]Install the .NET SDK using your package manager:[/]");
AnsiConsole.MarkupLine($" [link]{DotNetLinuxInstallUrl}[/]");
AnsiConsole.WriteLine();
return false;
}
private static bool OfferWindowsInstall(PlatformInfo platform, Version requiredVersion, string downloadUrl)
{
var rid = $"win-{platform.ArchRid}";
var installerUrl = GetInstallerUrl(requiredVersion, rid, "exe");
var install = AnsiConsole.Confirm(
"[yellow].NET SDK is required but not found.[/] Would you like to install it now?",
"[yellow].NET SDK is required. Download and run the installer?[/]",
defaultValue: true
);
if (!install)
{
AnsiConsole.MarkupLine($"\nDownload the .NET SDK from: [link]{DotNetDownloadUrl}[/]");
AnsiConsole.MarkupLine($"\n Download manually from: [link]{downloadUrl}[/]");
return false;
}
return RunInstallScript(platform);
}
private static bool RunInstallScript(PlatformInfo platform)
{
AnsiConsole.MarkupLine("\n[blue]Installing .NET SDK...[/]");
if (platform.IsWindows)
{
return RunWindowsInstall();
}
return RunUnixInstall();
}
private static bool RunWindowsInstall()
{
// Download and run the official Microsoft install script
var result = ProcessRunner.RunPassthrough(
"powershell",
$"-NoProfile -ExecutionPolicy Bypass -Command \"& {{ " +
$"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; " +
$"$script = Invoke-WebRequest -Uri '{InstallScriptUrlWindows}' -UseBasicParsing; " +
$"$scriptBlock = [scriptblock]::Create($script.Content); " +
$"& $scriptBlock -Channel 10.0 }}\""
);
AnsiConsole.MarkupLine("\n[blue]Opening .NET SDK installer...[/]");
var result = ProcessRunner.RunPassthrough("cmd", $"/c start \"\" \"{installerUrl}\"");
if (result != 0)
{
AnsiConsole.MarkupLine("[red]Failed to install .NET SDK.[/]");
AnsiConsole.MarkupLine($"Please install manually from: [link]{DotNetDownloadUrl}[/]");
AnsiConsole.MarkupLine("[red]Failed to open installer.[/]");
AnsiConsole.MarkupLine($" Download manually from: [link]{downloadUrl}[/]");
return false;
}
AnsiConsole.MarkupLine("[green]Successfully installed .NET SDK.[/]");
AnsiConsole.MarkupLine("[yellow]Note:[/] You may need to restart your terminal for the PATH to update.");
return true;
AnsiConsole.MarkupLine("[yellow]Install the SDK, then restart your terminal and run this tool again.[/]");
return false; // User needs to restart after installing
}
private static bool RunUnixInstall()
private static bool OfferMacOsInstall(PlatformInfo platform, Version requiredVersion, string downloadUrl)
{
// Download and run the official Microsoft install script
var result = ProcessRunner.RunPassthrough(
"bash",
$"-c \"curl -fsSL {InstallScriptUrlUnix} | bash -s -- --channel 10.0\""
var rid = $"osx-{platform.ArchRid}";
var installerUrl = GetInstallerUrl(requiredVersion, rid, "pkg");
var install = AnsiConsole.Confirm(
"[yellow].NET SDK is required. Download and run the installer?[/]",
defaultValue: true
);
if (result != 0)
if (!install)
{
AnsiConsole.MarkupLine("[red]Failed to install .NET SDK.[/]");
AnsiConsole.MarkupLine($"Please install manually from: [link]{DotNetDownloadUrl}[/]");
AnsiConsole.MarkupLine($"\n Download manually from: [link]{downloadUrl}[/]");
return false;
}
AnsiConsole.MarkupLine("[green]Successfully installed .NET SDK.[/]");
AnsiConsole.MarkupLine("[yellow]Note:[/] You may need to add ~/.dotnet to your PATH or restart your terminal.");
return true;
AnsiConsole.MarkupLine("\n[blue]Opening .NET SDK installer...[/]");
var result = ProcessRunner.RunPassthrough("open", installerUrl);
if (result != 0)
{
AnsiConsole.MarkupLine("[red]Failed to open installer.[/]");
AnsiConsole.MarkupLine($" Download manually from: [link]{downloadUrl}[/]");
return false;
}
AnsiConsole.MarkupLine("[yellow]Install the SDK, then restart your terminal and run this tool again.[/]");
return false; // User needs to restart after installing
}
private static string? ReadRequiredVersion(string repoRoot)
/// <summary>
/// Reads the required SDK version from global.json.
/// Falls back to 10.0 if global.json is missing or unparseable.
/// </summary>
private static Version ReadRequiredVersion(string repoRoot)
{
var globalJsonPath = Path.Combine(repoRoot, "global.json");
if (!File.Exists(globalJsonPath))
if (File.Exists(globalJsonPath))
{
return null;
var json = File.ReadAllText(globalJsonPath);
var globalJson = JsonSerializer.Deserialize(json, GlobalJsonContext.Default.GlobalJson);
if (globalJson?.Sdk?.Version is { } versionStr && Version.TryParse(versionStr, out var version))
{
return version;
}
}
var json = File.ReadAllText(globalJsonPath);
var globalJson = JsonSerializer.Deserialize(json, GlobalJsonContext.Default.GlobalJson);
return globalJson?.Sdk?.Version;
}
// Kept for reference but no longer used — Version.TryParse handles comparison now
private static int ParseMajorVersion(string version)
{
var dotIndex = version.IndexOf('.');
if (dotIndex <= 0)
{
return 0;
}
return int.TryParse(version.AsSpan(0, dotIndex), out var major) ? major : 0;
return new Version(10, 0);
}
[GeneratedRegex(@"^(\d+\.\d+\.\d+\S*)")]

View file

@ -69,7 +69,7 @@ public static class PrerequisiteChecker
if (!sdkResult.Passed)
{
var installed = DotNetSdkManager.OfferInstall(platform, interactive);
var installed = DotNetSdkManager.OfferInstall(platform, repoRoot, interactive);
if (!installed)
{
return false;