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
159 lines
7.0 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|