diff --git a/dnup.slnf b/dnup.slnf index d68938fd2ae0..b38b36feec3d 100644 --- a/dnup.slnf +++ b/dnup.slnf @@ -3,6 +3,7 @@ "path": "sdk.slnx", "projects": [ "src\\Installer\\dnup\\dnup.csproj", + "src\\Installer\\Microsoft.Dotnet.Installation\\Microsoft.Dotnet.Installation.csproj", "test\\dnup.Tests\\dnup.Tests.csproj", ] } diff --git a/sdk.slnx b/sdk.slnx index 2ea3064bed5f..62e668dc5c4f 100644 --- a/sdk.slnx +++ b/sdk.slnx @@ -87,6 +87,7 @@ + @@ -289,12 +290,12 @@ + - diff --git a/src/Installer/Microsoft.Dotnet.Installation/DotnetInstallRoot.cs b/src/Installer/Microsoft.Dotnet.Installation/DotnetInstallRoot.cs new file mode 100644 index 000000000000..0deeb807808d --- /dev/null +++ b/src/Installer/Microsoft.Dotnet.Installation/DotnetInstallRoot.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Dotnet.Installation; +public record DotnetInstallRoot( + string Path, + InstallArchitecture Architecture); diff --git a/src/Installer/Microsoft.Dotnet.Installation/IDotnetInstallDiscoverer.cs b/src/Installer/Microsoft.Dotnet.Installation/IDotnetInstallDiscoverer.cs new file mode 100644 index 000000000000..64c1e859f6ce --- /dev/null +++ b/src/Installer/Microsoft.Dotnet.Installation/IDotnetInstallDiscoverer.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Deployment.DotNet.Releases; + +namespace Microsoft.Dotnet.Installation; + +public interface IDotnetInstallDiscoverer +{ + DotnetInstallRoot GetDotnetInstallRootFromPath(); + + IEnumerable GetInstalledVersions(DotnetInstallRoot dotnetRoot, InstallComponent component); +} diff --git a/src/Installer/Microsoft.Dotnet.Installation/IDotnetInstaller.cs b/src/Installer/Microsoft.Dotnet.Installation/IDotnetInstaller.cs new file mode 100644 index 000000000000..04eff3cf325a --- /dev/null +++ b/src/Installer/Microsoft.Dotnet.Installation/IDotnetInstaller.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Deployment.DotNet.Releases; + +namespace Microsoft.Dotnet.Installation; + +public interface IDotnetInstaller +{ + void Install(DotnetInstallRoot dotnetRoot, InstallComponent component, ReleaseVersion version); + void Uninstall(DotnetInstallRoot dotnetRoot, InstallComponent component, ReleaseVersion version); +} diff --git a/src/Installer/Microsoft.Dotnet.Installation/IDotnetReleaseInfoProvider.cs b/src/Installer/Microsoft.Dotnet.Installation/IDotnetReleaseInfoProvider.cs new file mode 100644 index 000000000000..3f5e2e4d95a3 --- /dev/null +++ b/src/Installer/Microsoft.Dotnet.Installation/IDotnetReleaseInfoProvider.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Deployment.DotNet.Releases; + +namespace Microsoft.Dotnet.Installation; + +public interface IDotnetReleaseInfoProvider +{ + IEnumerable GetAvailableChannels(); + + ReleaseVersion GetLatestVersion(InstallComponent component, string channel); + + // Get all versions in a channel - do we have a scenario for this? + //IEnumerable GetAllVersions(InstallComponent component, string channel); + + SupportType GetSupportType(InstallComponent component, ReleaseVersion version); +} + +public enum SupportType +{ + OutOfSupport, + LongTermSupport, + StandardTermSupport +} diff --git a/src/Installer/dnup/InstallArchitecture.cs b/src/Installer/Microsoft.Dotnet.Installation/InstallArchitecture.cs similarity index 61% rename from src/Installer/dnup/InstallArchitecture.cs rename to src/Installer/Microsoft.Dotnet.Installation/InstallArchitecture.cs index a046bf3d1721..ddc1b5b93d0e 100644 --- a/src/Installer/dnup/InstallArchitecture.cs +++ b/src/Installer/Microsoft.Dotnet.Installation/InstallArchitecture.cs @@ -5,12 +5,11 @@ using System.Collections.Generic; using System.Text; -namespace Microsoft.DotNet.Tools.Bootstrapper +namespace Microsoft.Dotnet.Installation; + +public enum InstallArchitecture { - public enum InstallArchitecture - { - x86, - x64, - arm64 - } + x86, + x64, + arm64 } diff --git a/src/Installer/Microsoft.Dotnet.Installation/InstallComponent.cs b/src/Installer/Microsoft.Dotnet.Installation/InstallComponent.cs new file mode 100644 index 000000000000..1cecbe90e59d --- /dev/null +++ b/src/Installer/Microsoft.Dotnet.Installation/InstallComponent.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Dotnet.Installation; + +public enum InstallComponent +{ + SDK, + Runtime, + ASPNETCore, + WindowsDesktop +} diff --git a/src/Installer/Microsoft.Dotnet.Installation/Microsoft.Dotnet.Installation.csproj b/src/Installer/Microsoft.Dotnet.Installation/Microsoft.Dotnet.Installation.csproj new file mode 100644 index 000000000000..4b8baf27e21d --- /dev/null +++ b/src/Installer/Microsoft.Dotnet.Installation/Microsoft.Dotnet.Installation.csproj @@ -0,0 +1,14 @@ + + + + net10.0 + enable + enable + true + + + + + + + diff --git a/src/Installer/dnup/BootstrapperController.cs b/src/Installer/dnup/BootstrapperController.cs index 3e7a6b1e888a..ffc4ddc7f030 100644 --- a/src/Installer/dnup/BootstrapperController.cs +++ b/src/Installer/dnup/BootstrapperController.cs @@ -19,13 +19,13 @@ public BootstrapperController(IEnvironmentProvider? environmentProvider = null) _environmentProvider = environmentProvider ?? new EnvironmentProvider(); } - public DotnetInstallRoot GetConfiguredInstallType() + public DotnetInstallRootConfiguration? GetConfiguredInstallType() { string? foundDotnet = _environmentProvider.GetCommandPath("dotnet"); if (string.IsNullOrEmpty(foundDotnet)) { - return new(null, InstallType.None, DnupUtilities.GetDefaultInstallArchitecture()); + return null; } string installDir = Path.GetDirectoryName(foundDotnet)!; @@ -36,27 +36,12 @@ public DotnetInstallRoot GetConfiguredInstallType() string programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); bool isAdminInstall = installDir.StartsWith(Path.Combine(programFiles, "dotnet"), StringComparison.OrdinalIgnoreCase) || installDir.StartsWith(Path.Combine(programFilesX86, "dotnet"), StringComparison.OrdinalIgnoreCase); - - if (isAdminInstall) - { - // Admin install: DOTNET_ROOT should not be set, or if set, should match installDir - if (!string.IsNullOrEmpty(dotnetRoot) && !DnupUtilities.PathsEqual(dotnetRoot, installDir) && - !dotnetRoot.StartsWith(Path.Combine(programFiles, "dotnet"), StringComparison.OrdinalIgnoreCase) && - !dotnetRoot.StartsWith(Path.Combine(programFilesX86, "dotnet"), StringComparison.OrdinalIgnoreCase)) - { - return new(installDir, InstallType.Inconsistent, DnupUtilities.GetDefaultInstallArchitecture()); - } - return new(installDir, InstallType.Admin, DnupUtilities.GetDefaultInstallArchitecture()); - } - else - { - // User install: DOTNET_ROOT must be set and match installDir - if (string.IsNullOrEmpty(dotnetRoot) || !DnupUtilities.PathsEqual(dotnetRoot, installDir)) - { - return new(installDir, InstallType.Inconsistent, DnupUtilities.GetDefaultInstallArchitecture()); - } - return new(installDir, InstallType.User, DnupUtilities.GetDefaultInstallArchitecture()); - } + + var installRoot = new DotnetInstallRoot(installDir, DnupUtilities.GetDefaultInstallArchitecture()); + + bool isSetAsDotnetRoot = DnupUtilities.PathsEqual(dotnetRoot, installDir); + + return new(installRoot, isAdminInstall ? InstallType.Admin : InstallType.User, IsOnPath: true, isSetAsDotnetRoot); } public string GetDefaultDotnetInstallPath() @@ -153,10 +138,6 @@ public void ConfigureInstallType(InstallType installType, string? dotnetRoot = n // Unset DOTNET_ROOT Environment.SetEnvironmentVariable("DOTNET_ROOT", null, EnvironmentVariableTarget.User); break; - case InstallType.None: - // Unset DOTNET_ROOT - Environment.SetEnvironmentVariable("DOTNET_ROOT", null, EnvironmentVariableTarget.User); - break; default: throw new ArgumentException($"Unknown install type: {installType}", nameof(installType)); } diff --git a/src/Installer/dnup/Commands/Sdk/Install/EnvironmentVariableMockDotnetInstaller.cs b/src/Installer/dnup/Commands/Sdk/Install/EnvironmentVariableMockDotnetInstaller.cs index 4617d65eabe8..80b332521b4d 100644 --- a/src/Installer/dnup/Commands/Sdk/Install/EnvironmentVariableMockDotnetInstaller.cs +++ b/src/Installer/dnup/Commands/Sdk/Install/EnvironmentVariableMockDotnetInstaller.cs @@ -27,16 +27,16 @@ public string GetDefaultDotnetInstallPath() return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "dotnet"); } - public DotnetInstallRoot GetConfiguredInstallType() + public DotnetInstallRootConfiguration? GetConfiguredInstallType() { var testHookDefaultInstall = Environment.GetEnvironmentVariable("DOTNET_TESTHOOK_DEFAULT_INSTALL"); - InstallType installtype = InstallType.None; + InstallType installtype; if (!Enum.TryParse(testHookDefaultInstall, out installtype)) { - installtype = InstallType.None; + return null; } - var installPath = Environment.GetEnvironmentVariable("DOTNET_TESTHOOK_CURRENT_INSTALL_PATH"); - return new(installPath, installtype, DnupUtilities.GetDefaultInstallArchitecture()); + var installPath = Environment.GetEnvironmentVariable("DOTNET_TESTHOOK_CURRENT_INSTALL_PATH") ?? GetDefaultDotnetInstallPath(); + return new(new(installPath, DnupUtilities.GetDefaultInstallArchitecture()), installtype, true, true); } public string? GetLatestInstalledAdminVersion() diff --git a/src/Installer/dnup/Commands/Sdk/Install/EnvironmentVariableMockReleaseInfoProvider.cs b/src/Installer/dnup/Commands/Sdk/Install/EnvironmentVariableMockReleaseInfoProvider.cs index 681e0d40ac0d..29c71f70f56e 100644 --- a/src/Installer/dnup/Commands/Sdk/Install/EnvironmentVariableMockReleaseInfoProvider.cs +++ b/src/Installer/dnup/Commands/Sdk/Install/EnvironmentVariableMockReleaseInfoProvider.cs @@ -1,13 +1,14 @@ 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 : IReleaseInfoProvider + internal class EnvironmentVariableMockReleaseInfoProvider : IDotnetReleaseInfoProvider { - public List GetAvailableChannels() + IEnumerable IDotnetReleaseInfoProvider.GetAvailableChannels() { var channels = Environment.GetEnvironmentVariable("DOTNET_TESTHOOK_AVAILABLE_CHANNELS"); if (string.IsNullOrEmpty(channels)) @@ -16,34 +17,45 @@ public List GetAvailableChannels() } return channels.Split(',').ToList(); } - public string GetLatestVersion(string channel) + 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") { - return "11.0.100-preview.1.42424"; + version = "11.0.100-preview.1.42424"; } else if (channel == "latest" || channel == "10" || channel == "10.0.2xx") { - return "10.0.0-preview.7"; + version = "10.0.0-preview.7"; } else if (channel == "10.0.1xx") { - return "10.0.106"; + version = "10.0.106"; } else if (channel == "9" || channel == "9.0.3xx") { - return "9.0.309"; + version = "9.0.309"; } else if (channel == "9.0.2xx") { - return "9.0.212"; + version = "9.0.212"; } else if (channel == "9.0.1xx") { - return "9.0.115"; + version = "9.0.115"; } - return channel; + 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 888f0f65e689..a4f33e9aa6a7 100644 --- a/src/Installer/dnup/Commands/Sdk/Install/SdkInstallCommand.cs +++ b/src/Installer/dnup/Commands/Sdk/Install/SdkInstallCommand.cs @@ -22,7 +22,7 @@ internal class SdkInstallCommand(ParseResult result) : CommandBase(result) private readonly bool _interactive = result.GetValue(SdkInstallCommandParser.InteractiveOption); private readonly IBootstrapperController _dotnetInstaller = new BootstrapperController(); - private readonly IReleaseInfoProvider _releaseInfoProvider = new EnvironmentVariableMockReleaseInfoProvider(); + private readonly IDotnetReleaseInfoProvider _releaseInfoProvider = new EnvironmentVariableMockReleaseInfoProvider(); private readonly ManifestChannelVersionResolver _channelVersionResolver = new ManifestChannelVersionResolver(); public override int Execute() @@ -54,7 +54,7 @@ public override int Execute() resolvedInstallPath = _installPath; } - if (resolvedInstallPath == null && currentDotnetInstallRoot.Type == InstallType.User) + if (resolvedInstallPath == null && currentDotnetInstallRoot != null && currentDotnetInstallRoot.InstallType == InstallType.User) { // If a user installation is already set up, we don't need to prompt for the install path resolvedInstallPath = currentDotnetInstallRoot.Path; @@ -132,13 +132,13 @@ public override int Execute() // If global.json specified an install path, we don't prompt for setting the default install path (since you probably don't want to do that for a repo-local path) if (_interactive && installPathFromGlobalJson == null) { - if (currentDotnetInstallRoot.Type == InstallType.None) + if (currentDotnetInstallRoot == null) { resolvedSetDefaultInstall = SpectreAnsiConsole.Confirm( $"Do you want to set the install path ({resolvedInstallPath}) as the default dotnet install? This will update the PATH and DOTNET_ROOT environment variables.", defaultValue: true); } - else if (currentDotnetInstallRoot.Type == InstallType.User) + else if (currentDotnetInstallRoot.InstallType == InstallType.User) { if (DnupUtilities.PathsEqual(resolvedInstallPath, currentDotnetInstallRoot.Path)) { @@ -151,7 +151,7 @@ public override int Execute() defaultValue: false); } } - else if (currentDotnetInstallRoot.Type == InstallType.Admin) + else if (currentDotnetInstallRoot.InstallType == InstallType.Admin) { SpectreAnsiConsole.WriteLine($"You have an existing admin install of .NET in {currentDotnetInstallRoot.Path}. We can configure your system to use the new install of .NET " + $"in {resolvedInstallPath} instead. This would mean that the admin install of .NET would no longer be accessible from the PATH or from Visual Studio."); @@ -160,11 +160,8 @@ public override int Execute() $"Do you want to set the user install path ({resolvedInstallPath}) as the default dotnet install? This will update the PATH and DOTNET_ROOT environment variables.", defaultValue: true); } - else if (currentDotnetInstallRoot.Type == InstallType.Inconsistent) - { - // TODO: Figure out what to do here - resolvedSetDefaultInstall = false; - } + + // TODO: Add checks for whether PATH and DOTNET_ROOT need to be updated, or if the install is in an inconsistent state } else { @@ -176,14 +173,14 @@ public override int Execute() // Create a request and resolve it using the channel version resolver var installRequest = new DotnetInstallRequest( - new DotnetInstallRoot(resolvedInstallPath, InstallType.User, DnupUtilities.GetInstallArchitecture(RuntimeInformation.ProcessArchitecture)), + new DotnetInstallRoot(resolvedInstallPath, DnupUtilities.GetInstallArchitecture(RuntimeInformation.ProcessArchitecture)), new UpdateChannel(resolvedChannel), InstallComponent.SDK, new InstallRequestOptions()); var resolvedVersion = _channelVersionResolver.Resolve(installRequest); - if (resolvedSetDefaultInstall == true && currentDotnetInstallRoot.Type == InstallType.Admin) + if (resolvedSetDefaultInstall == true && currentDotnetInstallRoot?.InstallType == InstallType.Admin) { if (_interactive) { @@ -227,7 +224,7 @@ public override int Execute() { // Create the request for the additional version var additionalRequest = new DotnetInstallRequest( - new DotnetInstallRoot(resolvedInstallPath, InstallType.User, DnupUtilities.GetInstallArchitecture(RuntimeInformation.ProcessArchitecture)), + new DotnetInstallRoot(resolvedInstallPath, DnupUtilities.GetInstallArchitecture(RuntimeInformation.ProcessArchitecture)), new UpdateChannel(additionalVersion), InstallComponent.SDK, new InstallRequestOptions()); diff --git a/src/Installer/dnup/DnupManifestJsonContext.cs b/src/Installer/dnup/DnupManifestJsonContext.cs index 2abd4eb49b68..937f1d2112f7 100644 --- a/src/Installer/dnup/DnupManifestJsonContext.cs +++ b/src/Installer/dnup/DnupManifestJsonContext.cs @@ -11,7 +11,7 @@ namespace Microsoft.DotNet.Tools.Bootstrapper [JsonSerializable(typeof(List))] [JsonSerializable(typeof(DotnetVersion))] [JsonSerializable(typeof(DotnetVersionType))] - [JsonSerializable(typeof(InstallMode))] + [JsonSerializable(typeof(InstallComponent))] [JsonSerializable(typeof(InstallArchitecture))] [JsonSerializable(typeof(InstallType))] [JsonSerializable(typeof(ManagementCadence))] diff --git a/src/Installer/dnup/DotnetInstall.cs b/src/Installer/dnup/DotnetInstall.cs index 470b73f530b8..3dadd48884ef 100644 --- a/src/Installer/dnup/DotnetInstall.cs +++ b/src/Installer/dnup/DotnetInstall.cs @@ -3,17 +3,10 @@ using System; using Microsoft.Deployment.DotNet.Releases; +using Microsoft.Dotnet.Installation; namespace Microsoft.DotNet.Tools.Bootstrapper; -public record DotnetInstallRoot( - string? Path, - InstallType Type, - InstallArchitecture Architecture) -{ - // Do we need a GUID for the ID here? -} - /// /// Represents a .NET installation with a fully specified version. /// The MuxerDirectory is the directory of the corresponding .NET host that has visibility into this .NET installation. diff --git a/src/Installer/dnup/IBootstrapperController.cs b/src/Installer/dnup/IBootstrapperController.cs index be4d7760352b..5b9752c664f5 100644 --- a/src/Installer/dnup/IBootstrapperController.cs +++ b/src/Installer/dnup/IBootstrapperController.cs @@ -14,7 +14,7 @@ public interface IBootstrapperController string GetDefaultDotnetInstallPath(); - DotnetInstallRoot GetConfiguredInstallType(); + DotnetInstallRootConfiguration? GetConfiguredInstallType(); string? GetLatestInstalledAdminVersion(); @@ -39,8 +39,12 @@ public class GlobalJsonInfo public string? SdkPath => (GlobalJsonContents?.Sdk?.Paths != null && GlobalJsonContents.Sdk.Paths.Length > 0) ? GlobalJsonContents.Sdk.Paths[0] : null; } -public interface IReleaseInfoProvider +public record DotnetInstallRootConfiguration( + DotnetInstallRoot InstallRoot, + InstallType InstallType, + bool IsOnPath, + // We may also need additional information to handle the case of whether DOTNET_ROOT is not set or whether it's set to a different path + bool IsSetAsDotnetRoot) { - List GetAvailableChannels(); - string GetLatestVersion(string channel); + public string Path => InstallRoot.Path; } diff --git a/src/Installer/dnup/IChannelVersionResolver.cs b/src/Installer/dnup/IChannelVersionResolver.cs deleted file mode 100644 index f0cfb323f0f5..000000000000 --- a/src/Installer/dnup/IChannelVersionResolver.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; - -namespace Microsoft.DotNet.Tools.Bootstrapper -{ - internal interface ChannelVersionResolver - { - public DotnetInstall Resolve(DotnetInstallRequest dotnetChannelVersion); - } -} diff --git a/src/Installer/dnup/InstallComponent.cs b/src/Installer/dnup/InstallComponent.cs deleted file mode 100644 index a65f29c987b5..000000000000 --- a/src/Installer/dnup/InstallComponent.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.DotNet.Tools.Bootstrapper -{ - public enum InstallComponent - { - SDK, - Runtime, - ASPNETCore, - WindowsDesktop - } - -} diff --git a/src/Installer/dnup/InstallType.cs b/src/Installer/dnup/InstallType.cs index 065b520e7e6b..c63605e859f7 100644 --- a/src/Installer/dnup/InstallType.cs +++ b/src/Installer/dnup/InstallType.cs @@ -1,14 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Tools.Bootstrapper +namespace Microsoft.Dotnet.Installation; + +public enum InstallType { - public enum InstallType - { - None, - // Inconsistent would be when the dotnet on the path doesn't match what DOTNET_ROOT is set to - Inconsistent, - Admin, - User - } + User, + Admin, } diff --git a/src/Installer/dnup/dnup.csproj b/src/Installer/dnup/dnup.csproj index 3d343aae531e..5ef732fe7e6e 100644 --- a/src/Installer/dnup/dnup.csproj +++ b/src/Installer/dnup/dnup.csproj @@ -34,7 +34,15 @@ - + + + + + + + + + diff --git a/test/dnup.Tests/ReleaseManifestTests.cs b/test/dnup.Tests/ReleaseManifestTests.cs index 81e88633c8dd..5bc665138b18 100644 --- a/test/dnup.Tests/ReleaseManifestTests.cs +++ b/test/dnup.Tests/ReleaseManifestTests.cs @@ -1,6 +1,7 @@ using System; using Xunit; using Microsoft.DotNet.Tools.Bootstrapper; +using Microsoft.Dotnet.Installation; namespace Microsoft.DotNet.Tools.Dnup.Tests { @@ -10,17 +11,17 @@ public class ReleaseManifestTests public void GetLatestVersionForChannel_MajorOnly_ReturnsLatestVersion() { var manifest = new ReleaseManifest(); - var version = manifest.GetLatestVersionForChannel("9", InstallMode.SDK); - Assert.True(!string.IsNullOrEmpty(version)); + var version = manifest.GetLatestVersionForChannel(new UpdateChannel("9"), InstallComponent.SDK); + Assert.NotNull(version); } [Fact] public void GetLatestVersionForChannel_MajorMinor_ReturnsLatestVersion() { var manifest = new ReleaseManifest(); - var version = manifest.GetLatestVersionForChannel("9.0", InstallMode.SDK); - Assert.False(string.IsNullOrEmpty(version)); - Assert.StartsWith("9.0.", version); + var version = manifest.GetLatestVersionForChannel(new UpdateChannel("9"), InstallComponent.SDK); + Assert.NotNull(version); + Assert.StartsWith("9.0.", version.ToString()); } [Fact] @@ -28,78 +29,70 @@ public void GetLatestVersionForChannel_FeatureBand_ReturnsLatestVersion() { var manifest = new ReleaseManifest(); - var version = manifest.GetLatestVersionForChannel("9.0.1xx", InstallMode.SDK); - Console.WriteLine($"Version found: {version ?? "null"}"); + var version = manifest.GetLatestVersionForChannel(new UpdateChannel("9.0.1xx"), InstallComponent.SDK); + Console.WriteLine($"Version found: {version}"); // Feature band version should be returned in the format 9.0.100 - Assert.True(!string.IsNullOrEmpty(version)); - Assert.Matches(@"^9\.0\.1\d{2}$", version); + Assert.NotNull(version); + Assert.Matches(@"^9\.0\.1\d{2}$", version.ToString()); } [Fact] public void GetLatestVersionForChannel_LTS_ReturnsLatestLTSVersion() { var manifest = new ReleaseManifest(); - var version = manifest.GetLatestVersionForChannel("lts", InstallMode.SDK); + var version = manifest.GetLatestVersionForChannel(new UpdateChannel("lts"), InstallComponent.SDK); - Console.WriteLine($"LTS Version found: {version ?? "null"}"); + Console.WriteLine($"LTS Version found: {version}"); // Check that we got a version - Assert.False(string.IsNullOrEmpty(version)); + Assert.NotNull(version); - // LTS versions should have even minor versions (e.g., 6.0, 8.0, 10.0) - var versionParts = version.Split('.'); - Assert.True(versionParts.Length >= 2, "Version should have at least major.minor parts"); - - int minorVersion = int.Parse(versionParts[1]); - Assert.True(minorVersion % 2 == 0, $"LTS version {version} should have an even minor version"); + // LTS versions should have even major versions (e.g., 6.0, 8.0, 10.0) + Assert.True(version.Minor % 2 == 0, $"LTS version {version} should have an even minor version"); // Should not be a preview version - Assert.DoesNotContain("-", version); + Assert.Null(version.Prerelease); } [Fact] public void GetLatestVersionForChannel_STS_ReturnsLatestSTSVersion() { var manifest = new ReleaseManifest(); - var version = manifest.GetLatestVersionForChannel("sts", InstallMode.SDK); + var version = manifest.GetLatestVersionForChannel(new UpdateChannel("sts"), InstallComponent.SDK); - Console.WriteLine($"STS Version found: {version ?? "null"}"); + Console.WriteLine($"STS Version found: {version}"); // Check that we got a version - Assert.False(string.IsNullOrEmpty(version)); - - // STS versions should have odd minor versions (e.g., 7.0, 9.0, 11.0) - var versionParts = version.Split('.'); - Assert.True(versionParts.Length >= 2, "Version should have at least major.minor parts"); + Assert.NotNull(version); - int minorVersion = int.Parse(versionParts[1]); - Assert.True(minorVersion % 2 != 0, $"STS version {version} should have an odd minor version"); + // STS versions should have odd major versions (e.g., 7.0, 9.0, 11.0) + Assert.True(version.Major % 2 != 0, $"STS version {version} should have an odd minor version"); // Should not be a preview version - Assert.DoesNotContain("-", version); + Assert.Null(version.Prerelease); } [Fact] public void GetLatestVersionForChannel_Preview_ReturnsLatestPreviewVersion() { var manifest = new ReleaseManifest(); - var version = manifest.GetLatestVersionForChannel("preview", InstallMode.SDK); + var version = manifest.GetLatestVersionForChannel(new UpdateChannel("preview"), InstallComponent.SDK); - Console.WriteLine($"Preview Version found: {version ?? "null"}"); + Console.WriteLine($"Preview Version found: {version}"); // Check that we got a version - Assert.False(string.IsNullOrEmpty(version)); + Assert.NotNull(version); // Preview versions should contain a hyphen (e.g., "11.0.0-preview.1") - Assert.Contains("-", version); + Assert.NotNull(version.Prerelease); // Should contain preview, rc, beta, or alpha Assert.True( - version.Contains("preview", StringComparison.OrdinalIgnoreCase) || - version.Contains("rc", StringComparison.OrdinalIgnoreCase) || - version.Contains("beta", StringComparison.OrdinalIgnoreCase) || - version.Contains("alpha", StringComparison.OrdinalIgnoreCase), + version.Prerelease.Contains("preview", StringComparison.OrdinalIgnoreCase) || + version.Prerelease.Contains("rc", StringComparison.OrdinalIgnoreCase) || + version.Prerelease.Contains("beta", StringComparison.OrdinalIgnoreCase) || + version.Prerelease.Contains("alpha", StringComparison.OrdinalIgnoreCase), $"Version {version} should be a preview/rc/beta/alpha version" ); }