From ecc17652a70b0f1136fb4ac18e10936ff627125b Mon Sep 17 00:00:00 2001 From: Jerome Laban Date: Wed, 6 Nov 2024 21:46:05 -0500 Subject: [PATCH] fix: Adjust installed workloads detection --- UnoCheck/Checkups/DotNetWorkloadsCheckup.cs | 64 +++++++------- UnoCheck/DotNet/DotNetWorkloadManager.cs | 97 +++++++++++++-------- 2 files changed, 95 insertions(+), 66 deletions(-) diff --git a/UnoCheck/Checkups/DotNetWorkloadsCheckup.cs b/UnoCheck/Checkups/DotNetWorkloadsCheckup.cs index 74fa7d2..2731333 100644 --- a/UnoCheck/Checkups/DotNetWorkloadsCheckup.cs +++ b/UnoCheck/Checkups/DotNetWorkloadsCheckup.cs @@ -23,10 +23,10 @@ public DotNetWorkloadsCheckup(SharedState sharedState, string sdkVersion, Manife SdkRoot = dotnet.DotNetSdkLocation.FullName; SdkVersion = sdkVersion; - if (sharedState.TryGetState(StateKey.EntryPoint, StateKey.TargetPlatforms, out var activeTargetPlatforms)) - { - TargetPlatforms = activeTargetPlatforms; - } + if (sharedState.TryGetState(StateKey.EntryPoint, StateKey.TargetPlatforms, out var activeTargetPlatforms)) + { + TargetPlatforms = activeTargetPlatforms; + } RequiredWorkloads = requiredWorkloads.Where(FilterPlatform).ToArray(); NuGetPackageSources = nugetPackageSources; } @@ -36,32 +36,32 @@ private bool FilterPlatform(Manifest.DotNetWorkload w) var arch = Util.IsArm64 ? "arm64" : "x64"; var targetPlatform = Util.Platform + "/" + arch; - if (w.SupportedPlatforms?.Any(sp => sp == (sp.Contains("/") ? targetPlatform : Util.Platform.ToString())) ?? false) - { - switch (w.Id) - { - case "android" when TargetPlatforms.HasFlag(TargetPlatform.Android): - case "maui-android" when TargetPlatforms.HasFlag(TargetPlatform.Android): - case "maui" when (TargetPlatforms.HasFlag(TargetPlatform.Android) + if (w.SupportedPlatforms?.Any(sp => sp == (sp.Contains("/") ? targetPlatform : Util.Platform.ToString())) ?? false) + { + switch (w.Id) + { + case "android" when TargetPlatforms.HasFlag(TargetPlatform.Android): + case "maui-android" when TargetPlatforms.HasFlag(TargetPlatform.Android): + case "maui" when (TargetPlatforms.HasFlag(TargetPlatform.Android) || TargetPlatforms.HasFlag(TargetPlatform.iOS) || TargetPlatforms.HasFlag(TargetPlatform.macOS)): - case "ios" when TargetPlatforms.HasFlag(TargetPlatform.iOS): - case "macos" when TargetPlatforms.HasFlag(TargetPlatform.macOS): - case "maccatalyst" when TargetPlatforms.HasFlag(TargetPlatform.macOS): - case "wasm-tools" when TargetPlatforms.HasFlag(TargetPlatform.WebAssembly): - return true; - } - - } - - return false; - } + case "ios" when TargetPlatforms.HasFlag(TargetPlatform.iOS): + case "macos" when TargetPlatforms.HasFlag(TargetPlatform.macOS): + case "maccatalyst" when TargetPlatforms.HasFlag(TargetPlatform.macOS): + case "wasm-tools" when TargetPlatforms.HasFlag(TargetPlatform.WebAssembly): + return true; + } + + } + + return false; + } public readonly string SdkRoot; public readonly string SdkVersion; public readonly string[] NuGetPackageSources; public readonly Manifest.DotNetWorkload[] RequiredWorkloads; - public readonly TargetPlatform TargetPlatforms; + public readonly TargetPlatform TargetPlatforms; public override IEnumerable DeclareDependencies(IEnumerable checkupIds) => new[] { new CheckupDependency("dotnet") }; @@ -92,7 +92,8 @@ public override async Task Examine(SharedState history) var manager = new DotNetWorkloadManager(SdkRoot, SdkVersion, NuGetPackageSources); var missingWorkloads = new List(); - var installedPackageWorkloads = await manager.GetInstalledWorkloads(); + var installedWorkloads = await manager.GetInstalledWorkloads(); + var availablePackageWorkloads = await manager.GetAvailableWorkloads(); foreach (var rp in RequiredWorkloads) { @@ -106,20 +107,23 @@ public override async Task Examine(SharedState history) if (Util.Verbose) { - foreach (var installedWorload in installedPackageWorkloads) + foreach (var installedWorload in availablePackageWorkloads) { - ReportStatus($"Reported installed: {installedWorload.id}: {installedWorload.version}", null); + ReportStatus($"Available workload: {installedWorload.id}: {installedWorload.version}", null); } } - if (installedPackageWorkloads.FirstOrDefault(ip => ip.id.Equals(rp.WorkloadManifestId, StringComparison.OrdinalIgnoreCase) && NuGetVersion.TryParse(ip.version, out var ipVersion) && ipVersion >= rpVersion) is { id: not null } installed) + if ( + availablePackageWorkloads.FirstOrDefault(ip => ip.id.Equals(rp.WorkloadManifestId, StringComparison.OrdinalIgnoreCase) && NuGetVersion.TryParse(ip.version, out var ipVersion) && ipVersion >= rpVersion) is { id: not null } available + && installedWorkloads.Contains(rp.Id) + ) { - ReportStatus($"{installed.id} ({installed.version}/{installed.sdkVersion}) installed.", Status.Ok); + ReportStatus($"{available.id} ({available.version}/{available.sdkVersion}) is installed.", Status.Ok); } else { - ReportStatus($"{rp.Id} ({rp.PackageId} : {rp.Version}) not installed.", Status.Error); - missingWorkloads.Add(rp); + ReportStatus($"{rp.Id} ({rp.PackageId} : {rp.Version}) is not installed.", Status.Error); + missingWorkloads.Add(rp); } } diff --git a/UnoCheck/DotNet/DotNetWorkloadManager.cs b/UnoCheck/DotNet/DotNetWorkloadManager.cs index 06e6922..99d93c6 100644 --- a/UnoCheck/DotNet/DotNetWorkloadManager.cs +++ b/UnoCheck/DotNet/DotNetWorkloadManager.cs @@ -25,6 +25,8 @@ namespace DotNetCheck.DotNet { public class DotNetWorkloadManager { + private record WorkloadListResult(string[] installed); + public DotNetWorkloadManager(string sdkRoot, string sdkVersion, params string[] nugetPackageSources) { SdkRoot = sdkRoot; @@ -81,18 +83,23 @@ string WriteRollbackFile(Manifest.DotNetWorkload[] workloads) return rollbackFile; } - const string RollbackOutputBeginMarker = "==workloadRollbackDefinitionJsonOutputStart=="; - const string RollbackOutputEndMarker = "==workloadRollbackDefinitionJsonOutputEnd=="; + private (string begin, string end) RollbackOutputMarker = ( + "==workloadRollbackDefinitionJsonOutputStart==", + "==workloadRollbackDefinitionJsonOutputEnd=="); + + private (string begin, string end) ListOutputMarker = ( + "==workloadListJsonOutputStart==", + "==workloadListJsonOutputEnd=="); - public async Task<(string id, string version, string sdkVersion)[]> GetInstalledWorkloads() + public async Task GetInstalledWorkloads() { var dotnetExe = Path.Combine(SdkRoot, DotNetSdk.DotNetExeName); var args = new List { "workload", - "update", - "--print-rollback" + "list", + "--machine-readable" }; var r = await Util.WrapShellCommandWithSudo(dotnetExe, DotNetCliWorkingDir, Util.Verbose, args.ToArray()); @@ -101,33 +108,32 @@ string WriteRollbackFile(Manifest.DotNetWorkload[] workloads) if (r.ExitCode != 0) throw new Exception("Workload command failed: `dotnet " + string.Join(' ', args) + "`"); - var output = string.Join(" ", r.StandardOutput); + var output = FilterWorkloadCommandOutput(string.Join(" ", r.StandardOutput), ListOutputMarker); - var isNet8OrBelow = NuGetVersion.Parse(SdkVersion) < DotNetCheck.Manifest.DotNetSdk.Version9Preview3; + // example of output {"installed":["wasm-tools"],"updateAvailable":[]} + var workloads = JsonSerializer.Deserialize(output); - if(isNet8OrBelow) - { - var startIndex = output.IndexOf(RollbackOutputBeginMarker); - var endIndex = output.IndexOf(RollbackOutputEndMarker); - - if (startIndex >= 0 && endIndex >= 0) - { - // net8 and earlier use markers - var start = startIndex + RollbackOutputBeginMarker.Length; - output = output.Substring(start, endIndex - start); - } - } - else + return workloads.installed; + } + + public async Task<(string id, string version, string sdkVersion)[]> GetAvailableWorkloads() + { + var dotnetExe = Path.Combine(SdkRoot, DotNetSdk.DotNetExeName); + + var args = new List { - // This is needed to match the output of - // https://github.com/dotnet/sdk/blob/9a965db906ca70f57c4d44df1e0da09a5b662441/src/Cli/dotnet/commands/dotnet-workload/WorkloadIntegrityChecker.cs#L43 - var startIndex = output.IndexOf("{"); + "workload", + "update", + "--print-rollback" + }; - if (startIndex >= 0) - { - output = output.Substring(startIndex); - } - } + var r = await Util.WrapShellCommandWithSudo(dotnetExe, DotNetCliWorkingDir, Util.Verbose, args.ToArray()); + + // Throw if this failed with a bad exit code + if (r.ExitCode != 0) + throw new Exception("Workload command failed: `dotnet " + string.Join(' ', args) + "`"); + + var output = FilterWorkloadCommandOutput(string.Join(" ", r.StandardOutput), RollbackOutputMarker); var workloads = JsonSerializer.Deserialize>(output); @@ -194,17 +200,36 @@ async Task CliRepair() throw new Exception("Workload Repair failed: `dotnet " + string.Join(' ', args) + "`"); } - string GetInstalledWorkloadMetadataDir() + private string FilterWorkloadCommandOutput(string output, (string begin, string end) marker) { - int last2DigitsTo0(int versionBuild) - => versionBuild / 100 * 100; + var isNet8OrBelow = NuGetVersion.Parse(SdkVersion) < DotNetCheck.Manifest.DotNetSdk.Version9Preview3; - if (!Version.TryParse(SdkVersion.Split('-')[0], out var result)) - throw new ArgumentException("Invalid 'SdkVersion' version: " + SdkVersion); + if (isNet8OrBelow) + { + var startIndex = output.IndexOf(marker.begin); + var endIndex = output.IndexOf(marker.end); - var sdkVersionBand = $"{result.Major}.{result.Minor}.{last2DigitsTo0(result.Build)}"; - - return Path.Combine(SdkRoot, "metadata", "workloads", sdkVersionBand, "InstalledWorkloads"); + if (startIndex >= 0 && endIndex >= 0) + { + // net8 and earlier use markers + var start = startIndex + marker.begin.Length; + output = output.Substring(start, endIndex - start); + } + } + else + { + // This is needed to match the output of + // https://github.com/dotnet/sdk/blob/9a965db906ca70f57c4d44df1e0da09a5b662441/src/Cli/dotnet/commands/dotnet-workload/WorkloadIntegrityChecker.cs#L43 + var startIndex = output.IndexOf("{"); + + if (startIndex >= 0) + { + output = output.Substring(startIndex); + } + } + + return output; } + } }