diff --git a/installer.sln b/installer.sln index ed65ece..3b4abf3 100644 --- a/installer.sln +++ b/installer.sln @@ -1,22 +1,31 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34526.213 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elskom.Check", "src/Elskom.Check.csproj", "{C2F18097-0111-4A5F-978F-27F212F3F6D7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elskom.Check", "src\Elskom.Check\Elskom.Check.csproj", "{C2F18097-0111-4A5F-978F-27F212F3F6D7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elskom.Check.Cli.Internal", "src\Elskom.Check.Cli.Internal\Elskom.Check.Cli.Internal.csproj", "{A2C0B6E5-925A-4888-9E94-299E842F11C4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {C2F18097-0111-4A5F-978F-27F212F3F6D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C2F18097-0111-4A5F-978F-27F212F3F6D7}.Debug|Any CPU.Build.0 = Debug|Any CPU {C2F18097-0111-4A5F-978F-27F212F3F6D7}.Release|Any CPU.ActiveCfg = Release|Any CPU {C2F18097-0111-4A5F-978F-27F212F3F6D7}.Release|Any CPU.Build.0 = Release|Any CPU + {A2C0B6E5-925A-4888-9E94-299E842F11C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2C0B6E5-925A-4888-9E94-299E842F11C4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2C0B6E5-925A-4888-9E94-299E842F11C4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2C0B6E5-925A-4888-9E94-299E842F11C4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {54646281-95F0-406D-8F68-8D9469F14859} EndGlobalSection EndGlobal diff --git a/src/Elskom.Check.Cli.Internal/Commands/InstallCommand.cs b/src/Elskom.Check.Cli.Internal/Commands/InstallCommand.cs new file mode 100644 index 0000000..f4af88f --- /dev/null +++ b/src/Elskom.Check.Cli.Internal/Commands/InstallCommand.cs @@ -0,0 +1,12 @@ +namespace Elskom.Check; + +internal class InstallCommand : AsyncCommand +{ + public override async Task ExecuteAsync([NotNull] CommandContext context, [NotNull] WorkloadSettings settings) + { + DotNetSdkHelper.GetOrUpdateSdkVersion(ref settings); + var workloadInfos = await NuGetHelper.ResolveWildcardWorkloadPackageVersionsAsync(settings.RuntimeIdentifier!).ConfigureAwait(false); + await workloadInfos.InstallAsync(settings.SdkVersion!, settings.RuntimeIdentifier!).ConfigureAwait(false); + return 0; + } +} diff --git a/src/Elskom.Check.Cli.Internal/Commands/UninstallCommand.cs b/src/Elskom.Check.Cli.Internal/Commands/UninstallCommand.cs new file mode 100644 index 0000000..588523b --- /dev/null +++ b/src/Elskom.Check.Cli.Internal/Commands/UninstallCommand.cs @@ -0,0 +1,12 @@ +namespace Elskom.Check; + +internal class UninstallCommand : AsyncCommand +{ + public override async Task ExecuteAsync([NotNull] CommandContext context, [NotNull] WorkloadSettings settings) + { + DotNetSdkHelper.GetOrUpdateSdkVersion(ref settings); + var workloadInfos = await NuGetHelper.ResolveWildcardWorkloadPackageVersionsAsync(settings.RuntimeIdentifier!).ConfigureAwait(false); + workloadInfos.Uninstall(settings.SdkVersion!, settings.RuntimeIdentifier!); + return 0; + } +} diff --git a/src/Elskom.Check.Cli.Internal/Commands/UpdateCommand.cs b/src/Elskom.Check.Cli.Internal/Commands/UpdateCommand.cs new file mode 100644 index 0000000..f4da3a4 --- /dev/null +++ b/src/Elskom.Check.Cli.Internal/Commands/UpdateCommand.cs @@ -0,0 +1,12 @@ +namespace Elskom.Check; + +internal class UpdateCommand : AsyncCommand +{ + public override async Task ExecuteAsync([NotNull] CommandContext context, [NotNull] WorkloadSettings settings) + { + DotNetSdkHelper.GetOrUpdateSdkVersion(ref settings); + var workloadInfos = await NuGetHelper.ResolveWildcardWorkloadPackageVersionsAsync(settings.RuntimeIdentifier!).ConfigureAwait(false); + await workloadInfos.UpdateAsync(settings.SdkVersion!, settings.RuntimeIdentifier!).ConfigureAwait(false); + return 0; + } +} diff --git a/src/Constants.cs b/src/Elskom.Check.Cli.Internal/Constants.cs similarity index 54% rename from src/Constants.cs rename to src/Elskom.Check.Cli.Internal/Constants.cs index 84ecf7c..c851bb2 100644 --- a/src/Constants.cs +++ b/src/Elskom.Check.Cli.Internal/Constants.cs @@ -1,13 +1,23 @@ -namespace Elskom.Check; - -internal static class Constants -{ - internal const string WorkloadName = "elskom"; - internal const string SdkPackName = "Elskom.Sdk"; - internal const string RefPackName = "Elskom.Sdk.App.Ref"; - internal const string RuntimePackName = "Elskom.Sdk.App"; - internal const string TemplatePackName = "Elskom.Sdk.Templates"; - internal const string Sdk = "sdk"; - internal const string Framework = "framework"; - internal const string Template = "template"; -} +namespace Elskom.Check; + +internal static class Constants +{ + internal const string WorkloadName = "elskom"; + internal const string SdkPackName = "Elskom.Sdk"; + internal const string RefPackName = "Elskom.Sdk.App.Ref"; + internal const string RuntimePackName = "Elskom.Sdk.App"; + internal const string TemplatePackName = "Elskom.Sdk.Templates"; + internal const string Sdk = "sdk"; + internal const string Framework = "framework"; + internal const string Template = "template"; + internal static readonly string[] RuntimePacks = [ + "Elskom.Sdk.App.Runtime.win-x86", + "Elskom.Sdk.App.Runtime.win-x64", + "Elskom.Sdk.App.Runtime.win-arm64", + "Elskom.Sdk.App.Runtime.linux-x64", + "Elskom.Sdk.App.Runtime.linux-arm", + "Elskom.Sdk.App.Runtime.linux-arm64", + "Elskom.Sdk.App.Runtime.osx-x64", + "Elskom.Sdk.App.Runtime.osx-arm64" + ]; +} diff --git a/src/Directory.Build.targets b/src/Elskom.Check.Cli.Internal/Directory.Build.targets similarity index 100% rename from src/Directory.Build.targets rename to src/Elskom.Check.Cli.Internal/Directory.Build.targets diff --git a/src/DotNetSdkInfo.cs b/src/Elskom.Check.Cli.Internal/DotNetSdkInfo.cs similarity index 95% rename from src/DotNetSdkInfo.cs rename to src/Elskom.Check.Cli.Internal/DotNetSdkInfo.cs index 348b81a..1f9e0fc 100644 --- a/src/DotNetSdkInfo.cs +++ b/src/Elskom.Check.Cli.Internal/DotNetSdkInfo.cs @@ -1,19 +1,19 @@ -namespace Elskom.Check; - -internal class DotNetSdkInfo -{ - internal DotNetSdkInfo(string version, string directory) - : this(version, new DirectoryInfo(directory)) - { - } - - internal DotNetSdkInfo(string version, DirectoryInfo directory) - { - Version = version; - Directory = directory; - } - - internal string Version { get; set; } - - internal DirectoryInfo Directory { get; set; } -} +namespace Elskom.Check; + +internal class DotNetSdkInfo +{ + internal DotNetSdkInfo(string version, string directory) + : this(version, new DirectoryInfo(directory)) + { + } + + internal DotNetSdkInfo(string version, DirectoryInfo directory) + { + Version = version; + Directory = directory; + } + + internal string Version { get; set; } + + internal DirectoryInfo Directory { get; set; } +} diff --git a/src/Elskom.Check.Cli.Internal/Elskom.Check.Cli.Internal.csproj b/src/Elskom.Check.Cli.Internal/Elskom.Check.Cli.Internal.csproj new file mode 100644 index 0000000..84310fe --- /dev/null +++ b/src/Elskom.Check.Cli.Internal/Elskom.Check.Cli.Internal.csproj @@ -0,0 +1,24 @@ + + + + net8.0 + enable + enable + latest + Els_kom org. + Els_kom org. + $(Version) + true + embedded + Elskom.Check + False + preview + false + + + + + + + + diff --git a/src/Elskom.Check.Cli.Internal/Extensions/ListExtensions.cs b/src/Elskom.Check.Cli.Internal/Extensions/ListExtensions.cs new file mode 100644 index 0000000..84baecd --- /dev/null +++ b/src/Elskom.Check.Cli.Internal/Extensions/ListExtensions.cs @@ -0,0 +1,378 @@ +namespace Elskom.Check; + +internal static class ListExtensions +{ + internal static async Task InstallAsync(this List workloadInfos, string sdkVersion, string runtimeIdentifier) + { + _ = workloadInfos.CheckInstalledRefPackVersion(); + _ = workloadInfos.CheckInstalledRuntimePackVersion(); + workloadInfos.SetWorkloadOutputPaths( + DotNetSdkHelper.GetDotNetSdkWorkloadPacksFolder(runtimeIdentifier), + DotNetSdkHelper.GetDotNetSdkWorkloadRuntimePacksFolder(runtimeIdentifier)); + var result = false; + if (workloadInfos != null) + { + foreach (var info in workloadInfos) + { + result = await InstallCoreAsync(info, sdkVersion, runtimeIdentifier).ConfigureAwait(false); + } + } + + // avoid installing the templates and the workload manifest if the packs failed to install. + if (result) + { + var templatePackVersion = await DotNetSdkHelper.InstallTemplate( + Constants.TemplatePackName, runtimeIdentifier).ConfigureAwait(false); + if (!templatePackVersion.Equals("already installed", StringComparison.Ordinal)) + { + Console.WriteLine($"Successfully installed workload package '{Constants.TemplatePackName}'."); + } + + InstallManifest(sdkVersion, runtimeIdentifier, await workloadInfos!.ToManifestDictionaryAsync(runtimeIdentifier, templatePackVersion).ConfigureAwait(false)); + } + else + { + throw new InvalidOperationException("Could not install the workload due to failure to obtain package versions."); + } + } + + internal static void Uninstall(this List workloadInfos, string sdkVersion, string runtimeIdentifier) + { + _ = workloadInfos.CheckInstalledRefPackVersion(); + _ = workloadInfos.CheckInstalledRuntimePackVersion(); + workloadInfos.SetWorkloadOutputPaths( + DotNetSdkHelper.GetDotNetSdkWorkloadPacksFolder(runtimeIdentifier), + DotNetSdkHelper.GetDotNetSdkWorkloadRuntimePacksFolder(runtimeIdentifier)); + + // delete the directories to the workload. + _ = UninstallManifest(sdkVersion, runtimeIdentifier); + + if (workloadInfos != null) + { + foreach (var info in workloadInfos) + { + UninstallCore(info, sdkVersion, runtimeIdentifier); + } + } + + var result = DotNetSdkHelper.UninstallTemplate(Constants.TemplatePackName, runtimeIdentifier); + if (result.Contains("The template package '") && result.Contains("' is not found.")) + { + Console.WriteLine($"Workload package '{Constants.TemplatePackName}' was already uninstalled."); + } + else + { + Console.WriteLine($"Successfully uninstalled workload package '{Constants.TemplatePackName}'."); + } + } + + internal static async Task UpdateAsync(this List workloadInfos, string sdkVersion, string runtimeIdentifier) + { + var results = new Dictionary(); + var sdkPackUpdated = false; + var workloadFolder = DotNetSdkHelper.GetDotNetSdkWorkloadFolder( + Constants.WorkloadName, + sdkVersion, + runtimeIdentifier); + var text = await File.ReadAllTextAsync( + Path.Join( + workloadFolder, + "WorkloadManifest.json")).ConfigureAwait(false); + var workloadManifest = JsonSerializer.Deserialize(text); + workloadManifest!.Packs.SetPacksDictionary(); + var refPackUpdated = workloadInfos.CheckInstalledRefPackVersion(workloadManifest, runtimeIdentifier); + var runtimePackUpdated = workloadInfos.CheckInstalledRuntimePackVersion(workloadManifest, runtimeIdentifier); + workloadInfos.SetWorkloadOutputPaths( + DotNetSdkHelper.GetDotNetSdkWorkloadPacksFolder(runtimeIdentifier), + DotNetSdkHelper.GetDotNetSdkWorkloadRuntimePacksFolder(runtimeIdentifier)); + foreach (var info in workloadInfos) + { + var result = false; + var workloadPack = GetWorkloadPack(info, workloadManifest, runtimeIdentifier); + if (!workloadPack.Name.Equals(Constants.RuntimePackName) && !workloadPack.Name.Equals(Constants.RefPackName)) + { + result = await UpdateCore(info, workloadManifest, sdkVersion, runtimeIdentifier).ConfigureAwait(false); + } + else if (!runtimePackUpdated) + { + runtimePackUpdated = await UpdateCore(info, workloadManifest, sdkVersion, runtimeIdentifier).ConfigureAwait(false); + } + else if (!refPackUpdated) + { + refPackUpdated = await UpdateCore(info, workloadManifest, sdkVersion, runtimeIdentifier).ConfigureAwait(false); + } + + results.Add(info, result); + } + + var currentTemplatePackVersion = await NuGetHelper.ResolveWildcardPackageVersionAsync( + Constants.TemplatePackName).ConfigureAwait(false); + var templatePackUpdated = await DotNetSdkHelper.UpdateTemplate( + Constants.TemplatePackName, + currentTemplatePackVersion, + runtimeIdentifier).ConfigureAwait(false); + if (sdkPackUpdated) + { + Console.WriteLine($"Workload Manifest is now version: '{workloadManifest.Version}'."); + Console.WriteLine($"Workload Sdk is now version: '{workloadManifest.Packs.ElskomSdk.Version}'."); + } + + if (runtimePackUpdated) + { + Console.WriteLine($"Workload Runtime Pack is now version: '{workloadManifest.Packs.ElskomSdkApp.Version}'."); + } + + if (refPackUpdated) + { + Console.WriteLine($"Workload Reference Pack is now version: '{workloadManifest.Packs.ElskomSdkAppRef.Version}'."); + } + + if (templatePackUpdated) + { + workloadManifest.Packs.ElskomSdkTemplates.UpdateVersion(currentTemplatePackVersion); + Console.WriteLine($"Workload Template Pack is now version: '{workloadManifest.Packs.ElskomSdkTemplates.Version}'."); + } + + if (sdkPackUpdated || runtimePackUpdated || refPackUpdated || templatePackUpdated) + { + workloadManifest.WriteJsonFile(sdkVersion, runtimeIdentifier); + workloadManifest.WriteTargetsFile(sdkVersion, runtimeIdentifier); + } + } + + internal static async Task> ToManifestDictionaryAsync(this List workloadInfos, string runtimeIdentifier, string? templatePackVersion = null) + { + var manifest = new Dictionary(); + if (workloadInfos != null) + { + foreach (var info in workloadInfos) + { + manifest.Add(info.PackageName.Replace($".Runtime.{runtimeIdentifier}", string.Empty, StringComparison.OrdinalIgnoreCase), info.PackageVersion); + } + + templatePackVersion ??= await NuGetHelper.ResolveWildcardPackageVersionAsync( + Constants.TemplatePackName).ConfigureAwait(false); + manifest.Add(Constants.TemplatePackName, templatePackVersion); + } + + return manifest; + } + + private static bool CheckInstalledRefPackVersion(this List workloadInfos, WorkloadManifest? workloadManifest = null, string? runtimeIdentifier = null) + { + if (workloadInfos != null) + { + foreach (var info in from info in workloadInfos + where info.PackageName.Equals(Constants.RefPackName) + where !info.InstalledPackageVersion.Equals(string.Empty) + where info.InstalledPackageVersion.EndsWith("-dev") + || DotNetSdkHelper.ConvertVersionToNuGetVersion(info.InstalledPackageVersion) + > DotNetSdkHelper.ConvertVersionToNuGetVersion(info.PackageVersion) + select info) + { + Console.WriteLine("Picked up newer installed reference pack, using that instead."); + info.PackageVersion = info.InstalledPackageVersion; + if (workloadManifest != null) + { + GetWorkloadPack(info, workloadManifest, runtimeIdentifier!).UpdateVersion(info.PackageVersion); + } + + return true; + } + } + + return false; + } + + private static bool CheckInstalledRuntimePackVersion(this List workloadInfos, WorkloadManifest? workloadManifest = null, string? runtimeIdentifier = null) + { + if (workloadInfos != null) + { + foreach (var info in from info in workloadInfos + where Constants.RuntimePacks.Contains(info.PackageName) + where !info.InstalledPackageVersion.Equals(string.Empty) + where info.InstalledPackageVersion.EndsWith("-dev") + || DotNetSdkHelper.ConvertVersionToNuGetVersion(info.InstalledPackageVersion) + > DotNetSdkHelper.ConvertVersionToNuGetVersion(info.PackageVersion) + select info) + { + Console.WriteLine("Picked up newer installed runtime pack, using that instead."); + info.PackageVersion = info.InstalledPackageVersion; + if (workloadManifest != null) + { + GetWorkloadPack(info, workloadManifest, runtimeIdentifier!).UpdateVersion(info.PackageVersion); + } + + return true; + } + } + + return false; + } + + private static void SetWorkloadOutputPaths(this List workloadInfos, string outputPath, string runtimeOutputPath) + { + if (workloadInfos != null) + { + foreach (var info in workloadInfos) + { + info.OutputPath = Constants.RuntimePacks.Contains(info.PackageName) ? runtimeOutputPath : outputPath; + } + } + } + + private static WorkloadManifest.WorkloadPacks.WorkloadPack GetWorkloadPack(WorkloadInfo info, WorkloadManifest workloadManifest, string runtimeIdentifier) + { + var packageName = info.PackageName.Replace($".Runtime.{runtimeIdentifier}", string.Empty, StringComparison.OrdinalIgnoreCase); + var result = workloadManifest.Packs.Packs[packageName]; + if (string.IsNullOrEmpty(result.Name)) + { + result.Name = packageName; + } + + return result; + } + + private static async Task InstallCoreAsync(WorkloadInfo info, string sdkVersion, string runtimeIdentifier, bool update = false) + { + if (string.IsNullOrEmpty(info.InstalledPackageVersion) || update == true) + { + if (!string.IsNullOrEmpty(info.PackageVersion)) + { + await NuGetHelper.InstallPackageAsync( + info.PackageName, + info.PackageVersion, + info.OutputPath, + runtimeIdentifier).ConfigureAwait(false); + if (!Constants.RuntimePacks.Contains(info.PackageName)) + { + await using var fs = File.Create( + DotNetSdkHelper.GetDotNetSdkWorkloadMetadataInstalledPacks( + info.PackageName, + info.PackageVersion, + sdkVersion, + runtimeIdentifier)).ConfigureAwait(false); + } + + Console.WriteLine(!update + ? $"Successfully installed workload package '{info.PackageName.Replace($".Runtime.{runtimeIdentifier}", string.Empty, StringComparison.OrdinalIgnoreCase)}'." + : $"Successfully updated workload package '{info.PackageName.Replace($".Runtime.{runtimeIdentifier}", string.Empty, StringComparison.OrdinalIgnoreCase)}'."); + return true; + } + else + { + Console.WriteLine($"No version for workload package '{info.PackageName.Replace($".Runtime.{runtimeIdentifier}", string.Empty, StringComparison.OrdinalIgnoreCase)}' is published to nuget.org."); + } + } + else + { + Console.WriteLine($"Workload package '{info.PackageName.Replace($".Runtime.{runtimeIdentifier}", string.Empty, StringComparison.OrdinalIgnoreCase)}' is already installed. Did you intend to run 'update'?"); + return true; + } + + return false; + } + + private static void UninstallCore(WorkloadInfo info, string sdkVersion, string runtimeIdentifier) + { + if (UninstallPackage( + info.PackageName.Replace($".Runtime.{runtimeIdentifier}", string.Empty, StringComparison.OrdinalIgnoreCase), + info.PackageVersion, + info.OutputPath, + sdkVersion, + runtimeIdentifier)) + { + Console.WriteLine($"Successfully uninstalled workload package '{info.PackageName.Replace($".Runtime.{runtimeIdentifier}", string.Empty, StringComparison.OrdinalIgnoreCase)}'."); + } + else + { + Console.WriteLine($"Workload package '{info.PackageName.Replace($".Runtime.{runtimeIdentifier}", string.Empty, StringComparison.OrdinalIgnoreCase)}' was already uninstalled."); + } + } + + private static async Task UpdateCore(WorkloadInfo info, WorkloadManifest workloadManifest, string sdkVersion, string runtimeIdentifier) + { + var workloadPack = GetWorkloadPack(info, workloadManifest, runtimeIdentifier); + if (!workloadPack.Version.Equals(info.PackageVersion)) + { + Console.WriteLine($"Update found for workload package '{info.PackageName.Replace($".Runtime.{runtimeIdentifier}", string.Empty, StringComparison.OrdinalIgnoreCase)}'."); + _ = UninstallPackage( + info.PackageName.Replace($".Runtime.{runtimeIdentifier}", string.Empty, StringComparison.OrdinalIgnoreCase), + workloadPack.Version, + info.OutputPath, + sdkVersion, + runtimeIdentifier); + workloadPack.UpdateVersion(info.PackageVersion); + _ = await InstallCoreAsync( + info, + sdkVersion, + runtimeIdentifier, + true).ConfigureAwait(false); + return true; + } + + Console.WriteLine($"No updates found for workload package '{info.PackageName.Replace($".Runtime.{runtimeIdentifier}", string.Empty, StringComparison.OrdinalIgnoreCase)}'."); + return false; + } + + private static bool UninstallPackage(string packName, string packVersion, string packFolder, string sdkVersion, string runtimeIdentifier) + { + if (!string.IsNullOrEmpty(packVersion)) + { + Directory.Delete(Path.Join(packFolder, packName), true); + if (!packName.Equals(Constants.RuntimePackName, StringComparison.Ordinal)) + { + File.Delete( + DotNetSdkHelper.GetDotNetSdkWorkloadMetadataInstalledPacks( + Constants.SdkPackName, + packVersion, + sdkVersion, + runtimeIdentifier)); + } + + return true; + } + + return false; + } + + private static void InstallManifest(string sdkVersion, string runtimeIdentifier, IReadOnlyDictionary packVersions) + { + foreach (var packVersion in packVersions) + { + if (string.IsNullOrEmpty(packVersion.Value)) + { + throw new InvalidOperationException($"Workload package '{packVersion.Key}' not found."); + } + else if (packVersion.Value.Equals("already installed")) + { + throw new InvalidOperationException("The workload was already installed. Check above for the proper command to update."); + } + } + + var workloadManifest = WorkloadManifest.Create(packVersions); + workloadManifest.WriteJsonFile(sdkVersion, runtimeIdentifier); + workloadManifest.WriteTargetsFile(sdkVersion, runtimeIdentifier); + using var fs1 = File.Create( + DotNetSdkHelper.GetDotNetSdkWorkloadMetadataInstalledWorkloads( + Constants.WorkloadName, + sdkVersion, + runtimeIdentifier)); + } + + private static bool UninstallManifest(string sdkVersion, string runtimeIdentifier) + { + var workloadFolder = DotNetSdkHelper.GetDotNetSdkWorkloadFolder( + Constants.WorkloadName, + sdkVersion, + runtimeIdentifier); + if (Directory.Exists(workloadFolder)) + { + Directory.Delete(workloadFolder, true); + File.Delete(DotNetSdkHelper.GetDotNetSdkWorkloadMetadataInstalledWorkloads(Constants.WorkloadName, sdkVersion, runtimeIdentifier)); + return true; + } + + return false; + } +} diff --git a/src/DotNetSdkHelper.cs b/src/Elskom.Check.Cli.Internal/Helpers/DotNetSdkHelper.cs similarity index 52% rename from src/DotNetSdkHelper.cs rename to src/Elskom.Check.Cli.Internal/Helpers/DotNetSdkHelper.cs index aae7a4a..641ef1b 100644 --- a/src/DotNetSdkHelper.cs +++ b/src/Elskom.Check.Cli.Internal/Helpers/DotNetSdkHelper.cs @@ -1,269 +1,402 @@ -namespace Elskom.Check; - -internal static class DotNetSdkHelper -{ - internal static void GetOrUpdateSdkVersion(ref WorkloadSettings settings) - { - if (string.IsNullOrWhiteSpace(settings.SdkVersion)) - { - // find the feature band version of the .NET SDK currently being used. - settings.SdkVersion = GetDotNetSdkFeatureBandVersion(); - } - } - - internal static string GetDotNetSdkWorkloadFolder(string workloadName, string sdkVersion) - { - var sdkLocation = GetDotNetSdkLocation(); - var sdkVersionBand = string.IsNullOrEmpty(sdkVersion) switch - { - true => GetDotNetSdkFeatureBandVersion(), - false => ConvertVersionToSdkBand(sdkVersion), - }; - return Path.Join(sdkLocation, "sdk-manifests", sdkVersionBand, workloadName); - } - - internal static string GetDotNetSdkWorkloadPacksFolder() - { - var sdkLocation = GetDotNetSdkLocation(); - return Path.Join(sdkLocation, "packs"); - } - - internal static string GetDotNetSdkWorkloadRuntimePacksFolder() - { - var sdkLocation = GetDotNetSdkLocation(); - return Path.Join(sdkLocation, "shared"); - } - - internal static string GetDotNetSdkWorkloadMetadataInstalledWorkloads(string packName, string sdkVersion) - { - var sdkLocation = GetDotNetSdkLocation(); - var sdkVersionBand = string.IsNullOrEmpty(sdkVersion) switch - { - true => GetDotNetSdkFeatureBandVersion(), - false => ConvertVersionToSdkBand(sdkVersion), - }; - var installedWorkloadsPath = Path.Join( - sdkLocation, - "metadata", - "workloads", - sdkVersionBand, - "InstalledWorkloads"); - if (!Directory.Exists(installedWorkloadsPath)) - { - _ = Directory.CreateDirectory(installedWorkloadsPath); - } - - return Path.Join( - installedWorkloadsPath, - packName); - } - - internal static string GetDotNetSdkWorkloadMetadataInstalledPacks(string packName, string packVersion, string sdkVersion, bool delete = false) - { - var sdkLocation = GetDotNetSdkLocation(); - var sdkVersionBand = string.IsNullOrEmpty(sdkVersion) switch - { - true => GetDotNetSdkFeatureBandVersion(), - false => ConvertVersionToSdkBand(sdkVersion), - }; - var dir = Path.Join( - sdkLocation, - "metadata", - "workloads", - "InstalledPacks", - "v1", - packName, - packVersion); - if (!Directory.Exists(dir)) - { - _ = Directory.CreateDirectory(dir); - } - else if (delete) - { - Directory.Delete(dir, true); - } - - return Path.Join(dir, sdkVersionBand); - } - - internal static string GetInstalledDotNetSdkWorkloadPackVersion(string packName) - { - var sdkLocation = GetDotNetSdkLocation(); - var packPath = Path.Join(sdkLocation, "packs", packName); - if (Directory.Exists(packPath)) - { - var di = new DirectoryInfo(packPath); - return di.EnumerateDirectories("*", SearchOption.TopDirectoryOnly).First().Name; - } - - return string.Empty; - } - - internal static string GetInstalledDotNetSdkWorkloadRuntimePackVersion(string packName) - { - var workloadRuntimePackFolder = GetDotNetSdkWorkloadRuntimePacksFolder(); - var packPath = Path.Join(workloadRuntimePackFolder, packName); - if (Directory.Exists(packPath)) - { - var di = new DirectoryInfo(packPath); - return di.EnumerateDirectories("*", SearchOption.TopDirectoryOnly).MaxBy( - item => ConvertVersionToNuGetVersion(item.Name))?.Name ?? string.Empty; - } - - return string.Empty; - } - - internal static string GetDotNetSdkLocation() - { - var knownDotNetLocations = (OperatingSystem.IsWindows(), OperatingSystem.IsLinux(), OperatingSystem.IsMacOS()) switch - { - (true, false, false) => new[] - { - Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), - "dotnet", - "dotnet.exe"), - Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), - "dotnet", - "dotnet.exe"), - }, - (false, false, true) => new[] - { - "/usr/local/share/dotnet/dotnet", - }, - (false, true, false) => new[] - { - Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), - "share", - "dotnet", - "dotnet") - }, - _ => Array.Empty(), - }; - var sdkRoot = string.Empty; - var envSdkRoot = Environment.GetEnvironmentVariable("DOTNET_ROOT"); - if (envSdkRoot is not null && Directory.Exists(envSdkRoot)) - { - sdkRoot = envSdkRoot; - } - - if (string.IsNullOrEmpty(sdkRoot) || !Directory.Exists(sdkRoot)) - { - foreach (var loc in knownDotNetLocations) - { - if (OperatingSystem.IsWindows() - && RuntimeInformation.OSArchitecture == Architecture.X64 - && loc.Contains("Program Files (x86)")) - { - // we need the 64 bit sdk location (if installed) instead. - var loc2 = knownDotNetLocations[Array.IndexOf(knownDotNetLocations, loc) + 1]; - if (File.Exists(loc2)) - { - var dotnet = new FileInfo(loc2); - var sdkDir = dotnet.Directory; - if (sdkDir is not null) - { - sdkRoot = sdkDir.FullName; - } - - break; - } - } - - if (File.Exists(loc)) - { - var dotnet = new FileInfo(loc); - var sdkDir = dotnet.Directory; - if (sdkDir is not null) - { - sdkRoot = sdkDir.FullName; - } - - break; - } - } - } - - return sdkRoot; - } - - internal static NuGetVersion ConvertVersionToNuGetVersion(string version) - { - _ = NuGetVersion.TryParse(version, out var version2); - return version2; - } - - private static string GetDotNetSdkFeatureBandVersion() - { - var sdkLocation = GetDotNetSdkLocation(); - var startOptions = new ProcessStartOptions - { - WaitForProcessExit = true, - }.WithStartInformation( - $"{sdkLocation}{Path.DirectorySeparatorChar}dotnet{(OperatingSystem.IsWindows() ? ".exe" : string.Empty)}", - "--list-sdks", - true, - true, - false, - true, - ProcessWindowStyle.Hidden, - Environment.CurrentDirectory); - var result = startOptions.Start().Split( - Environment.NewLine, - StringSplitOptions.RemoveEmptyEntries); - var sdks = new List(); - foreach (var line in result) - { - try - { - if (line.Contains('[') && line.Contains(']')) - { - var versionStr = line[..line.IndexOf('[')].Trim(); - var locStr = line[line.IndexOf('[')..].Trim('[', ']'); - var loc = Path.Combine(locStr, versionStr); - if (Directory.Exists(locStr) && Directory.Exists(loc)) - { - // If only 1 file it's probably the - // EnableWorkloadResolver.sentinel file that was - // never uninstalled with the rest of the sdk - if (Directory.GetFiles(loc).Length > 1) - { - sdks.Add(new DotNetSdkInfo(versionStr, new DirectoryInfo(loc))); - } - } - } - } - catch - { - // Bad line, ignore - } - } - - FilterSdks(ref sdks); - if (sdks.Count > 1) - { - Console.WriteLine("Bug found. There should have just been 1 sdk left over from the filter."); - } - - var sdk = sdks[0]; // there should only be 1 listed now. - Console.WriteLine($"Detected .NET SDK Version: {sdk.Version}"); - var sdkVersion = ConvertVersionToSdkBand(sdk.Version); - Console.WriteLine($"Detected .NET SDK Band as: {sdkVersion}"); - return sdkVersion; - } - - private static string ConvertVersionToSdkBand(string version) - { - var version2 = ConvertVersionToNuGetVersion(version); - return $"{version2.Major}.{version2.Minor}.{version2.Patch / 100 * 100}"; - } - - private static void FilterSdks(ref List sdks) - { - var sdk = sdks.MaxBy(netSdkInfo => ConvertVersionToNuGetVersion(netSdkInfo.Version)); - sdks.Clear(); - sdks.Add(sdk!); - } -} +namespace Elskom.Check; + +internal static class DotNetSdkHelper +{ + internal static void GetOrUpdateSdkVersion(ref WorkloadSettings settings) + { + if (string.IsNullOrWhiteSpace(settings.RuntimeIdentifier)) + { + // find the runtime identifier of the .NET SDK currently being used. + settings.RuntimeIdentifier = GetDotNetSdkRID(); + } + + if (string.IsNullOrWhiteSpace(settings.SdkVersion)) + { + // find the feature band version of the .NET SDK currently being used. + settings.SdkVersion = GetDotNetSdkFeatureBandVersion(settings.RuntimeIdentifier!); + } + } + + internal static string GetDotNetSdkWorkloadFolder(string workloadName, string sdkVersion, string runtimeIdentifier) + { + var sdkLocation = GetDotNetSdkLocation(runtimeIdentifier); + var sdkVersionBand = string.IsNullOrEmpty(sdkVersion) switch + { + true => GetDotNetSdkFeatureBandVersion(runtimeIdentifier), + false => ConvertVersionToSdkBand(sdkVersion), + }; + return Path.Join(sdkLocation, "sdk-manifests", sdkVersionBand, workloadName); + } + + internal static string GetDotNetSdkWorkloadPacksFolder(string runtimeIdentifier) + { + var sdkLocation = GetDotNetSdkLocation(runtimeIdentifier); + return Path.Join(sdkLocation, "packs"); + } + + internal static string GetDotNetSdkWorkloadRuntimePacksFolder(string runtimeIdentifier) + { + var sdkLocation = GetDotNetSdkLocation(runtimeIdentifier); + return Path.Join(sdkLocation, "shared"); + } + + internal static string GetDotNetSdkWorkloadMetadataInstalledWorkloads(string packName, string sdkVersion, string runtimeIdentifier) + { + var sdkLocation = GetDotNetSdkLocation(runtimeIdentifier); + var sdkVersionBand = string.IsNullOrEmpty(sdkVersion) switch + { + true => GetDotNetSdkFeatureBandVersion(runtimeIdentifier), + false => ConvertVersionToSdkBand(sdkVersion), + }; + var installedWorkloadsPath = Path.Join( + sdkLocation, + "metadata", + "workloads", + sdkVersionBand, + "InstalledWorkloads"); + if (!Directory.Exists(installedWorkloadsPath)) + { + _ = Directory.CreateDirectory(installedWorkloadsPath); + } + + return Path.Join( + installedWorkloadsPath, + packName); + } + + internal static string GetDotNetSdkWorkloadMetadataInstalledPacks(string packName, string packVersion, string sdkVersion, string runtimeIdentifier, bool delete = false) + { + var sdkLocation = GetDotNetSdkLocation(runtimeIdentifier); + var sdkVersionBand = string.IsNullOrEmpty(sdkVersion) switch + { + true => GetDotNetSdkFeatureBandVersion(runtimeIdentifier), + false => ConvertVersionToSdkBand(sdkVersion), + }; + var dir = Path.Join( + sdkLocation, + "metadata", + "workloads", + "InstalledPacks", + "v1", + packName, + packVersion); + if (!Directory.Exists(dir)) + { + _ = Directory.CreateDirectory(dir); + } + else if (delete) + { + Directory.Delete(dir, true); + } + + return Path.Join(dir, sdkVersionBand); + } + + internal static string GetInstalledDotNetSdkWorkloadPackVersion(string packName, string runtimeIdentifier) + { + var sdkLocation = GetDotNetSdkLocation(runtimeIdentifier); + var packPath = Path.Join(sdkLocation, "packs", packName); + if (Directory.Exists(packPath)) + { + var di = new DirectoryInfo(packPath); + return di.EnumerateDirectories("*", SearchOption.TopDirectoryOnly).First().Name; + } + + return string.Empty; + } + + internal static string GetInstalledDotNetSdkWorkloadRuntimePackVersion(string packName, string runtimeIdentifier) + { + var workloadRuntimePackFolder = GetDotNetSdkWorkloadRuntimePacksFolder(runtimeIdentifier); + var packPath = Path.Join(workloadRuntimePackFolder, packName); + if (Directory.Exists(packPath)) + { + var di = new DirectoryInfo(packPath); + return di.EnumerateDirectories("*", SearchOption.TopDirectoryOnly).MaxBy( + item => ConvertVersionToNuGetVersion(item.Name))?.Name ?? string.Empty; + } + + return string.Empty; + } + + internal static string GetDotNetSdkLocation(string runtimeIdentifier) + { + var knownDotNetLocations = (OperatingSystem.IsWindows(), OperatingSystem.IsLinux(), OperatingSystem.IsMacOS()) switch + { + (true, false, false) => + [ + Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), + "dotnet", + "dotnet.exe"), + Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), + "dotnet", + "dotnet.exe"), + ], + (false, false, true) => + [ + "/usr/local/share/dotnet/dotnet", + ], + (false, true, false) => + [ + Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), + "share", + "dotnet", + "dotnet") + ], + _ => Array.Empty(), + }; + var sdkRoot = string.Empty; + var envSdkRoot = Environment.GetEnvironmentVariable("DOTNET_ROOT"); + if (envSdkRoot is not null && Directory.Exists(envSdkRoot)) + { + sdkRoot = envSdkRoot; + } + + if (string.IsNullOrEmpty(sdkRoot) || !Directory.Exists(sdkRoot)) + { + foreach (var loc in knownDotNetLocations) + { + // for x64/arm64 versions of the .NET SDK + if (OperatingSystem.IsWindows() + && (RuntimeInformation.OSArchitecture == Architecture.X64 + || RuntimeInformation.OSArchitecture == Architecture.Arm64) + && loc.Contains("Program Files (x86)") + && runtimeIdentifier.Equals(RuntimeInformation.RuntimeIdentifier, StringComparison.Ordinal)) + { + // we need the 64 bit sdk location (if installed) instead. + var loc2 = knownDotNetLocations[Array.IndexOf(knownDotNetLocations, loc) + 1]; + if (File.Exists(loc2)) + { + var dotnet = new FileInfo(loc2); + var sdkDir = dotnet.Directory; + if (sdkDir is not null) + { + sdkRoot = sdkDir.FullName; + } + + break; + } + } + + if (File.Exists(loc)) + { + var dotnet = new FileInfo(loc); + var sdkDir = dotnet.Directory; + if (sdkDir is not null) + { + sdkRoot = sdkDir.FullName; + } + + break; + } + } + } + + return sdkRoot; + } + + internal static NuGetVersion ConvertVersionToNuGetVersion(string version) + { + _ = NuGetVersion.TryParse(version, out var version2); + return version2!; + } + + internal static async Task InstallTemplate(string templatePackName, string runtimeIdentifier) + { + var version = string.Empty; + var templateInstallCommand = new ProcessStartOptions + { + WaitForProcessExit = true, + }.WithStartInformation( + $"{GetDotNetSdkLocation(runtimeIdentifier)}{Path.DirectorySeparatorChar}dotnet{(OperatingSystem.IsWindows() ? ".exe" : string.Empty)}", + $"new install {templatePackName}", + true, + false, + false, + true, + ProcessWindowStyle.Hidden, + Environment.CurrentDirectory); + var result = templateInstallCommand.Start(); + var results = result.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); + foreach (var resultItem in results) + { + if (resultItem.Contains("is already installed,")) + { + Console.WriteLine($"Workload package '{templatePackName}' is already installed. Did you intend to run 'update'?"); + version = "already installed"; + break; + } + else if (resultItem.LastIndexOf("::", StringComparison.Ordinal) > -1) + { + if (resultItem.Contains(" installed the following templates:")) + { + var resultItems = resultItem.Split(' ')[1].Split("::"); + version = resultItems[1]; + await Task.Delay(0).ConfigureAwait(false); + break; + } + } + } + + return version; + } + + internal static async Task UpdateTemplate( + string packName, + string packVersion, + string runtimeIdentifier) + { + if (!packVersion.Equals( + GetInstalledTemplateVersion(packName), StringComparison.OrdinalIgnoreCase)) + { + Console.WriteLine($"Update found for workload package '{packName}'."); + _ = UninstallTemplate(packName, runtimeIdentifier); + _ = await InstallTemplate(packName, runtimeIdentifier).ConfigureAwait(false); + Console.WriteLine($"Successfully updated workload package '{packName}'."); + return true; + } + + Console.WriteLine($"No updates found for workload package '{packName}'."); + return false; + } + + internal static string UninstallTemplate(string templatePackName, string runtimeIdentifier) + { + var templateUninstallCommand = new ProcessStartOptions + { + WaitForProcessExit = true, + }.WithStartInformation( + $"{DotNetSdkHelper.GetDotNetSdkLocation(runtimeIdentifier)}{Path.DirectorySeparatorChar}dotnet{(OperatingSystem.IsWindows() ? ".exe" : string.Empty)}", + $"new uninstall {templatePackName}", + false, + false, + false, + true, + ProcessWindowStyle.Hidden, + Environment.CurrentDirectory); + return templateUninstallCommand.Start(); + } + + private static string GetInstalledTemplateVersion(string packName) + { + var searchPath = $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}/.templateengine/packages/"; + var version = string.Empty; + foreach (var template in from template in Directory.EnumerateFileSystemEntries(searchPath, "*.nupkg", SearchOption.TopDirectoryOnly) + where template.Contains(packName, StringComparison.OrdinalIgnoreCase) && template.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase) + select template) + { + version = Path.GetFileNameWithoutExtension(template).Replace($"{packName}.", string.Empty); + } + + return version; + } + + private static string GetDotNetSdkFeatureBandVersion(string runtimeIdentifier) + { + var sdkLocation = GetDotNetSdkLocation(runtimeIdentifier); + var startOptions = new ProcessStartOptions + { + WaitForProcessExit = true, + }.WithStartInformation( + $"{sdkLocation}{Path.DirectorySeparatorChar}dotnet{(OperatingSystem.IsWindows() ? ".exe" : string.Empty)}", + "--list-sdks", + true, + true, + false, + true, + ProcessWindowStyle.Hidden, + Environment.CurrentDirectory); + var result = startOptions.Start().Split( + Environment.NewLine, + StringSplitOptions.RemoveEmptyEntries); + var sdks = new List(); + foreach (var line in result) + { + try + { + if (line.Contains('[') && line.Contains(']')) + { + var versionStr = line[..line.IndexOf('[')].Trim(); + var locStr = line[line.IndexOf('[')..].Trim('[', ']'); + var loc = Path.Combine(locStr, versionStr); + if (Directory.Exists(locStr) && Directory.Exists(loc)) + { + // If only 1 file it's probably the + // EnableWorkloadResolver.sentinel file that was + // never uninstalled with the rest of the sdk + if (Directory.GetFiles(loc).Length > 1) + { + sdks.Add(new DotNetSdkInfo(versionStr, new DirectoryInfo(loc))); + } + } + } + } + catch + { + // Bad line, ignore + } + } + + FilterSdks(ref sdks); + if (sdks.Count > 1) + { + Console.WriteLine("Bug found. There should have just been 1 sdk left over from the filter."); + } + + var sdk = sdks[0]; // there should only be 1 listed now. + Console.WriteLine($".NET SDK Location: {sdkLocation}"); + Console.WriteLine($"Detected .NET SDK Version: {sdk.Version}"); + var sdkVersion = ConvertVersionToSdkBand(sdk.Version); + Console.WriteLine($"Detected .NET SDK Band as: {sdkVersion}"); + var version = new Version(sdkVersion); + return version.Major < 8 + ? throw new InvalidOperationException("This tool is compatible only with .NET 8.0.100 SDK or newer.") + : sdkVersion; + } + + private static string GetDotNetSdkRID() + { + var sdkLocation = GetDotNetSdkLocation(RuntimeInformation.RuntimeIdentifier); + var startOptions = new ProcessStartOptions + { + WaitForProcessExit = true, + }.WithStartInformation( + $"{sdkLocation}{Path.DirectorySeparatorChar}dotnet{(OperatingSystem.IsWindows() ? ".exe" : string.Empty)}", + "--info", + true, + true, + false, + true, + ProcessWindowStyle.Hidden, + Environment.CurrentDirectory); + var result = startOptions.Start().Split( + Environment.NewLine, + StringSplitOptions.RemoveEmptyEntries); + var sdkRuntimeIdentifier = "Any"; + foreach (var line in result) + { + if (line.Contains("RID:")) + { + sdkRuntimeIdentifier = line.Replace("RID:", string.Empty).Trim(); + } + } + + return sdkRuntimeIdentifier; + } + + private static string ConvertVersionToSdkBand(string version) + { + var version2 = ConvertVersionToNuGetVersion(version); + return $"{version2.Major}.{version2.Minor}.{version2.Patch / 100 * 100}"; + } + + private static void FilterSdks(ref List sdks) + { + var sdk = sdks.MaxBy(netSdkInfo => ConvertVersionToNuGetVersion(netSdkInfo.Version)); + sdks.Clear(); + sdks.Add(sdk!); + } +} diff --git a/src/NuGetHelper.cs b/src/Elskom.Check.Cli.Internal/Helpers/NuGetHelper.cs similarity index 55% rename from src/NuGetHelper.cs rename to src/Elskom.Check.Cli.Internal/Helpers/NuGetHelper.cs index 908627b..74bb683 100644 --- a/src/NuGetHelper.cs +++ b/src/Elskom.Check.Cli.Internal/Helpers/NuGetHelper.cs @@ -1,91 +1,134 @@ -namespace Elskom.Check; - -internal static class NuGetHelper -{ - internal static HttpClient? HttpClient { get; set; } - - internal static async Task InstallPackageAsync(string packageName, string version, string outputPath) - { - var lowerPackageName = packageName.ToLowerInvariant(); - var lowerVersion = version.ToLowerInvariant(); - var packageResponse = await HttpClient!.GetAsync($"https://api.nuget.org/v3-flatcontainer/{lowerPackageName}/{lowerVersion}/{lowerPackageName}.{lowerVersion}.nupkg"); - if (packageResponse.StatusCode.Equals(HttpStatusCode.NotFound)) - { - throw new InvalidOperationException("bug in package install."); - } - - if (!packageResponse.IsSuccessStatusCode) - { - return; - } - - var extractionPath = $"{outputPath}/{packageName}/{version}"; - Directory.CreateDirectory(extractionPath); - var packageContents = await packageResponse.Content.ReadAsByteArrayAsync(); - if (outputPath.Equals(DotNetSdkHelper.GetDotNetSdkWorkloadRuntimePacksFolder())) - { - // properly unpackage the runtime pack. - using var ms = new MemoryStream(packageContents); - using var zipArchive = new ZipArchive(ms, ZipArchiveMode.Read, true); - foreach (var entry in zipArchive.Entries) - { - if (entry.FullName.Contains("runtimes/any/lib/net6.0/")) - { - entry.ExtractToFile(Path.Join(extractionPath, entry.Name), true); - } - else if (entry.Name.Equals("Elskom.Sdk.App.versions.txt")) - { - entry.ExtractToFile(Path.Join(extractionPath, ".version"), true); - } - } - } - else - { - var filePath = Path.Combine(extractionPath, $"{packageName}.nupkg"); - await File.WriteAllBytesAsync(filePath, packageContents); - ZipFile.ExtractToDirectory(filePath, extractionPath, true); - } - } - - internal static (string version, string fileName) GetDownloadedPackageVersion(string packageName, string inputPath) - { - var files = Directory.EnumerateFiles( - inputPath, - $"{packageName}.*").ToList(); - string version = string.Empty; - string file = string.Empty; - if (files.Any()) - { - file = files.First(); - version = file.Replace($"{packageName}.", string.Empty).Replace(".nupkg", string.Empty); - } - - return (version, file); - } - - internal static async Task ResolveWildcardPackageVersionAsync(string packageName) - { - var lowerPackageName = packageName.ToLowerInvariant(); - var versionResponse = await HttpClient!.GetAsync($"https://api.nuget.org/v3-flatcontainer/{lowerPackageName}/index.json").ConfigureAwait(false); - if (versionResponse.StatusCode.Equals(HttpStatusCode.NotFound)) - { - throw new InvalidOperationException("bug in package version request."); - } - - if (!versionResponse.IsSuccessStatusCode) - { - return string.Empty; - } - - var content = await versionResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - var versionsInfo = JsonSerializer.Deserialize(content); - var version = versionsInfo?.Versions?.LastOrDefault(); - return version ?? string.Empty; - } - - private record VersionInfo - { - [JsonPropertyName("versions")] - public string[]? Versions { get; init; } - } -} +namespace Elskom.Check; + +internal static class NuGetHelper +{ + internal static HttpClient? HttpClient { get; set; } + + internal static async Task InstallPackageAsync(string packageName, string version, string outputPath, string runtimeIdentifier) + { + var lowerPackageName = packageName.ToLowerInvariant(); + var lowerVersion = version.ToLowerInvariant(); + var packageResponse = await HttpClient!.GetAsync($"https://api.nuget.org/v3-flatcontainer/{lowerPackageName}/{lowerVersion}/{lowerPackageName}.{lowerVersion}.nupkg"); + if (packageResponse.StatusCode.Equals(HttpStatusCode.NotFound)) + { + throw new InvalidOperationException("bug in package install."); + } + + if (!packageResponse.IsSuccessStatusCode) + { + return; + } + + // strip out the '.Runtime.{runtimeIdentifier}' part of the package name for the extraction path. + var extractionPath = $"{outputPath}/{packageName.Replace($".Runtime.{runtimeIdentifier}", string.Empty)}/{version}"; + Directory.CreateDirectory(extractionPath); + var packageContents = await packageResponse.Content.ReadAsByteArrayAsync(); + if (outputPath.Equals(DotNetSdkHelper.GetDotNetSdkWorkloadRuntimePacksFolder(runtimeIdentifier))) + { + // properly unpackage the runtime pack. + // var runtimeIdentifier = packageName.Replace("Elskom.Sdk.App.Runtime.", "", StringComparison.OrdinalIgnoreCase); + using var ms = new MemoryStream(packageContents); + using var zipArchive = new ZipArchive(ms, ZipArchiveMode.Read, true); + foreach (var entry in zipArchive.Entries) + { + if (entry.FullName.Contains($"runtimes/{runtimeIdentifier}/lib/net8.0/") + || entry.FullName.Contains($"runtimes/{runtimeIdentifier}/native/")) + { + entry.ExtractToFile(Path.Join(extractionPath, entry.Name), true); + } + else if (entry.Name.Equals("Elskom.Sdk.App.versions.txt")) + { + entry.ExtractToFile(Path.Join(extractionPath, ".version"), true); + } + } + } + else + { + var filePath = Path.Combine(extractionPath, $"{packageName}.nupkg"); + await File.WriteAllBytesAsync(filePath, packageContents); + ZipFile.ExtractToDirectory(filePath, extractionPath, true); + } + } + + internal static (string version, string fileName) GetDownloadedPackageVersion(string packageName, string inputPath) + { + var files = Directory.EnumerateFiles( + inputPath, + $"{packageName}.*").ToList(); + string version = string.Empty; + string file = string.Empty; + if (files.Any()) + { + file = files.First(); + version = file.Replace($"{packageName}.", string.Empty).Replace(".nupkg", string.Empty); + } + + return (version, file); + } + + internal static async Task> ResolveWildcardWorkloadPackageVersionsAsync(string runtimeIdentifier) + { + var list = new List(); + var currentSdkPackVersion = await ResolveWildcardPackageVersionAsync( + Constants.SdkPackName).ConfigureAwait(false); + var installedSdkPackVersion = DotNetSdkHelper.GetInstalledDotNetSdkWorkloadPackVersion( + Constants.SdkPackName, runtimeIdentifier); + var currentRefPackVersion = await ResolveWildcardPackageVersionAsync( + Constants.RefPackName).ConfigureAwait(false); + var installedRefPackVersion = DotNetSdkHelper.GetInstalledDotNetSdkWorkloadPackVersion( + Constants.RefPackName, runtimeIdentifier); + list.Add(new WorkloadInfo() + { + PackageName = Constants.SdkPackName, + PackageVersion = currentSdkPackVersion, + InstalledPackageVersion = installedSdkPackVersion + }); + list.Add(new WorkloadInfo() + { + PackageName = Constants.RefPackName, + PackageVersion = currentRefPackVersion, + InstalledPackageVersion = installedRefPackVersion + }); + foreach (var runtimePack in from runtimePack in Constants.RuntimePacks + where runtimePack.Contains(runtimeIdentifier) + select runtimePack) + { + var currentRuntimePackVersion = await ResolveWildcardPackageVersionAsync(runtimePack).ConfigureAwait(false); + var installedRuntimePackVersion = DotNetSdkHelper.GetInstalledDotNetSdkWorkloadRuntimePackVersion(runtimePack.Replace($".Runtime.{runtimeIdentifier}", string.Empty, StringComparison.OrdinalIgnoreCase), runtimeIdentifier); + list.Add(new WorkloadInfo() + { + PackageName = runtimePack, + PackageVersion = currentRuntimePackVersion, + InstalledPackageVersion = installedRuntimePackVersion, + }); + } + + return list; + } + + internal static async Task ResolveWildcardPackageVersionAsync(string packageName) + { + var lowerPackageName = packageName.ToLowerInvariant(); + var versionResponse = await HttpClient!.GetAsync($"https://api.nuget.org/v3-flatcontainer/{lowerPackageName}/index.json").ConfigureAwait(false); + if (versionResponse.StatusCode.Equals(HttpStatusCode.NotFound)) + { + throw new InvalidOperationException("bug in package version request."); + } + + if (!versionResponse.IsSuccessStatusCode) + { + return string.Empty; + } + + var content = await versionResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + var versionsInfo = JsonSerializer.Deserialize(content); + var version = versionsInfo?.Versions?.LastOrDefault(); + return version ?? string.Empty; + } + + private record VersionInfo + { + [JsonPropertyName("versions")] + public string[]? Versions { get; init; } + } +} diff --git a/src/ProcessStartOptions.cs b/src/Elskom.Check.Cli.Internal/ProcessStartOptions.cs similarity index 76% rename from src/ProcessStartOptions.cs rename to src/Elskom.Check.Cli.Internal/ProcessStartOptions.cs index 1673d52..79d03ce 100644 --- a/src/ProcessStartOptions.cs +++ b/src/Elskom.Check.Cli.Internal/ProcessStartOptions.cs @@ -1,90 +1,74 @@ -namespace Elskom.Check; - -internal sealed class ProcessStartOptions -{ - internal ProcessStartInfo? StartInfo { get; private set; } - - public bool WaitForProcessExit { get; set; } - - internal ProcessStartOptions WithStartInformation(string fileName, string arguments, bool redirectStandardOutput, bool redirectStandardError, bool useShellExecute, bool createNoWindow, ProcessWindowStyle windowStyle, string workingDirectory) - { - this.StartInfo = new ProcessStartInfo - { - FileName = fileName, - Arguments = arguments, - RedirectStandardOutput = redirectStandardOutput, - RedirectStandardError = redirectStandardError, - UseShellExecute = useShellExecute, - CreateNoWindow = createNoWindow, - WindowStyle = windowStyle, - WorkingDirectory = workingDirectory, - }; - return this; - } - - internal string Start() - { - if (this.StartInfo is null) - { - throw new InvalidOperationException("StartInfo must not be null."); - } - - if (!File.Exists(this.StartInfo.FileName)) - { - throw new FileNotFoundException("File to execute does not exist."); - } - - StringBuilder? stdout = null; - StringBuilder? stderr = null; - using var proc = Process.Start(this.StartInfo); - proc!.OutputDataReceived += (_, e) => - { - if (stdout is null) - { - stdout = new StringBuilder(); - stdout.Append(e.Data); - stdout.AppendLine(); - } - else - { - stdout.AppendLine(e.Data); - } - }; - proc.ErrorDataReceived += (_, e) => - { - if (stderr is null) - { - stderr = new StringBuilder(); - stderr.Append(e.Data); - stderr.AppendLine(); - } - else - { - stderr.AppendLine(e.Data); - } - }; - if (this.StartInfo.RedirectStandardOutput) - { - proc.BeginOutputReadLine(); - } - - if (this.StartInfo.RedirectStandardError) - { - proc.BeginErrorReadLine(); - } - - if (this.WaitForProcessExit) - { - proc.WaitForExit(); - } - - return (stdout is not null, stderr is not null) switch - { - (true, false) => $"{stdout}", - (true, true) => $@"{stdout} -{stderr}", - (false, false) => string.Empty, - (false, true) => $"{stderr}", - }; - } -} +namespace Elskom.Check; + +internal sealed class ProcessStartOptions +{ + internal ProcessStartInfo? StartInfo { get; private set; } + + public bool WaitForProcessExit { get; set; } + + internal ProcessStartOptions WithStartInformation(string fileName, string arguments, bool redirectStandardOutput, bool redirectStandardError, bool useShellExecute, bool createNoWindow, ProcessWindowStyle windowStyle, string workingDirectory) + { + this.StartInfo = new ProcessStartInfo + { + FileName = fileName, + Arguments = arguments, + RedirectStandardOutput = redirectStandardOutput, + RedirectStandardError = redirectStandardError, + UseShellExecute = useShellExecute, + CreateNoWindow = createNoWindow, + WindowStyle = windowStyle, + WorkingDirectory = workingDirectory, + }; + return this; + } + + internal string Start() + { + if (this.StartInfo is null) + { + throw new InvalidOperationException("StartInfo must not be null."); + } + + if (!File.Exists(this.StartInfo.FileName)) + { + throw new FileNotFoundException("File to execute does not exist."); + } + + StringBuilder? stdout = null; + StringBuilder? stderr = null; + using var proc = Process.Start(this.StartInfo); + proc!.OutputDataReceived += (_, e) => + { + stdout ??= new StringBuilder(); + _ = stdout.AppendLine(e.Data); + }; + proc.ErrorDataReceived += (_, e) => + { + stderr ??= new StringBuilder(); + _ = stderr.AppendLine(e.Data); + }; + if (this.StartInfo.RedirectStandardOutput) + { + proc.BeginOutputReadLine(); + } + + if (this.StartInfo.RedirectStandardError) + { + proc.BeginErrorReadLine(); + } + + if (this.WaitForProcessExit) + { + proc.WaitForExit(); + } + + return (stdout is not null, stderr is not null) switch + { + (true, false) => $"{stdout}", + (true, true) => $@"{stdout} +{stderr}", + (false, false) => string.Empty, + (false, true) => $"{stderr}", + }; + } +} diff --git a/src/Elskom.Check.Cli.Internal/WorkloadInfo.cs b/src/Elskom.Check.Cli.Internal/WorkloadInfo.cs new file mode 100644 index 0000000..2d058fc --- /dev/null +++ b/src/Elskom.Check.Cli.Internal/WorkloadInfo.cs @@ -0,0 +1,9 @@ +namespace Elskom.Check; + +internal class WorkloadInfo +{ + internal string PackageName { get; set; } = null!; + internal string PackageVersion { get; set; } = null!; + internal string InstalledPackageVersion { get; set; } = null!; + internal string OutputPath { get; set; } = null!; +} diff --git a/src/WorkloadManifest.cs b/src/Elskom.Check.Cli.Internal/WorkloadManifest.cs similarity index 70% rename from src/WorkloadManifest.cs rename to src/Elskom.Check.Cli.Internal/WorkloadManifest.cs index fa8ffe4..7d78579 100644 --- a/src/WorkloadManifest.cs +++ b/src/Elskom.Check.Cli.Internal/WorkloadManifest.cs @@ -1,157 +1,173 @@ -namespace Elskom.Check; - -public class WorkloadManifest -{ - [JsonPropertyName("version")] - public string Version { get; set; } = null!; - - [JsonPropertyName("workloads")] - public Workload Workloads { get; set; } = null!; - - [JsonPropertyName("packs")] - public WorkloadPacks Packs { get; set; } = null!; - - internal static WorkloadManifest Create(IReadOnlyDictionary packVersions) - { - return new WorkloadManifest - { - Version = packVersions.GetValueOrDefault(Constants.SdkPackName)!, - Workloads = Workload.Create(), - Packs = WorkloadPacks.Create(packVersions), - }; - } - - internal void WriteJsonFile(string sdkVersion) - { - var workloadFolder = DotNetSdkHelper.GetDotNetSdkWorkloadFolder( - Constants.WorkloadName, - sdkVersion); - if (!Directory.Exists(workloadFolder)) - { - _ = Directory.CreateDirectory(workloadFolder); - } - - var json = JsonSerializer.Serialize( - this, - new JsonSerializerOptions - { - WriteIndented = true, - - }); - File.WriteAllText( - Path.Join( - workloadFolder, - "WorkloadManifest.json"), - json); - } - - internal void WriteTargetsFile(string sdkVersion) - { - var workloadFolder = DotNetSdkHelper.GetDotNetSdkWorkloadFolder( - Constants.WorkloadName, - sdkVersion); - File.WriteAllText( - $"{workloadFolder}{Path.DirectorySeparatorChar}WorkloadManifest.targets", - @$" - - - - {this.Packs.ElskomSdkApp.Version} - - - - - -"); - } - - public class Workload - { - [JsonPropertyName("elskom")] - public ElskomWorkload Elskom { get; set; } = null!; - - public class ElskomWorkload - { - [JsonPropertyName("description")] - public string Description { get; set; } = null!; - - [JsonPropertyName("packs")] - public List Packs { get; set; } = null!; - - internal static ElskomWorkload Create(string description, List packs) - => new() - { - Description = description, - Packs = packs, - }; - } - - internal static Workload Create() - => new() - { - Elskom = ElskomWorkload.Create( - ".NET SDK Workload for building Els_kom, and it's plugins.", - new List - { - Constants.SdkPackName, - Constants.RefPackName, - Constants.RuntimePackName, - Constants.TemplatePackName, - }), - }; - } - - public class WorkloadPacks - { - [JsonPropertyName(Constants.SdkPackName)] - public WorkloadPack ElskomSdk { get; set; } = null!; - - [JsonPropertyName(Constants.RefPackName)] - public WorkloadPack ElskomSdkAppRef { get; set; } = null!; - - [JsonPropertyName(Constants.RuntimePackName)] - public WorkloadPack ElskomSdkApp { get; set; } = null!; - - [JsonPropertyName(Constants.TemplatePackName)] - public WorkloadPack ElskomSdkTemplates { get; set; } = null!; - - internal static WorkloadPacks Create(IReadOnlyDictionary packVersions) - => new() - { - ElskomSdk = WorkloadPack.Create(Constants.Sdk, Constants.SdkPackName, packVersions), - ElskomSdkAppRef = WorkloadPack.Create(Constants.Framework, Constants.RefPackName, packVersions), - ElskomSdkApp = WorkloadPack.Create(Constants.Framework, Constants.RefPackName, packVersions), - ElskomSdkTemplates = WorkloadPack.Create(Constants.Template, Constants.TemplatePackName, packVersions) - }; - - public class WorkloadPack - { - [JsonPropertyName("kind")] - public string Kind { get; set; } = null!; - - [JsonPropertyName("version")] - public string Version { get; set; } = null!; - - internal static WorkloadPack Create( - string kind, - string packName, - IReadOnlyDictionary packVersions) - => new() - { - Kind = kind, - Version = packVersions.GetValueOrDefault(packName)!, - }; - - internal void UpdateVersion(string version) - { - this.Version = version; - } - } - } -} +namespace Elskom.Check; + +internal class WorkloadManifest +{ + [JsonPropertyName("version")] + public string Version { get; set; } = null!; + + [JsonPropertyName("workloads")] + public Workload Workloads { get; set; } = null!; + + [JsonPropertyName("packs")] + public WorkloadPacks Packs { get; set; } = null!; + + internal static WorkloadManifest Create(IReadOnlyDictionary packVersions) + => new() + { + Version = packVersions.GetValueOrDefault(Constants.SdkPackName)!, + Workloads = Workload.Create(), + Packs = WorkloadPacks.Create(packVersions), + }; + + internal void WriteJsonFile(string sdkVersion, string runtimeIdentifier) + { + var workloadFolder = DotNetSdkHelper.GetDotNetSdkWorkloadFolder( + Constants.WorkloadName, + sdkVersion, + runtimeIdentifier); + if (!Directory.Exists(workloadFolder)) + { + _ = Directory.CreateDirectory(workloadFolder); + } + + var json = JsonSerializer.Serialize( + this, + new JsonSerializerOptions + { + WriteIndented = true, + }); + File.WriteAllText( + Path.Join( + workloadFolder, + "WorkloadManifest.json"), + json); + } + + internal void WriteTargetsFile(string sdkVersion, string runtimeIdentifier) + { + var workloadFolder = DotNetSdkHelper.GetDotNetSdkWorkloadFolder( + Constants.WorkloadName, + sdkVersion, + runtimeIdentifier); + File.WriteAllText( + $"{workloadFolder}{Path.DirectorySeparatorChar}WorkloadManifest.targets", + $""" + + + + + {this.Packs.ElskomSdkApp.Version} + + + + + + + """); + } + + public class Workload + { + [JsonPropertyName("elskom")] + public ElskomWorkload Elskom { get; set; } = null!; + + public class ElskomWorkload + { + [JsonPropertyName("description")] + public string Description { get; set; } = null!; + + [JsonPropertyName("packs")] + public List Packs { get; set; } = null!; + + internal static ElskomWorkload Create(string description, List packs) + => new() + { + Description = description, + Packs = packs, + }; + } + + internal static Workload Create() + => new() + { + Elskom = ElskomWorkload.Create( + ".NET SDK Workload for building Els_kom, and it's plugins.", + [ + Constants.SdkPackName, + Constants.RefPackName, + Constants.RuntimePackName, + Constants.TemplatePackName, + ]), + }; + } + + public class WorkloadPacks + { + [JsonPropertyName(Constants.SdkPackName)] + public WorkloadPack ElskomSdk { get; set; } = null!; + + [JsonPropertyName(Constants.RefPackName)] + public WorkloadPack ElskomSdkAppRef { get; set; } = null!; + + [JsonPropertyName(Constants.RuntimePackName)] + public WorkloadPack ElskomSdkApp { get; set; } = null!; + + [JsonPropertyName(Constants.TemplatePackName)] + public WorkloadPack ElskomSdkTemplates { get; set; } = null!; + + [JsonIgnore] + public Dictionary Packs { get; set; } = null!; + + internal static WorkloadPacks Create(IReadOnlyDictionary packVersions) + => new() + { + ElskomSdk = WorkloadPack.Create(Constants.Sdk, Constants.SdkPackName, packVersions), + ElskomSdkAppRef = WorkloadPack.Create(Constants.Framework, Constants.RefPackName, packVersions), + ElskomSdkApp = WorkloadPack.Create(Constants.Framework, Constants.RuntimePackName, packVersions), + ElskomSdkTemplates = WorkloadPack.Create(Constants.Template, Constants.TemplatePackName, packVersions), + }; + + internal void SetPacksDictionary() + => this.Packs = new Dictionary() + { + { Constants.SdkPackName, this.ElskomSdk }, + { Constants.RefPackName, this.ElskomSdkAppRef }, + { Constants.RuntimePackName, this.ElskomSdkApp }, + { Constants.TemplatePackName, this.ElskomSdkTemplates }, + }; + + public class WorkloadPack + { + [JsonIgnore] + public string Name { get; set; } = null!; + + [JsonPropertyName("kind")] + public string Kind { get; set; } = null!; + + [JsonPropertyName("version")] + public string Version { get; set; } = null!; + + internal static WorkloadPack Create( + string kind, + string packName, + IReadOnlyDictionary packVersions) + => new() + { + Kind = kind, + Version = packVersions.GetValueOrDefault(packName)!, + Name = packName, + }; + + internal void UpdateVersion(string version) + { + this.Version = version; + } + } + } +} diff --git a/src/Elskom.Check.Cli.Internal/WorkloadSettings.cs b/src/Elskom.Check.Cli.Internal/WorkloadSettings.cs new file mode 100644 index 0000000..6abc7e2 --- /dev/null +++ b/src/Elskom.Check.Cli.Internal/WorkloadSettings.cs @@ -0,0 +1,10 @@ +namespace Elskom.Check; + +internal class WorkloadSettings : CommandSettings +{ + [CommandOption("--sdk")] + public string? SdkVersion { get; set; } + + [CommandOption("--rid")] + public string? RuntimeIdentifier { get; set; } +} diff --git a/src/Elskom.Check/Directory.Build.targets b/src/Elskom.Check/Directory.Build.targets new file mode 100644 index 0000000..7095cff --- /dev/null +++ b/src/Elskom.Check/Directory.Build.targets @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/Elskom.Check.csproj b/src/Elskom.Check/Elskom.Check.csproj similarity index 54% rename from src/Elskom.Check.csproj rename to src/Elskom.Check/Elskom.Check.csproj index 78835e3..d174ec6 100644 --- a/src/Elskom.Check.csproj +++ b/src/Elskom.Check/Elskom.Check.csproj @@ -1,39 +1,49 @@ - - - - Exe - net6.0 - enable - enable - true - elskom-check - LatestMajor - ../artifacts - app.manifest - latest - Elskom.Net.Check - .NET Elskom Workload Check Tool - 1.0.15 - Els_kom org. - Els_kom org. - A dotnet tool for installing/uninstalling and updating the Elskom workload. - - A dotnet tool for installing/uninstalling and updating the Elskom workload. - - - Fixed issue where users cant use the tool when only the .NET 7 runtime is installed. - - Copyright © 2021~2022 - https://github.com/Elskom/installer - MIT - https://github.com/Elskom/installer - $(Version) - true - true - embedded - Elskom.Check - False - preview - - - + + + + Exe + net8.0 + enable + enable + true + elskom-check + LatestMajor + ../../artifacts + app.manifest + latest + Elskom.Net.Check + .NET Elskom Workload Check Tool + 1.0.16 + Els_kom org. + Els_kom org. + A dotnet tool for installing/uninstalling and updating the Elskom workload. + + A dotnet tool for installing/uninstalling and updating the Elskom workload. + + + Fixed issue where the restructured Runtime packages and reference packs are not ever installed/updated. + Fixed issue where tool does not require .NET SDK version 8.0.100 or newer to be installed. + Fixed issue where a specific runtime version (specified by a --rid argument in the cli) cannot be requested. + Fixed issue where on x64 machines specifying --rid win-x86 would not install the workload into the "Program Files (x86)" install of the .NET SDK. + Fixed issue where installing on an ARM64 Machine might not set the ARM64 runtime to go inside of the normal "Program Files" install of the .NET SDK where the ARM64 version of it lies. + Added the printing of the path to where the .NET SDK install that the tool will install the workload into for the user to be able to update the workloads easier. + Overall restructure of the codebase as well to make things easier to maintain. + + Copyright © 2021~2024 + https://github.com/Elskom/installer + MIT + https://github.com/Elskom/installer + $(Version) + true + true + embedded + Elskom.Check + False + preview + + + + + + + diff --git a/src/Elskom.Check/Program.cs b/src/Elskom.Check/Program.cs new file mode 100644 index 0000000..7deab5d --- /dev/null +++ b/src/Elskom.Check/Program.cs @@ -0,0 +1,24 @@ +// set the console title. +Console.Title = "Elskom workload cross-platform installer"; + +// Need to register the code pages provider for code that parses +// and later needs ISO-8859-2 +Encoding.RegisterProvider( + CodePagesEncodingProvider.Instance); + +// Test that it loads +_ = Encoding.GetEncoding("ISO-8859-2"); +var app = new CommandApp(); +app.Configure(config => +{ + _ = config.AddCommand("install").WithDescription("Installs the Workload."); + _ = config.AddCommand("uninstall").WithDescription("Uninstalls the Workload."); + _ = config.AddCommand("update").WithDescription("Updates the Workload."); +}); + +using (NuGetHelper.HttpClient = new HttpClient()) +{ + var result = await app.RunAsync(args).ConfigureAwait(false); + Console.Title = ""; + return result; +} diff --git a/src/app.manifest b/src/Elskom.Check/app.manifest similarity index 100% rename from src/app.manifest rename to src/Elskom.Check/app.manifest diff --git a/src/InstallCommand.cs b/src/InstallCommand.cs deleted file mode 100644 index c9d70e7..0000000 --- a/src/InstallCommand.cs +++ /dev/null @@ -1,197 +0,0 @@ -namespace Elskom.Check; - -public class InstallCommand : AsyncCommand -{ - public override async Task ExecuteAsync([NotNull] CommandContext context, [NotNull] WorkloadSettings settings) - { - DotNetSdkHelper.GetOrUpdateSdkVersion(ref settings); - var sdkPackVersion = await NuGetHelper.ResolveWildcardPackageVersionAsync( - Constants.SdkPackName).ConfigureAwait(false); - var refPackVersion = await NuGetHelper.ResolveWildcardPackageVersionAsync( - Constants.RefPackName).ConfigureAwait(false); - var runtimePackVersion = await NuGetHelper.ResolveWildcardPackageVersionAsync( - Constants.RuntimePackName).ConfigureAwait(false); - var installedSdkPackVersion = DotNetSdkHelper.GetInstalledDotNetSdkWorkloadPackVersion( - Constants.SdkPackName); - var installedRefPackVersion = DotNetSdkHelper.GetInstalledDotNetSdkWorkloadPackVersion( - Constants.RefPackName); - var installedRuntimePackVersion = DotNetSdkHelper.GetInstalledDotNetSdkWorkloadRuntimePackVersion( - Constants.RuntimePackName); - if (!installedRefPackVersion.Equals(string.Empty)) - { - if (installedRefPackVersion.EndsWith("-dev") - || DotNetSdkHelper.ConvertVersionToNuGetVersion(installedRefPackVersion) - > DotNetSdkHelper.ConvertVersionToNuGetVersion(refPackVersion)) - { - Console.WriteLine("Picked up newer installed reference pack, using that instead."); - refPackVersion = installedRefPackVersion; - } - } - - if (!installedRuntimePackVersion.Equals(string.Empty)) - { - if (installedRuntimePackVersion.EndsWith("-dev") - || DotNetSdkHelper.ConvertVersionToNuGetVersion(installedRuntimePackVersion) - > DotNetSdkHelper.ConvertVersionToNuGetVersion(runtimePackVersion)) - { - Console.WriteLine("Picked up newer installed runtime pack, using that instead."); - runtimePackVersion = installedRuntimePackVersion; - } - } - - if (await InstallPackageAsync( - Constants.SdkPackName, - sdkPackVersion, - installedSdkPackVersion, - settings.SdkVersion!, - DotNetSdkHelper.GetDotNetSdkWorkloadPacksFolder()).ConfigureAwait(false)) - { - Console.WriteLine($"Successfully installed workload package '{Constants.SdkPackName}'."); - } - - if (await InstallPackageAsync( - Constants.RefPackName, - refPackVersion, - installedRefPackVersion, - settings.SdkVersion!, - DotNetSdkHelper.GetDotNetSdkWorkloadPacksFolder()).ConfigureAwait(false)) - { - Console.WriteLine($"Successfully installed workload package '{Constants.RefPackName}'."); - } - - if (await InstallPackageAsync( - Constants.RuntimePackName, - runtimePackVersion, - installedRuntimePackVersion, - settings.SdkVersion!, - DotNetSdkHelper.GetDotNetSdkWorkloadRuntimePacksFolder()).ConfigureAwait(false)) - { - Console.WriteLine($"Successfully installed workload package '{Constants.RuntimePackName}'."); - } - - // avoid installing the templates and the workload manifest if sdkPackVersion, refPackVersion, - // and runtimePackVersion is null or empty. - if (!string.IsNullOrEmpty(sdkPackVersion) - && !string.IsNullOrEmpty(refPackVersion) - && !string.IsNullOrEmpty(runtimePackVersion)) - { - var templatePackVersion = await DownloadPackageAsync( - Constants.TemplatePackName).ConfigureAwait(false); - if (!templatePackVersion.Equals("already installed")) - { - Console.WriteLine($"Successfully installed workload package '{Constants.TemplatePackName}'."); - } - - InstallManifest(settings.SdkVersion!, new Dictionary - { - { Constants.SdkPackName, sdkPackVersion }, - { Constants.RefPackName, refPackVersion }, - { Constants.RuntimePackName, runtimePackVersion }, - { Constants.TemplatePackName, templatePackVersion }, - }); - } - else - { - throw new InvalidOperationException("Could not install the workload due to failure to obtain package versions."); - } - - return 0; - } - - private static void InstallManifest(string sdkVersion, IReadOnlyDictionary packVersions) - { - foreach (var packVersion in packVersions) - { - if (string.IsNullOrEmpty(packVersion.Value)) - { - throw new InvalidOperationException($"Workload package '{packVersion.Key}' not found."); - } - else if (packVersion.Value.Equals("already installed")) - { - throw new InvalidOperationException("The workload was already installed. Check above for the proper command to update."); - } - } - - var workloadManifest = WorkloadManifest.Create(packVersions); - workloadManifest.WriteJsonFile(sdkVersion); - workloadManifest.WriteTargetsFile(sdkVersion); - using var fs1 = File.Create( - DotNetSdkHelper.GetDotNetSdkWorkloadMetadataInstalledWorkloads( - Constants.WorkloadName, - sdkVersion)); - } - - internal static async Task InstallPackageAsync(string packName, string packVersion, string installedPackVersion, string sdkVersion, string outputPath) - { - if (string.IsNullOrEmpty(installedPackVersion)) - { - if (!string.IsNullOrEmpty(packVersion)) - { - await NuGetHelper.InstallPackageAsync( - packName, - packVersion, - outputPath).ConfigureAwait(false); - if (!packName.Equals(Constants.RuntimePackName)) - { - await using var fs = File.Create( - DotNetSdkHelper.GetDotNetSdkWorkloadMetadataInstalledPacks( - packName, - packVersion, - sdkVersion)).ConfigureAwait(false); - } - - return true; - } - else - { - Console.WriteLine($"No version for workload package '{packName}' is published to nuget.org."); - } - } - else - { - Console.WriteLine($"Workload package '{packName}' is already installed. Did you intend to run 'update'?"); - } - - return false; - } - - internal static async Task DownloadPackageAsync(string packName) - { - var version = string.Empty; - var templateInstallCommand = new ProcessStartOptions - { - WaitForProcessExit = true, - }.WithStartInformation( - $"{DotNetSdkHelper.GetDotNetSdkLocation()}{Path.DirectorySeparatorChar}dotnet{(OperatingSystem.IsWindows() ? ".exe" : string.Empty)}", - $"new -i {packName}", - true, - false, - false, - true, - ProcessWindowStyle.Hidden, - Environment.CurrentDirectory); - var result = templateInstallCommand.Start(); - var results = result.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); - foreach (var resultItem in results) - { - if (resultItem.IndexOf("::", StringComparison.Ordinal) > -1) - { - if (resultItem.Contains(" installed the following templates:")) - { - var resultItems = resultItem.Split(' '); - resultItems = resultItems[1].Split("::"); - version = resultItems[1]; - await Task.Delay(0).ConfigureAwait(false); - } - } - else if (resultItem.Contains(" is already installed, version:")) - { - Console.WriteLine($"Workload package '{packName}' is already installed. Did you intend to run 'update'?"); - version = "already installed"; - break; - } - } - - return version; - } -} diff --git a/src/Program.cs b/src/Program.cs deleted file mode 100644 index f9544c7..0000000 --- a/src/Program.cs +++ /dev/null @@ -1,36 +0,0 @@ -// set the console title. -Console.Title = "Elskom workload cross-platform installer"; - -// Need to register the code pages provider for code that parses -// and later needs ISO-8859-2 -Encoding.RegisterProvider( - CodePagesEncodingProvider.Instance); - -// Test that it loads -_ = Encoding.GetEncoding("ISO-8859-2"); -var app = new CommandApp(); -app.Configure(config => -{ - config.AddCommand("install").WithDescription("Installs the Workload."); - config.AddCommand("uninstall").WithDescription("Uninstalls the Workload."); - config.AddCommand("update").WithDescription("Updates the Workload."); -}); - -var finalArgs = new List(); -var firstArg = args.FirstOrDefault()?.Trim().ToLowerInvariant() ?? string.Empty; -if (firstArg != "install" && firstArg != "uninstall" && firstArg != "update") -{ - finalArgs.Add("install"); -} - -if (args.Any()) -{ - finalArgs.AddRange(args); -} - -using (NuGetHelper.HttpClient = new HttpClient()) -{ - var result = await app.RunAsync(finalArgs).ConfigureAwait(false); - Console.Title = ""; - return result; -} diff --git a/src/UninstallCommand.cs b/src/UninstallCommand.cs deleted file mode 100644 index 3f03821..0000000 --- a/src/UninstallCommand.cs +++ /dev/null @@ -1,122 +0,0 @@ -namespace Elskom.Check; - -public class UninstallCommand : Command -{ - public override int Execute([NotNull] CommandContext context, [NotNull] WorkloadSettings settings) - { - DotNetSdkHelper.GetOrUpdateSdkVersion(ref settings); - return Uninstall(settings.SdkVersion!); - } - - private static int Uninstall(string sdkVersion) - { - var sdkPackVersion = DotNetSdkHelper.GetInstalledDotNetSdkWorkloadPackVersion( - Constants.SdkPackName); - var refPackVersion = DotNetSdkHelper.GetInstalledDotNetSdkWorkloadPackVersion( - Constants.RefPackName); - var runtimePackVersion = DotNetSdkHelper.GetInstalledDotNetSdkWorkloadRuntimePackVersion( - Constants.RuntimePackName); - - // delete the directories to the workload. - _ = UninstallManifest(sdkVersion); - - // delete the directories to the workload. - if (UninstallPackage( - Constants.SdkPackName, - sdkPackVersion, - DotNetSdkHelper.GetDotNetSdkWorkloadPacksFolder(), - sdkVersion)) - { - Console.WriteLine($"Successfully uninstalled workload package '{Constants.SdkPackName}'."); - } - else - { - Console.WriteLine($"Workload package '{Constants.SdkPackName}' was already uninstalled."); - } - - if (UninstallPackage( - Constants.RefPackName, - refPackVersion, - DotNetSdkHelper.GetDotNetSdkWorkloadPacksFolder(), - sdkVersion)) - { - Console.WriteLine($"Successfully uninstalled workload package '{Constants.RefPackName}'."); - } - else - { - Console.WriteLine($"Workload package '{Constants.RefPackName}' was already uninstalled."); - } - - if (UninstallPackage( - Constants.RuntimePackName, - runtimePackVersion, - DotNetSdkHelper.GetDotNetSdkWorkloadRuntimePacksFolder(), - sdkVersion)) - { - Console.WriteLine($"Successfully uninstalled workload package '{Constants.RuntimePackName}'."); - } - else - { - Console.WriteLine($"Workload package '{Constants.RuntimePackName}' was already uninstalled."); - } - - var templateUninstallCommand = new ProcessStartOptions - { - WaitForProcessExit = true, - }.WithStartInformation( - $"{DotNetSdkHelper.GetDotNetSdkLocation()}{Path.DirectorySeparatorChar}dotnet{(OperatingSystem.IsWindows() ? ".exe" : string.Empty)}", - $"new -u {Constants.TemplatePackName}", - true, - true, - false, - true, - ProcessWindowStyle.Hidden, - Environment.CurrentDirectory); - var result = templateUninstallCommand.Start(); - if (result.Contains("The template package '") && result.Contains("' is not found.")) - { - Console.WriteLine($"Workload package '{Constants.TemplatePackName}' was already uninstalled."); - } - else - { - Console.WriteLine($"Successfully uninstalled workload package '{Constants.TemplatePackName}'."); - } - - return 0; - } - - internal static bool UninstallPackage(string packName, string packVersion, string packFolder, string sdkVersion) - { - if (!string.IsNullOrEmpty(packVersion)) - { - Directory.Delete(Path.Join(packFolder, packName), true); - if (!packName.Equals(Constants.RuntimePackName)) - { - File.Delete( - DotNetSdkHelper.GetDotNetSdkWorkloadMetadataInstalledPacks( - Constants.SdkPackName, - packVersion, - sdkVersion)); - } - - return true; - } - - return false; - } - - private static bool UninstallManifest(string sdkVersion) - { - var workloadFolder = DotNetSdkHelper.GetDotNetSdkWorkloadFolder( - Constants.WorkloadName, - sdkVersion); - if (Directory.Exists(workloadFolder)) - { - Directory.Delete(workloadFolder, true); - File.Delete(DotNetSdkHelper.GetDotNetSdkWorkloadMetadataInstalledWorkloads(Constants.WorkloadName, sdkVersion)); - return true; - } - - return false; - } -} diff --git a/src/UpdateCommand.cs b/src/UpdateCommand.cs deleted file mode 100644 index 8f66231..0000000 --- a/src/UpdateCommand.cs +++ /dev/null @@ -1,201 +0,0 @@ -namespace Elskom.Check; - -public class UpdateCommand : AsyncCommand -{ - public override async Task ExecuteAsync([NotNull] CommandContext context, [NotNull] WorkloadSettings settings) - { - DotNetSdkHelper.GetOrUpdateSdkVersion(ref settings); - return await UpdateAsync(settings.SdkVersion!).ConfigureAwait(false); - } - - private static async Task UpdateAsync(string sdkVersion) - { - var currentSdkPackVersion = await NuGetHelper.ResolveWildcardPackageVersionAsync( - Constants.SdkPackName).ConfigureAwait(false); - var currentRefPackVersion = await NuGetHelper.ResolveWildcardPackageVersionAsync( - Constants.RefPackName).ConfigureAwait(false); - var currentRuntimePackVersion = await NuGetHelper.ResolveWildcardPackageVersionAsync( - Constants.RuntimePackName).ConfigureAwait(false); - var currentTemplatePackVersion = await NuGetHelper.ResolveWildcardPackageVersionAsync( - Constants.TemplatePackName).ConfigureAwait(false); - var packVersions = new Dictionary - { - { Constants.SdkPackName, currentSdkPackVersion }, - { Constants.RefPackName, currentRefPackVersion }, - { Constants.RuntimePackName, currentRuntimePackVersion }, - { Constants.TemplatePackName, currentTemplatePackVersion }, - }; - return await UpdateManifestAsync(packVersions, sdkVersion).ConfigureAwait(false); - } - - private static async Task UpdateManifestAsync(IReadOnlyDictionary packVersions, string sdkVersion) - { - var workloadFolder = DotNetSdkHelper.GetDotNetSdkWorkloadFolder( - Constants.WorkloadName, - sdkVersion); - var text = await File.ReadAllTextAsync( - Path.Join( - workloadFolder, - "WorkloadManifest.json")).ConfigureAwait(false); - var workloadManifest = JsonSerializer.Deserialize(text); - var sdkPackUpdated = await UpdateWorkloadPackAsync( - packVersions, - workloadManifest!.Packs.ElskomSdk, - Constants.SdkPackName, - sdkVersion, - DotNetSdkHelper.GetDotNetSdkWorkloadPacksFolder()).ConfigureAwait(false); - if (sdkPackUpdated) - { - workloadManifest.Version = - packVersions.GetValueOrDefault(Constants.SdkPackName)!; - } - - var refPackUpdated = false; - var runtimePackUpdated = false; - var installedRefPackVersion = DotNetSdkHelper.GetInstalledDotNetSdkWorkloadPackVersion( - Constants.RefPackName); - var installedRuntimePackVersion = DotNetSdkHelper.GetInstalledDotNetSdkWorkloadRuntimePackVersion( - Constants.RuntimePackName); - if (!installedRefPackVersion.Equals(string.Empty)) - { - if (installedRefPackVersion.EndsWith("-dev") - || DotNetSdkHelper.ConvertVersionToNuGetVersion(installedRefPackVersion) - > DotNetSdkHelper.ConvertVersionToNuGetVersion(workloadManifest.Packs.ElskomSdkAppRef.Version)) - { - Console.WriteLine("Picked up newer installed reference pack, using that instead."); - workloadManifest.Packs.ElskomSdkAppRef.UpdateVersion(installedRefPackVersion); - refPackUpdated = true; - } - } - - if (!installedRuntimePackVersion.Equals(string.Empty)) - { - if (installedRuntimePackVersion.EndsWith("-dev") - || DotNetSdkHelper.ConvertVersionToNuGetVersion(installedRuntimePackVersion) - > DotNetSdkHelper.ConvertVersionToNuGetVersion(workloadManifest.Packs.ElskomSdkApp.Version)) - { - Console.WriteLine("Picked up newer installed runtime pack, using that instead."); - workloadManifest.Packs.ElskomSdkApp.UpdateVersion(installedRuntimePackVersion); - runtimePackUpdated = true; - } - } - - if (!refPackUpdated) - { - refPackUpdated = await UpdateWorkloadPackAsync( - packVersions, - workloadManifest.Packs.ElskomSdkAppRef, - Constants.RefPackName, - sdkVersion, - DotNetSdkHelper.GetDotNetSdkWorkloadPacksFolder()).ConfigureAwait(false); - } - - if (!runtimePackUpdated) - { - runtimePackUpdated = await UpdateWorkloadPackAsync( - packVersions, - workloadManifest.Packs.ElskomSdkApp, - Constants.RuntimePackName, - sdkVersion, - DotNetSdkHelper.GetDotNetSdkWorkloadRuntimePacksFolder()).ConfigureAwait(false); - } - - var templatePackUpdated = await UpdateWorkloadTemplatePackAsync( - packVersions, - workloadManifest.Packs.ElskomSdkTemplates, - Constants.TemplatePackName).ConfigureAwait(false); - if (sdkPackUpdated) - { - Console.WriteLine($"Workload Manifest is now version: '{workloadManifest.Version}'."); - Console.WriteLine($"Workload Sdk is now version: '{workloadManifest.Packs.ElskomSdk.Version}'."); - } - - if (runtimePackUpdated) - { - Console.WriteLine($"Workload Runtime Pack is now version: '{workloadManifest.Packs.ElskomSdkApp.Version}'."); - } - - if (refPackUpdated) - { - Console.WriteLine($"Workload Reference Pack is now version: '{workloadManifest.Packs.ElskomSdkAppRef.Version}'."); - } - - if (templatePackUpdated) - { - Console.WriteLine($"Workload Template Pack is now version: '{workloadManifest.Packs.ElskomSdkTemplates.Version}'."); - } - - if (sdkPackUpdated || runtimePackUpdated || refPackUpdated || templatePackUpdated) - { - workloadManifest.WriteJsonFile(sdkVersion); - workloadManifest.WriteTargetsFile(sdkVersion); - } - - return 0; - } - - private static async Task UpdateWorkloadPackAsync( - IReadOnlyDictionary packVersions, - WorkloadManifest.WorkloadPacks.WorkloadPack workloadPack, - string packName, - string sdkVersion, - string outputPath) - { - if (!workloadPack.Version.Equals( - packVersions.GetValueOrDefault(packName)!)) - { - Console.WriteLine($"Update found for workload package '{packName}'."); - UninstallCommand.UninstallPackage( - packName, - workloadPack.Version, - outputPath, - sdkVersion); - workloadPack.UpdateVersion( - packVersions.GetValueOrDefault(packName)!); - await InstallCommand.InstallPackageAsync( - packName, - workloadPack.Version, - string.Empty, - sdkVersion, - outputPath).ConfigureAwait(false); - Console.WriteLine($"Successfully updated workload package '{packName}'."); - return true; - } - - Console.WriteLine($"No updates found for workload package '{packName}'."); - return false; - } - - private static async Task UpdateWorkloadTemplatePackAsync( - IReadOnlyDictionary packVersions, - WorkloadManifest.WorkloadPacks.WorkloadPack workloadPack, - string packName) - { - if (!workloadPack.Version.Equals( - packVersions.GetValueOrDefault(packName))) - { - Console.WriteLine($"Update found for workload package '{packName}'."); - var templateUninstallCommand = new ProcessStartOptions - { - WaitForProcessExit = true, - }.WithStartInformation( - $"{DotNetSdkHelper.GetDotNetSdkLocation()}{Path.DirectorySeparatorChar}dotnet{(OperatingSystem.IsWindows() ? ".exe" : string.Empty)}", - $"new -u {Constants.TemplatePackName}", - false, - false, - false, - true, - ProcessWindowStyle.Hidden, - Environment.CurrentDirectory); - _ = templateUninstallCommand.Start(); - var packVersion = await InstallCommand.DownloadPackageAsync( - packName).ConfigureAwait(false); - workloadPack.UpdateVersion(packVersion); - Console.WriteLine($"Successfully updated workload package '{packName}'."); - return true; - } - - Console.WriteLine($"No updates found for workload package '{packName}'."); - return false; - } -} diff --git a/src/WorkloadSettings.cs b/src/WorkloadSettings.cs deleted file mode 100644 index b49de92..0000000 --- a/src/WorkloadSettings.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Elskom.Check; - -public class WorkloadSettings : CommandSettings -{ - [CommandOption("--sdk")] - public string? SdkVersion { get; set; } -}