using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Text.RegularExpressions; namespace Scripts { public static class RunHelper { private static string MsBuildPath { get; } = GetMsBuildPath(); private static string GetMsBuildPath() { var vsWhere = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Microsoft Visual Studio", "Installer", "vswhere.exe"); var path = Read(vsWhere, "-products * -requires Microsoft.Component.MSBuild -property installationPath -version 16.0 -prerelease").Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries).First(); var msbuildPath = Path.Combine(path, "MSBuild", "Current", "Bin", "MSBuild.exe"); if (File.Exists(msbuildPath) == false) throw new FileNotFoundException("msbuild.exe not found. Is Visual Studio 2019 installed?"); return msbuildPath; } public static void RunDotNet(string arguments) { Run("dotnet", arguments); } public static void RunDotNetTest(string arguments) { try { Run("dotnet", "test " + arguments); } catch (Exception e) when (e.Data.Contains("ExitCode") && (int?)e.Data["ExitCode"] == 1) { //The test run failed, but we don't want to error //Instead return and allow the rest of the process to read the TestResults file } } public static void RunDotNetTool(string arguments) { Run("dotnet", "tool " + arguments); } public static void RunMsBuild(string arguments) { Run(MsBuildPath, arguments); } public static void RunSignTool(string certificate, string password, string timeServer, string description, params string[] files) { var possibleSignToolPaths = new[] { @"C:\Program Files (x86)\Windows Kits\8.1\bin\x86\signtool.exe", @"C:\Program Files (x86)\Windows Kits\10\bin\x86\signtool.exe", @"C:\Program Files (x86)\Microsoft SDKs\ClickOnce\SignTool\signtool.exe", }; var signToolToUse = possibleSignToolPaths.FirstOrDefault(File.Exists) ?? throw new FileNotFoundException("signtool.exe not found"); var arguments = $"sign /fd sha384 /tr \"{timeServer}\" /td sha384 /d \"{description}\" {string.Join(" ", files.Select(f => "\"" + f + "\""))} "; Run(signToolToUse, arguments); } public static void Run(string name, string args, string? workingDirectory = null) { Read(name, args, workingDirectory); } public static string Read(string name, string args, string? workingDirectory = null) { using var process = new Process { StartInfo = new ProcessStartInfo { FileName = name, Arguments = args, WorkingDirectory = workingDirectory ?? string.Empty, UseShellExecute = false, RedirectStandardError = true, RedirectStandardOutput = true, } }; process.Start(); var output = process.StandardOutput.ReadToEnd(); //Make sure to read the output before we WaitForExit, or the process might hang forever process.WaitForExit(); if (process.ExitCode != 0) throw new InvalidOperationException($"The command \"{name} {args}\" failed!{Environment.NewLine}{output}") { Data = { ["ExitCode"] = process.ExitCode } }; return output; } } }