diff --git a/Directory.Packages.props b/Directory.Packages.props index 2fc29d442726..a6af2f4d1bbe 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -114,7 +114,7 @@ - + diff --git a/dnup.slnf b/dnup.slnf index 8384b13f95f0..ba550e59653e 100644 --- a/dnup.slnf +++ b/dnup.slnf @@ -5,7 +5,7 @@ "src\\Installer\\dnup\\dnup.csproj", "src\\Installer\\Microsoft.Dotnet.Installation\\Microsoft.Dotnet.Installation.csproj", "test\\dnup.Tests\\dnup.Tests.csproj", - "src\\Resolvers\\Microsoft.DotNet.NativeWrapper\\Microsoft.DotNet.NativeWrapper.csproj + "src\\Resolvers\\Microsoft.DotNet.NativeWrapper\\Microsoft.DotNet.NativeWrapper.csproj" ] } } diff --git a/src/Installer/Microsoft.Dotnet.Installation/IDotnetReleaseInfoProvider.cs b/src/Installer/Microsoft.Dotnet.Installation/IDotnetReleaseInfoProvider.cs index 92571c2e4c8f..ff48a53e5660 100644 --- a/src/Installer/Microsoft.Dotnet.Installation/IDotnetReleaseInfoProvider.cs +++ b/src/Installer/Microsoft.Dotnet.Installation/IDotnetReleaseInfoProvider.cs @@ -10,7 +10,7 @@ namespace Microsoft.Dotnet.Installation; public interface IDotnetReleaseInfoProvider { - IEnumerable GetAvailableChannels(); + IEnumerable GetSupportedChannels(); ReleaseVersion? GetLatestVersion(InstallComponent component, string channel); diff --git a/src/Installer/Microsoft.Dotnet.Installation/Internal/DotnetReleaseInfoProvider.cs b/src/Installer/Microsoft.Dotnet.Installation/Internal/DotnetReleaseInfoProvider.cs index 168bcb95f063..17d444209285 100644 --- a/src/Installer/Microsoft.Dotnet.Installation/Internal/DotnetReleaseInfoProvider.cs +++ b/src/Installer/Microsoft.Dotnet.Installation/Internal/DotnetReleaseInfoProvider.cs @@ -10,7 +10,11 @@ namespace Microsoft.Dotnet.Installation.Internal; internal class DotnetReleaseInfoProvider : IDotnetReleaseInfoProvider { - public IEnumerable GetAvailableChannels() => throw new NotImplementedException(); + public IEnumerable GetSupportedChannels() + { + var releaseManifest = new ReleaseManifest(); + return releaseManifest.GetSupportedChannels(); + } public ReleaseVersion? GetLatestVersion(InstallComponent component, string channel) { var releaseManifest = new ReleaseManifest(); diff --git a/src/Installer/Microsoft.Dotnet.Installation/Internal/ReleaseManifest.cs b/src/Installer/Microsoft.Dotnet.Installation/Internal/ReleaseManifest.cs index 243e49da0766..85de5895bc2e 100644 --- a/src/Installer/Microsoft.Dotnet.Installation/Internal/ReleaseManifest.cs +++ b/src/Installer/Microsoft.Dotnet.Installation/Internal/ReleaseManifest.cs @@ -7,6 +7,7 @@ using System.Net; using System.Net.Http; using System.Net.Http.Headers; +using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text; using System.Threading; @@ -53,6 +54,31 @@ internal class ReleaseManifest(HttpClient httpClient) : IDisposable return (major, minor, featureBand, isFullySpecified); } + public IEnumerable GetSupportedChannels() + { + + return ["latest", "preview", "lts", "sts", + ..GetProductCollection() + .Where(p => p.IsSupported) + .OrderByDescending(p => p.LatestReleaseVersion) + .SelectMany(GetChannelsForProduct) + ]; + + static IEnumerable GetChannelsForProduct(Product product) + { + return [product.ProductVersion, + ..product.GetReleasesAsync().GetAwaiter().GetResult() + .SelectMany(r => r.Sdks) + .Select(sdk => sdk.Version) + .OrderByDescending(v => v) + .Select(v => $"{v.Major}.{v.Minor}.{(v.Patch / 100)}xx") + .Distinct() + .ToList() + ]; + } + + } + /// /// Finds the latest fully specified version for a given channel string (major, major.minor, or feature band). /// @@ -64,17 +90,17 @@ internal class ReleaseManifest(HttpClient httpClient) : IDisposable if (string.Equals(channel.Name, "lts", StringComparison.OrdinalIgnoreCase) || string.Equals(channel.Name, "sts", StringComparison.OrdinalIgnoreCase)) { var releaseType = string.Equals(channel.Name, "lts", StringComparison.OrdinalIgnoreCase) ? ReleaseType.LTS : ReleaseType.STS; - var productIndex = ProductCollection.GetAsync().GetAwaiter().GetResult(); + var productIndex = GetProductCollection(); return GetLatestVersionByReleaseType(productIndex, releaseType, component); } else if (string.Equals(channel.Name, "preview", StringComparison.OrdinalIgnoreCase)) { - var productIndex = ProductCollection.GetAsync().GetAwaiter().GetResult(); + var productIndex = GetProductCollection(); return GetLatestPreviewVersion(productIndex, component); } else if (string.Equals(channel.Name, "latest", StringComparison.OrdinalIgnoreCase)) { - var productIndex = ProductCollection.GetAsync().GetAwaiter().GetResult(); + var productIndex = GetProductCollection(); return GetLatestActiveVersion(productIndex, component); } @@ -93,7 +119,7 @@ internal class ReleaseManifest(HttpClient httpClient) : IDisposable } // Load the index manifest - var index = ProductCollection.GetAsync().GetAwaiter().GetResult(); + var index = GetProductCollection(); if (minor < 0) { return GetLatestVersionForMajorOrMajorMinor(index, major, component); // Major Only (e.g., "9") diff --git a/src/Installer/dnup/Commands/Sdk/Install/EnvironmentVariableMockDotnetInstaller.cs b/src/Installer/dnup/Commands/Sdk/Install/EnvironmentVariableMockDotnetInstaller.cs deleted file mode 100644 index 37fc7435c741..000000000000 --- a/src/Installer/dnup/Commands/Sdk/Install/EnvironmentVariableMockDotnetInstaller.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Threading; -using Microsoft.Dotnet.Installation.Internal; -using Microsoft.DotNet.Tools.Bootstrapper.Commands.Sdk.Install; -using Spectre.Console; - -using SpectreAnsiConsole = Spectre.Console.AnsiConsole; - -namespace Microsoft.DotNet.Tools.Bootstrapper.Commands.Sdk.Install -{ - internal class EnvironmentVariableMockDotnetInstaller : IDotnetInstallManager - { - public GlobalJsonInfo GetGlobalJsonInfo(string initialDirectory) - { - return new GlobalJsonInfo - { - GlobalJsonPath = Environment.GetEnvironmentVariable("DOTNET_TESTHOOK_GLOBALJSON_PATH"), - GlobalJsonContents = null // Set to null for test mock; update as needed for tests - }; - } - - public string GetDefaultDotnetInstallPath() - { - return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "dotnet"); - } - - public DotnetInstallRootConfiguration? GetConfiguredInstallType() - { - var testHookDefaultInstall = Environment.GetEnvironmentVariable("DOTNET_TESTHOOK_DEFAULT_INSTALL"); - InstallType installtype; - if (!Enum.TryParse(testHookDefaultInstall, out installtype)) - { - return null; - } - var installPath = Environment.GetEnvironmentVariable("DOTNET_TESTHOOK_CURRENT_INSTALL_PATH") ?? GetDefaultDotnetInstallPath(); - return new(new(installPath, InstallerUtilities.GetDefaultInstallArchitecture()), installtype, true, true); - } - - public string? GetLatestInstalledAdminVersion() - { - var latestAdminVersion = Environment.GetEnvironmentVariable("DOTNET_TESTHOOK_LATEST_ADMIN_VERSION"); - if (string.IsNullOrEmpty(latestAdminVersion)) - { - latestAdminVersion = "10.0.0-preview.7"; - } - return latestAdminVersion; - } - - public void InstallSdks(DotnetInstallRoot dotnetRoot, ProgressContext progressContext, IEnumerable sdkVersions) - { - using (var httpClient = new HttpClient()) - { - List downloads = sdkVersions.Select(version => - { - string downloadLink = "https://builds.dotnet.microsoft.com/dotnet/Sdk/9.0.303/dotnet-sdk-9.0.303-win-x64.exe"; - var task = progressContext.AddTask($"Downloading .NET SDK {version}"); - return (Action)(() => - { - Download(downloadLink, httpClient, task); - }); - }).ToList(); - - foreach (var download in downloads) - { - download(); - } - } - } - - void Download(string url, HttpClient httpClient, ProgressTask task) - { - for (int i = 0; i < 100; i++) - { - task.Increment(1); - Thread.Sleep(20); // Simulate some work - } - task.Value = 100; - } - - public void UpdateGlobalJson(string globalJsonPath, string? sdkVersion = null, bool? allowPrerelease = null, string? rollForward = null) - { - SpectreAnsiConsole.WriteLine($"Updating {globalJsonPath} to SDK version {sdkVersion} (AllowPrerelease={allowPrerelease}, RollForward={rollForward})"); - } - public void ConfigureInstallType(InstallType installType, string? dotnetRoot = null) - { - SpectreAnsiConsole.WriteLine($"Configuring install type to {installType} (dotnetRoot={dotnetRoot})"); - } - } -} diff --git a/src/Installer/dnup/Commands/Sdk/Install/EnvironmentVariableMockReleaseInfoProvider.cs b/src/Installer/dnup/Commands/Sdk/Install/EnvironmentVariableMockReleaseInfoProvider.cs deleted file mode 100644 index 78a2b9a819dd..000000000000 --- a/src/Installer/dnup/Commands/Sdk/Install/EnvironmentVariableMockReleaseInfoProvider.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Deployment.DotNet.Releases; -using Microsoft.DotNet.Tools.Bootstrapper.Commands.Sdk.Install; - -namespace Microsoft.DotNet.Tools.Bootstrapper.Commands.Sdk.Install -{ - internal class EnvironmentVariableMockReleaseInfoProvider : IDotnetReleaseInfoProvider - { - IEnumerable IDotnetReleaseInfoProvider.GetAvailableChannels() - { - var channels = Environment.GetEnvironmentVariable("DOTNET_TESTHOOK_AVAILABLE_CHANNELS"); - if (string.IsNullOrEmpty(channels)) - { - return new List { "latest", "preview", "10", "10.0.1xx", "10.0.2xx", "9", "9.0.3xx", "9.0.2xx", "9.0.1xx" }; - } - return channels.Split(',').ToList(); - } - public ReleaseVersion GetLatestVersion(InstallComponent component, string channel) - { - if (component != InstallComponent.SDK) - { - throw new NotImplementedException("Only SDK component is supported in this mock provider"); - } - - string version; - if (channel == "preview") - { - version = "11.0.100-preview.1.42424"; - } - else if (channel == "latest" || channel == "10" || channel == "10.0.2xx") - { - version = "10.0.0-preview.7"; - } - else if (channel == "10.0.1xx") - { - version = "10.0.106"; - } - else if (channel == "9" || channel == "9.0.3xx") - { - version = "9.0.309"; - } - else if (channel == "9.0.2xx") - { - version = "9.0.212"; - } - else if (channel == "9.0.1xx") - { - version = "9.0.115"; - } - - version = channel; - - return new ReleaseVersion(version); - } - - public SupportType GetSupportType(InstallComponent component, ReleaseVersion version) => throw new NotImplementedException(); - - } -} diff --git a/src/Installer/dnup/Commands/Sdk/Install/SdkInstallCommand.cs b/src/Installer/dnup/Commands/Sdk/Install/SdkInstallCommand.cs index 89957ad9106d..af4493136621 100644 --- a/src/Installer/dnup/Commands/Sdk/Install/SdkInstallCommand.cs +++ b/src/Installer/dnup/Commands/Sdk/Install/SdkInstallCommand.cs @@ -23,7 +23,7 @@ internal class SdkInstallCommand(ParseResult result) : CommandBase(result) private readonly bool _noProgress = result.GetValue(SdkInstallCommandParser.NoProgressOption); private readonly IDotnetInstallManager _dotnetInstaller = new DotnetInstallManager(); - private readonly IDotnetReleaseInfoProvider _releaseInfoProvider = new EnvironmentVariableMockReleaseInfoProvider(); + private readonly IDotnetReleaseInfoProvider _releaseInfoProvider = new DotnetReleaseInfoProvider(); private readonly ManifestChannelVersionResolver _channelVersionResolver = new ManifestChannelVersionResolver(); public override int Execute() @@ -113,7 +113,7 @@ public override int Execute() if (_interactive) { - SpectreAnsiConsole.WriteLine("Available supported channels: " + string.Join(' ', _releaseInfoProvider.GetAvailableChannels())); + SpectreAnsiConsole.WriteLine("Available supported channels: " + string.Join(' ', _releaseInfoProvider.GetSupportedChannels())); SpectreAnsiConsole.WriteLine("You can also specify a specific version (for example 9.0.304)."); resolvedChannel = SpectreAnsiConsole.Prompt( diff --git a/src/Installer/dnup/IDotnetInstallManager.cs b/src/Installer/dnup/IDotnetInstallManager.cs index 4b8c742dbb7c..b665d056d57f 100644 --- a/src/Installer/dnup/IDotnetInstallManager.cs +++ b/src/Installer/dnup/IDotnetInstallManager.cs @@ -43,7 +43,14 @@ public class GlobalJsonInfo public string? SdkVersion => GlobalJsonContents?.Sdk?.Version; public bool? AllowPrerelease => GlobalJsonContents?.Sdk?.AllowPrerelease; public string? RollForward => GlobalJsonContents?.Sdk?.RollForward; - public string? SdkPath => (GlobalJsonContents?.Sdk?.Paths is not null && GlobalJsonContents.Sdk.Paths.Length > 0) ? GlobalJsonContents.Sdk.Paths[0] : null; + public string? SdkPath + { + get + { + return (GlobalJsonContents?.Sdk?.Paths is not null && GlobalJsonContents.Sdk.Paths.Length > 0) ? + Path.GetFullPath(GlobalJsonContents.Sdk.Paths[0], GlobalJsonPath!) : null; + } + } } public record DotnetInstallRootConfiguration( diff --git a/src/Installer/dnup/dnup.csproj b/src/Installer/dnup/dnup.csproj index a272529118cb..52537eba9131 100644 --- a/src/Installer/dnup/dnup.csproj +++ b/src/Installer/dnup/dnup.csproj @@ -6,6 +6,7 @@ enable enable true + false $(NoWarn);CS8002 diff --git a/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs b/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs index 678577e9053f..6c900028af10 100644 --- a/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs +++ b/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs @@ -33,7 +33,7 @@ static Interop() // construction so that subsequent P/Invokes can find it. private static void PreloadWindowsLibrary(string dllFileName) { - string? basePath = Path.GetDirectoryName(typeof(Interop).Assembly.Location); + string? basePath = AppContext.BaseDirectory; string architecture = RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(); string dllPath = Path.Combine(basePath ?? string.Empty, architecture, $"{dllFileName}.dll"); diff --git a/src/Resolvers/Microsoft.DotNet.NativeWrapper/Microsoft.DotNet.NativeWrapper.csproj b/src/Resolvers/Microsoft.DotNet.NativeWrapper/Microsoft.DotNet.NativeWrapper.csproj index f39d5b24e2c2..b700520b359b 100644 --- a/src/Resolvers/Microsoft.DotNet.NativeWrapper/Microsoft.DotNet.NativeWrapper.csproj +++ b/src/Resolvers/Microsoft.DotNet.NativeWrapper/Microsoft.DotNet.NativeWrapper.csproj @@ -2,6 +2,7 @@ $(ResolverTargetFramework);net472 + true diff --git a/src/Resolvers/Microsoft.DotNet.NativeWrapper/NETEnvironmentInfo.cs b/src/Resolvers/Microsoft.DotNet.NativeWrapper/NETEnvironmentInfo.cs index e294a7d59b55..90045bdf6ab0 100644 --- a/src/Resolvers/Microsoft.DotNet.NativeWrapper/NETEnvironmentInfo.cs +++ b/src/Resolvers/Microsoft.DotNet.NativeWrapper/NETEnvironmentInfo.cs @@ -66,7 +66,7 @@ internal void Initialize(IntPtr info, IntPtr resultContext) var runtimes = new hostfxr_dotnet_environment_framework_info[infoStruct.framework_count]; for (var i = 0; i < (int)infoStruct.framework_count; i++) { - var pointer = new IntPtr(infoStruct.frameworks.ToInt64() + i * Marshal.SizeOf(typeof(hostfxr_dotnet_environment_framework_info))); + var pointer = new IntPtr(infoStruct.frameworks.ToInt64() + i * Marshal.SizeOf()); runtimes[i] = Marshal.PtrToStructure(pointer); } RuntimeInfo = runtimes.Select(runtime => new NetRuntimeInfo(runtime.name, runtime.version, runtime.path)); @@ -74,7 +74,7 @@ internal void Initialize(IntPtr info, IntPtr resultContext) var sdks = new hostfxr_dotnet_environment_sdk_info[infoStruct.sdk_count]; for (var i = 0; i < (int)infoStruct.sdk_count; i++) { - var pointer = new IntPtr(infoStruct.sdks.ToInt64() + i * Marshal.SizeOf(typeof(hostfxr_dotnet_environment_sdk_info))); + var pointer = new IntPtr(infoStruct.sdks.ToInt64() + i * Marshal.SizeOf()); sdks[i] = Marshal.PtrToStructure(pointer); } SdkInfo = sdks.Select(sdk => new NetSdkInfo(sdk.version, sdk.path)); diff --git a/test/UnitTests.proj b/test/UnitTests.proj index 6c396845af1d..e8e0f8969d56 100644 --- a/test/UnitTests.proj +++ b/test/UnitTests.proj @@ -16,7 +16,7 @@ - + net472 diff --git a/test/dnup.Tests/LibraryTests.cs b/test/dnup.Tests/LibraryTests.cs index 217116c0c1eb..098df5619c4c 100644 --- a/test/dnup.Tests/LibraryTests.cs +++ b/test/dnup.Tests/LibraryTests.cs @@ -22,6 +22,9 @@ public LibraryTests(ITestOutputHelper log) [Theory] [InlineData("9")] [InlineData("latest")] + [InlineData("sts")] + [InlineData("lts")] + [InlineData("preview")] public void LatestVersionForChannelCanBeInstalled(string channel) { var releaseInfoProvider = InstallerFactory.CreateReleaseInfoProvider(); @@ -40,4 +43,21 @@ public void LatestVersionForChannelCanBeInstalled(string channel) InstallComponent.SDK, latestVersion!); } + + [Fact] + public void TestGetSupportedChannels() + { + var releaseInfoProvider = InstallerFactory.CreateReleaseInfoProvider(); + var channels = releaseInfoProvider.GetSupportedChannels(); + + channels.Should().Contain(new[] { "latest", "lts", "sts", "preview" }); + + // This will need to be updated every few years as versions go out of support + channels.Should().Contain(new[] { "10.0", "10.0.1xx" }); + channels.Should().NotContain("10"); + + channels.Should().NotContain("7.0"); + channels.Should().NotContain("7.0.1xx"); + + } }