Files
Masterarbeit/QuellCode/CentronERP/scripts/Centron.Scripts/RunHelper.cs
T
Christoph Schwörer f045b99a25 Codebasis als Dateien ins Arbeitsrepo statt als Gitlink
QuellCode/CentronERP war nur als Gitlink (Submodul-Referenz auf 79c1142)
getrackt, ohne .gitmodules und ohne erreichbares Remote. Der
Untersuchungsgegenstand der Versuchsreihe war damit nicht reproduzierbar
gesichert: Ein Klon haette ein leeres Verzeichnis erhalten, und die Belege
der 3.287 Anforderungen waeren nicht ueberpruefbar gewesen.

Umstellung:
- Historie nach c:\DEV\CentronERP_git_snapshot_79c1142 ausgelagert
  (vollstaendig lesbar, enthaelt 79c1142 und Vorgaenger 89ccfd6)
- Gitlink aus dem Index entfernt
- Dateiinhalt aufgenommen: 24.557 Dateien, rund 333 MB

Die verschachtelte .gitignore der Codebasis gilt weiter, Build-Artefakte
bleiben ausgeschlossen. Details in Versuche/Versuch_01/_Codebasis-Nachweis.md
2026-08-26 07:43:51 +02:00

159 lines
7.0 KiB
C#

using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using CliWrap;
using CliWrap.Buffered;
namespace Centron.Scripts
{
public static class RunHelper
{
private static string _msBuildPath;
private static string MsBuildPath
{
get
{
if (_msBuildPath == null)
FillPaths();
return _msBuildPath;
}
}
private static void FillPaths()
{
var vsWhere = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Microsoft Visual Studio", "Installer", "vswhere.exe");
var output = Read(vsWhere, "-products * -requires Microsoft.Component.MSBuild -property installationPath -prerelease");
var paths = output.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
if (paths.Length == 0)
throw new Exception("Visual Studio with MSBuild component not found. Please complete the Visual Studio installation and ensure the 'Desktop development with C++' or '.NET desktop development' workload is installed.");
var path = paths.First();
var msbuildPath = Path.Combine(path, "MSBuild", "Current", "Bin", "MSBuild.exe");
if (File.Exists(msbuildPath) == false)
throw new Exception($"msbuild.exe not found at {msbuildPath}. Is Visual Studio properly installed?");
_msBuildPath = msbuildPath;
}
public static void RunDotNet(string arguments, TimeSpan? timeout = null)
{
Run("dotnet", arguments, timeout: timeout);
}
public static void RunDotNetTest(string arguments, TimeSpan? timeout = null)
{
try
{
Run("dotnet", "test " + arguments, timeout: timeout);
}
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 RunMsBuild(string arguments, TimeSpan? timeout = null)
{
try
{
Run(MsBuildPath, arguments, timeout: timeout);
}
catch (Exception e) when (e.Data.Contains("ExitCode") && EnvironmentHelper.RunningInAzurePipelines())
{
ParseAndOutputDiagnosticsForAzurePipelines(e.Message);
throw new Exception($"MSBuild.exe failed with command: {arguments}.", e);
}
}
private static void ParseAndOutputDiagnosticsForAzurePipelines(string message)
{
string GetValue(Match match, string name) => match.Groups.Cast<Group>().First(f => f.Name == name).Value.Replace("[", string.Empty).Replace("]", string.Empty);
var diagnosticsRegex = new Regex(@"^\s*[\d>]*(?<FileName>.*)\((?<LineNumber>\d*),(?<ColumnNumber>\d*)\): (?<Type>error|warning) (?<Code>.*): (?<Message>.*)\[.*$", RegexOptions.Multiline);
var diagnostics = diagnosticsRegex
.Matches(message)
.Cast<Match>()
.Select(f => new
{
Type = GetValue(f, "Type"),
FileName = GetValue(f, "FileName"),
LineNumber = GetValue(f, "LineNumber"),
ColumnNumber = GetValue(f, "ColumnNumber"),
Code = GetValue(f, "Code"),
Message = GetValue(f, "Message"),
})
.Where(f => f.Type == "error") //For now, only output errors
.Distinct()
.ToList();
foreach (var diagnostic in diagnostics)
{
Console.WriteLine($"##vso[task.logissue type=error;sourcepath={diagnostic.FileName};linenumber={diagnostic.LineNumber};columnnumber={diagnostic.ColumnNumber};code={diagnostic.Code};]{diagnostic.Message}");
}
}
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);
if (signToolToUse == null)
throw new Exception("signtool.exe not found");
string arguments = $"sign /fd sha384 /tr \"{timeServer}\" /td sha384 /d \"{description}\" {string.Join(" ", files.Select(f => "\"" + f + "\""))} ";
// string arguments = $"sign /f \"{certificate}\" /p \"{password}\" /fd sha256 /tr \"{timeServer}\" /td sha256 /d \"{description}\" {string.Join(" ", files.Select(f => "\"" + f + "\""))} ";
Run(signToolToUse, arguments);
}
public static string ReadDotNetTool(string tool, string arguments, string workingDirectory = null, TimeSpan? timeout = null)
{
return Read(Path.Combine(CentronPaths.DotNetToolsDirectory, tool), arguments, workingDirectory, timeout);
}
public static void Run(string name, string args, string workingDirectory = null, TimeSpan? timeout = null)
{
Read(name, args, workingDirectory, timeout);
}
public static string Read(string name, string args, string workingDirectory = null, TimeSpan? timeout = null)
{
var tokenSource = new CancellationTokenSource();
tokenSource.CancelAfter(timeout ?? GetDefaultTimeout());
var result = Cli.Wrap(name)
.WithArguments(args)
.WithWorkingDirectory(workingDirectory ?? Directory.GetCurrentDirectory())
.WithValidation(CommandResultValidation.None)
.ExecuteBufferedAsync(tokenSource.Token)
.Task.Result;
if (result.ExitCode != 0)
throw new Exception($"The command \"{name} {args}\" failed!{Environment.NewLine}{result.StandardOutput}{Environment.NewLine}{result.StandardError}")
{
Data =
{
["ExitCode"] = result.ExitCode
}
};
return result.StandardOutput + Environment.NewLine + result.StandardError;
}
private static TimeSpan GetDefaultTimeout()
{
return EnvironmentHelper.UseLongTimeout()
? TimeSpan.FromHours(2) // Long-Timeout is typically used when running a security scan during build (like GitHub Advanced Security for Azure DevOps)
: TimeSpan.FromMinutes(20);
}
}
}