diff --git a/.gitignore b/.gitignore index 67111bb0..bd0ccefa 100644 --- a/.gitignore +++ b/.gitignore @@ -351,3 +351,5 @@ MigrationBackup/ .DS_Store +# Rider +.idea/** \ No newline at end of file diff --git a/UnoCheck/Checkups/DotNetCheckup.cs b/UnoCheck/Checkups/DotNetCheckup.cs index 6c816524..85288408 100644 --- a/UnoCheck/Checkups/DotNetCheckup.cs +++ b/UnoCheck/Checkups/DotNetCheckup.cs @@ -97,10 +97,10 @@ public override async Task Examine(SharedState history) var remedies = new List(); - if (Util.CI || Util.IsLinux) + if (Util.CI || !Util.IsWindows) { remedies.AddRange(missingSdks - .Select(ms => new DotNetSdkScriptInstallSolution(ms.Version))); + .Select(ms => new DotNetSdkPackageManagerOrScriptInstallSolution(ms.Version))); } else { diff --git a/UnoCheck/Checkups/DotNetNewUnoTemplatesCheckup.cs b/UnoCheck/Checkups/DotNetNewUnoTemplatesCheckup.cs index a116a784..871bd0fd 100644 --- a/UnoCheck/Checkups/DotNetNewUnoTemplatesCheckup.cs +++ b/UnoCheck/Checkups/DotNetNewUnoTemplatesCheckup.cs @@ -31,7 +31,7 @@ internal class DotNetNewUnoTemplatesCheckup : Checkup public override async Task Examine(SharedState history) { - var dotnetOutput = GetDotNetNewInstalledList(); + var dotnetOutput = GetDotNetNewInstalledList(history); var legacyVersion = GetInstalledVersion(dotnetOutput, _unoLegacyTemplatesOutputRegex); var version = GetInstalledVersion(dotnetOutput, _unoTemplatesOutputRegex); @@ -63,11 +63,13 @@ public override async Task Examine(SharedState history) return DiagnosticResult.Ok(this); } - private string GetDotNetNewInstalledList() + private string GetDotNetNewInstalledList(SharedState? history) { + var dotnetCommand = new DotNetSdk(history).DotNetExecutable; + // Running 'dotnet new uninstall' without any package ID will list all installed // dotnet new templates along with their versions. - var processInfo = new ProcessStartInfo("dotnet", "new uninstall"); + var processInfo = new ProcessStartInfo(dotnetCommand, "new uninstall"); processInfo.RedirectStandardOutput = true; processInfo.UseShellExecute = false; processInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"] = "en-US"; diff --git a/UnoCheck/Checkups/LinuxNinjaPresenceCheckup.cs b/UnoCheck/Checkups/LinuxNinjaPresenceCheckup.cs index e9581bf1..667fa852 100644 --- a/UnoCheck/Checkups/LinuxNinjaPresenceCheckup.cs +++ b/UnoCheck/Checkups/LinuxNinjaPresenceCheckup.cs @@ -20,35 +20,34 @@ public override bool IsPlatformSupported(Platform platform) public override async Task Examine(SharedState history) { var shellHasNinjaRunnerResult = ShellProcessRunner.Run("ninja", "--version"); - var shellRunnerAptResult = ShellProcessRunner.Run("apt", "--version"); - - var ninjaVersionExitCode = shellHasNinjaRunnerResult.ExitCode; var output = shellHasNinjaRunnerResult.GetOutput().Trim(); - var shellRunnerAboutLinuxResult = ShellProcessRunner.Run("lsb_release", "-a"); - - var linuxRelease = shellRunnerAboutLinuxResult.GetOutput().Trim(); + var hasNinja = shellHasNinjaRunnerResult.Success; + if (hasNinja) + { + ReportStatus($"Ninja Build Version: {output}", Status.Ok); - var hasNinja = ninjaVersionExitCode == 0; - var hasApt = shellRunnerAptResult.ExitCode == 0; - var isDebianBased = linuxRelease.Contains(Ubuntu) || linuxRelease.Contains(Debian); + return await Task.FromResult(DiagnosticResult.Ok(this)); + }; - if (!hasNinja && (isDebianBased || hasApt)) + var foundPackage = await LinuxPackageManagerWrapper.SearchForPackage(LinuxNinjaSolution.NinjaPackageNamesWithWrappers, true); + if (foundPackage) { return await Task.FromResult( new DiagnosticResult( Status.Error, this, - new Suggestion(InstallMessage, new LinuxNinjaSolution()))); + new Suggestion(InstallMessage, new LinuxNinjaSolution()) + )); } - ReportStatus($"Ninja Build Version: {output}", Status.Ok); - - return await Task.FromResult(DiagnosticResult.Ok(this)); + return await Task.FromResult( + new DiagnosticResult( + Status.Error, + this, + new Suggestion(InstallMessage, "Ninja-build is missing, follow the installation instructions here: https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages"))); } - private const string Ubuntu = "Ubuntu"; - private const string Debian = "Debian"; private const string InstallMessage = "Install Ninja Build"; } } \ No newline at end of file diff --git a/UnoCheck/DotNet/DotNetSdk.cs b/UnoCheck/DotNet/DotNetSdk.cs index 137a91d6..bceb0a41 100644 --- a/UnoCheck/DotNet/DotNetSdk.cs +++ b/UnoCheck/DotNet/DotNetSdk.cs @@ -20,12 +20,14 @@ public class DotNetSdk { public readonly string[] KnownDotnetLocations; - public readonly FileInfo DotNetExeLocation; + private readonly FileInfo DotNetExeLocation; public readonly DirectoryInfo DotNetSdkLocation; public static string DotNetExeName => Util.IsWindows ? "dotnet.exe" : "dotnet"; + public string DotNetExecutable => Exists ? DotNetExeLocation.FullName : "dotnet"; + public DotNetSdk(SharedState sharedState) { KnownDotnetLocations = Util.Platform switch @@ -47,12 +49,16 @@ public DotNetSdk(SharedState sharedState) }, Platform.Linux => new string[] { - // /home/user/share/dotnet/dotnet + // /usr/share/dotnet/dotnet Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), - "share", + Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "dotnet", - DotNetExeName) + DotNetExeName), + // ~/.dotnet/dotnet + Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), + ".dotnet", + DotNetExeName), }, _ => new string[] { } }; @@ -64,12 +70,13 @@ public DotNetSdk(SharedState sharedState) if (Directory.Exists(envSdkRoot)) sdkRoot = envSdkRoot; } - - if (string.IsNullOrEmpty(sdkRoot) || !Directory.Exists(sdkRoot)) + + string environmentOverride = Environment.GetEnvironmentVariable("DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR"); + if (!string.IsNullOrEmpty(environmentOverride)) { - sdkRoot = Microsoft.DotNet.NativeWrapper.EnvironmentProvider.GetDotnetExeDirectory(); + sdkRoot = environmentOverride; } - + if (string.IsNullOrEmpty(sdkRoot) || !Directory.Exists(sdkRoot)) { var l = FindDotNetLocations(); @@ -79,6 +86,11 @@ public DotNetSdk(SharedState sharedState) } } + if (string.IsNullOrEmpty(sdkRoot) || !Directory.Exists(sdkRoot)) + { + sdkRoot = Microsoft.DotNet.NativeWrapper.EnvironmentProvider.GetDotnetExeDirectory(log: (s) => Util.Log(s.ToString())); + } + sharedState.SetEnvironmentVariable("DOTNET_ROOT", sdkRoot); // First try and use the actual resolver logic diff --git a/UnoCheck/LinuxPackageManagerWrapper.cs b/UnoCheck/LinuxPackageManagerWrapper.cs new file mode 100644 index 00000000..600aa2cf --- /dev/null +++ b/UnoCheck/LinuxPackageManagerWrapper.cs @@ -0,0 +1,245 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; + +namespace DotNetCheck; + +public class LinuxPackageManagerWrapper +{ + public string Label { get; } + public string InstallExecutable { get; } + public string UpdateExecutable { get; } + public string SearchExecutable { get; } + + public string InstallFormat { get; } + public string UpdateFormat { get; } + public string SearchFormat { get; } + public bool NeedsSudoForInstall { get; } + public bool NeedsSudoForUpdate { get; } + public bool NeedsSudoForSearch { get; } + + public LinuxPackageManagerWrapper(string label, string installExecutable, string updateExecutable, string searchExecutable, string installFormat, string updateFormat, string searchFormat, bool needsSudoForInstall, bool needsSudoForUpdate, bool needsSudoForSearch) + { + Label = label; + InstallExecutable = installExecutable; + UpdateExecutable = updateExecutable; + SearchExecutable = searchExecutable; + InstallFormat = installFormat; + UpdateFormat = updateFormat; + SearchFormat = searchFormat; + NeedsSudoForInstall = needsSudoForInstall; + NeedsSudoForUpdate = needsSudoForUpdate; + NeedsSudoForSearch = needsSudoForSearch; + } + + public static IEnumerable<(LinuxPackageManagerWrapper wrapper, string packageName)> MatchPackageNamesWithStandardSupport( + string debianName, + string archName, + string fedoraRhelName, + string oldFedoraRhelName, + string openSuseName + ) + { + var packageNames = new[] + { + debianName, + archName, + fedoraRhelName, + oldFedoraRhelName, + openSuseName + }; + + Debug.Assert(packageNames.Length == StandardSupport.Count()); + + return StandardSupport.Zip(packageNames); + } + + public static IEnumerable StandardSupport => new[] + { + Debian, + Arch, + FedoraRHEL, + OldFedoraRHEL, + OpenSUSE + }; + + public static LinuxPackageManagerWrapper Debian { get; } = new LinuxPackageManagerWrapper( + nameof(Debian), + "apt-get", + "apt-get", + "apt", + "install -y {0}", + "update", + "search ^{0}$", + true, + true, + false); + + public static LinuxPackageManagerWrapper Arch { get; } = new LinuxPackageManagerWrapper( + nameof(Arch), + "pacman", + "pacman", + "[", + "-S --noconfirm {0}", + "-Sy", + "\"$(pacman -Sqs {0} | grep '^{0}$' | wc -l)\" -eq 1 ]", // a hack to search for an exact match + true, + true, + false); + + // TODO + public static LinuxPackageManagerWrapper FedoraRHEL { get; } = new LinuxPackageManagerWrapper( + nameof(FedoraRHEL), + "dnf", + "dnf", + "[", + "install -y {0}", + "makecache", + "\"$(dnf repoquery --queryformat '%{NAME}' {0} | grep '^{0}$' | wc -l)\" -eq 1 ]", // a hack to search for an exact match + true, + true, + false); + + // TODO + public static LinuxPackageManagerWrapper OldFedoraRHEL { get; } = new LinuxPackageManagerWrapper( + nameof(OldFedoraRHEL), + "yum", + "yum", + "yum", + "install -y {0}", + "makecache", + "list {0}", + true, + true, + false); + + // TODO + public static LinuxPackageManagerWrapper OpenSUSE { get; } = new LinuxPackageManagerWrapper( + nameof(OpenSUSE), + "zypper", + "zypper", + "zypper", + "-n install {0}", + "refresh", + "se --match-exact {0}", + true, + true, + false); + + public async Task Update() + { + var result = NeedsSudoForUpdate ? + await Util.WrapShellCommandWithSudo(UpdateExecutable, null, true, UpdateFormat.Split(" ")) : + ShellProcessRunner.Run(UpdateExecutable, UpdateFormat); + + return result.Success; + } + + public async Task SearchForPackage(string packageName, bool updateFirst = true) + { + if (updateFirst) + { + var updateResult = await Update(); + + if (!updateResult) + { + return false; + } + } + + var searchResult = + NeedsSudoForSearch ? + await Util.WrapShellCommandWithSudo(SearchExecutable, null, true, string.Format(SearchFormat, packageName).Split(" ")) : + ShellProcessRunner.Run(SearchExecutable, string.Format(SearchFormat, packageName)); + + return searchResult.Success; + } + + public async Task InstallPackage(string packageName, bool updateFirst = true) + { + if (updateFirst) + { + var updateResult = await Update(); + + if (!updateResult) + { + return false; + } + } + + var installResult = + NeedsSudoForInstall ? + await Util.WrapShellCommandWithSudo(InstallExecutable, null, true, string.Format(InstallFormat, packageName).Split(" ")) : + ShellProcessRunner.Run(InstallExecutable, string.Format(InstallFormat, packageName)); + + return installResult.Success; + } + + public static async Task InstallPackage(IEnumerable<(LinuxPackageManagerWrapper wrapper, string packageName)> distros, bool updateFirst) + { + Debug.Assert(distros.Count() != 0); + + foreach (var distro in distros) + { + var wrapper = distro.wrapper; + if (IsCorrectWrapper(wrapper)) + { + if (await wrapper.InstallPackage(distro.packageName, updateFirst)) + { + Util.Log($"Installed {distro.packageName} using {wrapper.SearchExecutable}"); + return true; + } + else + { + Util.Log($"Found {wrapper.Label}, but couldn't install {distro.packageName}"); + return false; + } + } + else + { + Util.Log($"Installing: Couldn't find {distro.wrapper.Label}, moving on"); + } + } + + Util.Log($"Couldn't find any package manager executable to install {distros.First().packageName}"); + return false; + } + + public static async Task SearchForPackage(IEnumerable<(LinuxPackageManagerWrapper wrapper, string packageName)> distros, bool updateFirst) + { + Debug.Assert(distros.Count() != 0); + + foreach (var distro in distros) + { + var wrapper = distro.wrapper; + if (IsCorrectWrapper(distro.wrapper)) + { + if (await wrapper.SearchForPackage(distro.packageName, updateFirst)) + { + Util.Log($"Found {distro.packageName} using {wrapper.SearchExecutable}"); + return true; + } + else + { + Util.Log($"Found {wrapper.Label}, but couldn't find {distro.packageName}"); + return false; + } + } + else + { + Util.Log($"Searching: Couldn't detect {wrapper.Label}, moving on"); + } + } + + return false; + } + + private static bool IsCorrectWrapper(LinuxPackageManagerWrapper wrapper) + { + // `which` is not POSIX + return ShellProcessRunner.Run("command", $"-v {wrapper.SearchExecutable}").Success && + ShellProcessRunner.Run("command", $"-v {wrapper.UpdateExecutable}").Success && + ShellProcessRunner.Run("command", $"-v {wrapper.InstallExecutable}").Success; + } +} \ No newline at end of file diff --git a/UnoCheck/Process/ShellProcessRunner.cs b/UnoCheck/Process/ShellProcessRunner.cs index e847d76b..5c430f1a 100644 --- a/UnoCheck/Process/ShellProcessRunner.cs +++ b/UnoCheck/Process/ShellProcessRunner.cs @@ -48,8 +48,8 @@ public ShellProcessRunnerOptions(string exe, string args, CancellationToken canc public class ShellProcessRunner { - public static string MacOSShell - => File.Exists("/bin/zsh") ? "/bin/zsh" : "/bin/bash"; + // sh should be available for all POSIX + public static string UnixShell => Environment.GetEnvironmentVariable("SHELL") ?? "sh"; public static ShellProcessResult Run(string executable, string args) { @@ -85,7 +85,7 @@ public ShellProcessRunner(ShellProcessRunnerOptions options) // process.StartInfo.FileName = Util.IsWindows ? "cmd.exe" : (File.Exists("/bin/zsh") ? "/bin/zsh" : "/bin/bash"); // process.StartInfo.Arguments = Util.IsWindows ? $"/c \"{executable} {args}\"" : $"-c \"{executable} {args}\""; - process.StartInfo.FileName = Util.IsWindows ? Options.Executable : MacOSShell; + process.StartInfo.FileName = Util.IsWindows ? Options.Executable : UnixShell; process.StartInfo.Arguments = Util.IsWindows ? Options.Args : tmpFile; } else diff --git a/UnoCheck/Solutions/DotNetNewTemplatesInstallSolution.cs b/UnoCheck/Solutions/DotNetNewTemplatesInstallSolution.cs index df58393b..822f097d 100644 --- a/UnoCheck/Solutions/DotNetNewTemplatesInstallSolution.cs +++ b/UnoCheck/Solutions/DotNetNewTemplatesInstallSolution.cs @@ -29,22 +29,24 @@ public DotNetNewTemplatesInstallSolution( public override async Task Implement(SharedState sharedState, CancellationToken cancellationToken) { + var dotnetCommand = new DotNetSdk(sharedState).DotNetExecutable; + var version = _requestedVersion ?? await NuGetHelper.GetLatestPackageVersionAsync(UnoTemplatesPackageName, ToolInfo.CurrentVersion.IsPrerelease); if (_uninstallLegacy) { - var uninstallCli = new ShellProcessRunner(new ShellProcessRunnerOptions("dotnet", $"new uninstall {UnoLegacyTemplatesPackageName}")); + var uninstallCli = new ShellProcessRunner(new ShellProcessRunnerOptions(dotnetCommand, $"new uninstall {UnoLegacyTemplatesPackageName}")); uninstallCli.WaitForExit(); } if (_uninstallExisting) { - var uninstallCli = new ShellProcessRunner(new ShellProcessRunnerOptions("dotnet", $"new uninstall {UnoTemplatesPackageName}")); + var uninstallCli = new ShellProcessRunner(new ShellProcessRunnerOptions(dotnetCommand, $"new uninstall {UnoTemplatesPackageName}")); uninstallCli.WaitForExit(); } - var cli = new ShellProcessRunner(new ShellProcessRunnerOptions("dotnet", $"new install {UnoTemplatesPackageName}::{version}") { Verbose = Util.Verbose }); + var cli = new ShellProcessRunner(new ShellProcessRunnerOptions(dotnetCommand, $"new install {UnoTemplatesPackageName}::{version}") { Verbose = Util.Verbose }); cli.WaitForExit(); } } diff --git a/UnoCheck/Solutions/DotNetSdkPackageManagerOrScriptInstallSolution.cs b/UnoCheck/Solutions/DotNetSdkPackageManagerOrScriptInstallSolution.cs new file mode 100644 index 00000000..3f945a37 --- /dev/null +++ b/UnoCheck/Solutions/DotNetSdkPackageManagerOrScriptInstallSolution.cs @@ -0,0 +1,134 @@ +using DotNetCheck; +using DotNetCheck.Models; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace DotNetCheck.Solutions +{ + public class DotNetSdkPackageManagerOrScriptInstallSolution : Solution + { + const string installScriptBash = "https://dot.net/v1/dotnet-install.sh"; + const string installScriptPwsh = "https://dot.net/v1/dotnet-install.ps1"; + const string environmentInstructionsUrl = "https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script#set-environment-variables"; + + private readonly (LinuxPackageManagerWrapper, string)[] LinuxDotNet8PackageNames = new[] + { + (LinuxPackageManagerWrapper.Debian, "dotnet-sdk-8.0"), // may or may not be available on latest LTS as of 30/11/2023 + // (LinuxPackageManagerWrapper.Arch, "dotnet-sdk"), // Arch doesn't have a stable package name, uses dotnet-sdk for latest sdk and relies on aur for older releases + (LinuxPackageManagerWrapper.FedoraRHEL, "dotnet-sdk-8.0"), // not available yet as of 30/11/2023, even Microsoft's own docs are misleading on this + (LinuxPackageManagerWrapper.OldFedoraRHEL, "dotnet-sdk-8.0"), // will likely be unavailable, not tested + // (LinuxPackageManagerWrapper.OpenSUSE, "dotnet-sdk-8.0") // not in the standard repos, must add Microsoft's repo + }; + + private readonly (LinuxPackageManagerWrapper, string)[] LinuxDotNet7PackageNames = new[] + { + (LinuxPackageManagerWrapper.Debian, "dotnet-sdk-7.0"), + // (LinuxPackageManagerWrapper.Arch, "dotnet-sdk"), // Arch doesn't have a stable package name, uses dotnet-sdk for latest sdk and relies on aur for older releases + (LinuxPackageManagerWrapper.FedoraRHEL, "dotnet-sdk-7.0"), + (LinuxPackageManagerWrapper.OldFedoraRHEL, "dotnet-sdk-7.0"), + // (LinuxPackageManagerWrapper.OpenSUSE, "dotnet-sdk-7.0") // not in the standard repos, must add Microsoft's repo + }; + + public DotNetSdkPackageManagerOrScriptInstallSolution(string version) + { + Version = version; + } + + public readonly string Version; + + public override async Task Implement(SharedState sharedState, CancellationToken cancellationToken) + { + await base.Implement(sharedState, cancellationToken); + + string sdkRoot = default; + + if (sharedState != null && sharedState.TryGetEnvironmentVariable("DOTNET_ROOT", out var envSdkRoot)) + { + if (Directory.Exists(envSdkRoot)) + sdkRoot = envSdkRoot; + } + + Debug.Assert(!string.IsNullOrEmpty(sdkRoot)); + + if (!Util.IsWindows && sdkRoot.StartsWith("/usr")) // installed using a package manager + { + // We first try to install the missing version(s) using the package manager too. + // Failing that, we do NOT attempt to use the automated script as it will either fail or + // cause conflicts with the existing managed installation. + + if (Util.Platform == Platform.Linux) // TODO: do the same for MacOS (perhaps using Homebrew) + { + var packageNamesWithWrappers = Version.Split(".")[0] switch + { + "7" => LinuxDotNet7PackageNames, + "8" => LinuxDotNet8PackageNames, + _ => Array.Empty<(LinuxPackageManagerWrapper, string)>() + } ; + + if (packageNamesWithWrappers.Length > 0) + { + Debug.Assert(Util.Platform == Platform.Linux); + var installedPackage = await LinuxPackageManagerWrapper.InstallPackage(packageNamesWithWrappers, true); + + if (installedPackage) + { + ReportStatus($"SUCCESS: a .NET {Version.Split(".")[0]} version was installed successfully using a package manager."); + + return; + } + else + { + ReportStatus($"FAIL: installing a .NET {Version.Split(".")[0]} version failed using a package manager."); + } + } + } + + ReportStatus( + $""" + FAIL: installing missing .NET SDKs using uno-check will fail or interfere with the existing .NET installation if it was installed using a package manager. + It's advised to install all SDK versions in the same way. Either uninstall the existing installation and reinstall using {installScriptBash} then rerun uno-check or + install the missing version using your package manager. + """); + + return; + } + + if (!Util.IsWindows && !ShellProcessRunner.Run("command", $"-v bash").Success) + { + // the script specifically requires bash in the shebang and also has some bashisms + ReportStatus($"FAIL: bash was not found. Installing using the script from {installScriptBash} requires bash."); + } + + var scriptUrl = Util.IsWindows ? installScriptPwsh : installScriptBash; + var scriptPath = Path.Combine(Path.GetTempPath(), Util.IsWindows ? "dotnet-install.ps1" : "dotnet-install.sh"); + + Util.Log($"Downloading dotnet-install script: {scriptUrl}"); + + var http = new HttpClient(); + var data = await http.GetStringAsync(scriptUrl); + File.WriteAllText(scriptPath, data); + + var exe = Util.IsWindows ? "powershell" : "bash"; + + var args = Util.IsWindows + ? $"\"{scriptPath}\" -InstallDir '{sdkRoot}' -Version {Version}" + : $"\"{scriptPath}\" --install-dir '{sdkRoot}' --version {Version}"; + + Util.Log($"Executing dotnet-install script..."); + Util.Log($"\t{exe} {args}"); + + // Launch the process + var p = new ShellProcessRunner(new ShellProcessRunnerOptions(exe, args)); + + p.WaitForExit(); + + ReportStatus($"WARNING: dotnet was installed from {scriptUrl}, but the script doesn't add the install location to the user's PATH environment variable, you must manually add it. To learn more, visit {environmentInstructionsUrl}"); + } + } +} diff --git a/UnoCheck/Solutions/DotNetSdkScriptInstallSolution.cs b/UnoCheck/Solutions/DotNetSdkScriptInstallSolution.cs deleted file mode 100644 index 6138229f..00000000 --- a/UnoCheck/Solutions/DotNetSdkScriptInstallSolution.cs +++ /dev/null @@ -1,72 +0,0 @@ -using DotNetCheck; -using DotNetCheck.Models; -using System; -using System.Collections.Generic; -using System.IO; -using System.Net.Http; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace DotNetCheck.Solutions -{ - public class DotNetSdkScriptInstallSolution : Solution - { - const string installScriptBash = "https://dot.net/v1/dotnet-install.sh"; - const string installScriptPwsh = "https://dot.net/v1/dotnet-install.ps1"; - - public DotNetSdkScriptInstallSolution(string version) - { - Version = version; - } - - public readonly string Version; - - public override async Task Implement(SharedState sharedState, CancellationToken cancellationToken) - { - await base.Implement(sharedState, cancellationToken); - - string sdkRoot = default; - - if (sharedState != null && sharedState.TryGetEnvironmentVariable("DOTNET_ROOT", out var envSdkRoot)) - { - if (Directory.Exists(envSdkRoot)) - sdkRoot = envSdkRoot; - } - - if (string.IsNullOrEmpty(sdkRoot)) - sdkRoot = Util.IsWindows - ? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet") - : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".dotnet"); - - var scriptUrl = Util.IsWindows ? installScriptPwsh : installScriptBash; - var scriptPath = Path.Combine(Path.GetTempPath(), Util.IsWindows ? "dotnet-install.ps1" : "dotnet-install.sh"); - - Util.Log($"Downloading dotnet-install script: {scriptUrl}"); - - var http = new HttpClient(); - var data = await http.GetStringAsync(scriptUrl); - File.WriteAllText(scriptPath, data); - - var exe = Util.Platform switch - { - Platform.Linux => "bash", - Platform.OSX => "sh", - Platform.Windows => "powershell", - _ => throw new NotSupportedException($"Unsupported platform {Util.Platform}") - }; - - var args = Util.IsWindows - ? $"\"{scriptPath}\" -InstallDir \"{sdkRoot}\" -Version \"{Version}\"" - : $"\"{scriptPath}\" --install-dir \"{sdkRoot}\" --version \"{Version}\""; - - Util.Log($"Executing dotnet-install script..."); - Util.Log($"\t{exe} {args}"); - - // Launch the process - var p = new ShellProcessRunner(new ShellProcessRunnerOptions(exe, args)); - - p.WaitForExit(); - } - } -} diff --git a/UnoCheck/Solutions/LinuxNinjaSolution.cs b/UnoCheck/Solutions/LinuxNinjaSolution.cs index 1d22084f..5f6bbe9f 100644 --- a/UnoCheck/Solutions/LinuxNinjaSolution.cs +++ b/UnoCheck/Solutions/LinuxNinjaSolution.cs @@ -1,4 +1,5 @@ -using DotNetCheck.Models; +using System.Collections.Generic; +using DotNetCheck.Models; using System.Threading; using System.Threading.Tasks; @@ -6,14 +7,30 @@ namespace DotNetCheck.Solutions { public class LinuxNinjaSolution : Solution { + public static IEnumerable<(LinuxPackageManagerWrapper wrapper, string packageName)> NinjaPackageNamesWithWrappers { get; } = + LinuxPackageManagerWrapper.MatchPackageNamesWithStandardSupport( + "ninja-build", + "ninja", + "ninja-build", + "ninja-build", + "ninja" + ); + public override async Task Implement(SharedState sharedState, CancellationToken cancellationToken) { await base.Implement(sharedState, cancellationToken); + + // no need to update first, we should've already searched for the package + var installedPackage = await LinuxPackageManagerWrapper.InstallPackage(NinjaPackageNamesWithWrappers, false); - _ = await Util.WrapShellCommandWithSudo("apt-get", new[] { "update" }); - _ = await Util.WrapShellCommandWithSudo("apt-get", new[] { "install", "ninja-build" }); - - ReportStatus("Ninja Build System was installed on Linux."); + if (installedPackage) + { + ReportStatus("ninja-build was installed on Linux."); + } + else + { + ReportStatus("ninja-build could not be installed."); + } } } } \ No newline at end of file diff --git a/UnoCheck/Util.cs b/UnoCheck/Util.cs index efeb8f5d..819f43fc 100644 --- a/UnoCheck/Util.cs +++ b/UnoCheck/Util.cs @@ -156,9 +156,9 @@ public static bool Delete(string path, bool isFile) var args = $"-c 'sudo rm -rf \"{path}\"'"; if (Verbose) - Console.WriteLine($"{ShellProcessRunner.MacOSShell} {args}"); + Console.WriteLine($"{ShellProcessRunner.UnixShell} {args}"); - var p = new ShellProcessRunner(new ShellProcessRunnerOptions(ShellProcessRunner.MacOSShell, args) + var p = new ShellProcessRunner(new ShellProcessRunnerOptions(ShellProcessRunner.UnixShell, args) { RedirectOutput = Verbose }); @@ -197,8 +197,8 @@ public static bool Delete(string path, bool isFile) if (!Util.IsWindows) { - actualCmd = ShellProcessRunner.MacOSShell; - actualArgs = $"-c 'sudo {cmd} {actualArgs}'"; + actualCmd = ShellProcessRunner.UnixShell; + actualArgs = $"-c 'sudo {cmd} {actualArgs}'"; } var cli = new ShellProcessRunner(new ShellProcessRunnerOptions(actualCmd, actualArgs) { WorkingDirectory = workingDir, Verbose = verbose } ); @@ -244,9 +244,9 @@ public static async Task WrapCopyWithShellSudo(string destination, bool is : $"-c 'sudo mkdir -p \"{destDir}\" && sudo cp -pPR \"{intermediate}/\" \"{destination}\"'"; // note the / at the end of the dir if (Verbose) - Console.WriteLine($"{ShellProcessRunner.MacOSShell} {args}"); + Console.WriteLine($"{ShellProcessRunner.UnixShell} {args}"); - var p = new ShellProcessRunner(new ShellProcessRunnerOptions(ShellProcessRunner.MacOSShell, args) + var p = new ShellProcessRunner(new ShellProcessRunnerOptions(ShellProcessRunner.UnixShell, args) { RedirectOutput = Verbose });