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
This commit is contained in:
Christoph Schwörer
2026-08-26 07:43:51 +02:00
parent 18edae75b6
commit f045b99a25
24664 changed files with 5846716 additions and 1 deletions
@@ -0,0 +1,259 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml.Linq;
namespace Centron.Scripts
{
public static class WXSHelper
{
private static readonly XNamespace Xmlns = @"http://schemas.microsoft.com/wix/2006/wi";
public static bool UpdateCentronSetupProductHeat()
{
return UpdateHeatWxs(CentronPaths.CentronNetInstaller.ProductHeat,
CustomizeComponentInWxs,
AllowIgnoreUpdatesToComponent,
(string.Empty, CentronPaths.CentronNet.PublishDirectory),
("de", CentronPaths.CentronNet.PublishedDeDirectory),
("en", CentronPaths.CentronNet.PublishedEnDirectory),
("Tools", CentronPaths.CentronNet.PublishedToolsDirectory));
void CustomizeComponentInWxs(string fileName, XElement componentElement)
{
if (fileName == "Files\\CentronConnections.xml")
{
componentElement.SetAttributeValue("NeverOverwrite", "yes");
componentElement.SetAttributeValue("Permanent", "yes");
}
}
}
public static bool UpdateWebServiceSetupProductHeat()
{
return UpdateHeatWxs(CentronPaths.WebServiceInstaller.ProductHeat,
CustomizeComponentInWxs,
AllowIgnoreUpdatesToComponent,
(string.Empty, CentronPaths.WebService.PublishDirectory),
("de", CentronPaths.WebService.PublishedDeDirectory),
("en", CentronPaths.WebService.PublishedEnDirectory),
("development", CentronPaths.WebService.PublishedDevelopmentDirectory),
("ConnectionManager", CentronPaths.WebService.PublishedConnectionManagerDirectory),
("ConnectionManager\\de", CentronPaths.WebService.PublishedConnectionManagerDeDirectory),
("ConnectionManager\\en", CentronPaths.WebService.PublishedConnectionManagerEnDirectory));
void CustomizeComponentInWxs(string fileName, XElement componentElement)
{
if (fileName == "Files\\Centron.Host.WindowsService.exe")
{
var serviceInstallElement = new XElement(Xmlns + "ServiceInstall",
new XAttribute("Id", "ServiceInstaller"),
new XAttribute("Type", "ownProcess"),
new XAttribute("Name", "CentronWebService"),
new XAttribute("DisplayName", "c-entron Web-Service"),
new XAttribute("Description", "c-entron Web-Service for access c-entron informations from outside"),
new XAttribute("Start", "auto"),
new XAttribute("ErrorControl", "normal"));
componentElement.Add(serviceInstallElement);
var serviceControlElement = new XElement(Xmlns + "ServiceControl",
new XAttribute("Id", "StartService"),
new XAttribute("Stop", "both"),
new XAttribute("Remove", "uninstall"),
new XAttribute("Name", "CentronWebService"),
new XAttribute("Wait", "no"),
new XAttribute("Start", "install"));
componentElement.Add(serviceControlElement);
// The Centron.Host.WindowsService.exe is referenced by this file-Id in the Product.wxs
// So it always should stay this id: fil04C6E9A1324A0A1878612A211EC8F58D
var fileElement = componentElement.Element(Xmlns + "File");
fileElement.SetAttributeValue("Id", "fil04C6E9A1324A0A1878612A211EC8F58D");
}
}
}
private static bool AllowIgnoreUpdatesToComponent(XElement componentElement)
{
var fileNamesToIgnore = new[]
{
// This file contains a version-number, and is different depending on which .NET SDK is installed
// So to allow building versions of the c-entron.NET and the Web-Services with .NET SDK version roll-forward,
// we have to allow different versions of this file too
// So we ignore WXS-heat updates to this file
"Files\\mscordaccore_amd64_amd64_(.*).dll",
"Files\\ConnectionManager\\mscordaccore_amd64_amd64_(.*).dll",
// These nuget-packages contain the build-version number in their file-name
// So we ignore WXS-heat updates to these files
$"Files\\development\\{CentronPaths.CentronCore.ProjectName}.(.*).nupkg",
$"Files\\development\\{CentronPaths.CentronInterfaces.ProjectName}.(.*).nupkg",
$"Files\\development\\{CentronPaths.CentronWebServicesCore.ProjectName}.(.*).nupkg",
// FastReport font cache files are excluded via ShouldExcludeFile, but we also need to
// suppress removal warnings when these files are removed from existing heat files
@"Files\\font(.*)\.list",
};
var fileName = componentElement.Element(Xmlns + "File")?.Attribute("Source")?.Value ?? string.Empty;
// Regex patterns see a single backslash as a escape-sequence, but in our case it's file-paths
// So we replace single backslashes with double backslashes
return fileNamesToIgnore.Any(f => Regex.IsMatch(fileName, f.Replace(@"\", @"\\")));
}
private static bool UpdateHeatWxs(string filePath, Action<string, XElement> componentModifier, Func<XElement, bool> allowIgnoreUpdatesToComponent, params (string directoryName, string directoryFilesPath)[] fileDirectories)
{
XDocument document;
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
document = XDocument.Load(fileStream);
}
var fragments = document.Element(Xmlns + "Wix").Elements(Xmlns + "Fragment");
var directoryFragment = fragments.First(f => f.Element(Xmlns + "DirectoryRef") != null).Element(Xmlns + "DirectoryRef");
var componentGroupFragment = fragments.First(f => f.Element(Xmlns + "ComponentGroup") != null).Element(Xmlns + "ComponentGroup");
bool result = false;
foreach (var (directoryName, directoryFilesPath) in fileDirectories)
{
result = result | UpdateComponentsForDirectory(directoryFragment, componentGroupFragment, directoryName, directoryFilesPath, componentModifier, allowIgnoreUpdatesToComponent);
}
OrderComponents(componentGroupFragment);
using (var fileStream = new FileStream(filePath, FileMode.Truncate))
{
document.Save(fileStream);
}
return result;
}
private static bool UpdateComponentsForDirectory(XElement directoryFragment, XElement rootComponentGroup, string directoryName, string directoryFilePath, Action<string, XElement> componentModifier, Func<XElement, bool> allowIgnoreUpdatesToComponent)
{
string directoryGuid;
if (string.IsNullOrWhiteSpace(directoryName))
directoryGuid = directoryFragment.Attribute("Id").Value;
else
{
var parts = directoryName.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
var current = (XElement)directoryFragment;
foreach (var part in parts)
{
current = current.Elements(Xmlns + "Directory").First(f => f.Attribute("Name").Value == part);
}
directoryGuid = current.Attribute("Id").Value;
}
var files = Directory.Exists(directoryFilePath)
? Directory.GetFiles(directoryFilePath).Where(f => !ShouldExcludeFile(f)).ToList()
: new List<string>();
var removedFiles = RemoveComponentsForDirectory(directoryGuid, rootComponentGroup, files, allowIgnoreUpdatesToComponent);
var addedFiles = AddComponentsForFiles(directoryGuid, directoryName, rootComponentGroup, files, componentModifier, allowIgnoreUpdatesToComponent);
return removedFiles || addedFiles;
}
private static bool ShouldExcludeFile(string filePath)
{
var fileName = Path.GetFileName(filePath);
// FastReport creates a font cache file (e.g. font2022.1.6.list) containing machine-specific font paths
// This file is auto-generated at runtime and should not be included in the installer
if (Regex.IsMatch(fileName, @"^font.*\.list$", RegexOptions.IgnoreCase))
return true;
return false;
}
private static bool RemoveComponentsForDirectory(string directoryGuid, XElement rootComponentGroup, List<string> files, Func<XElement, bool> allowIgnoreUpdatesToComponent)
{
var fileNames = files
.Select(f => Path.GetFileName(f))
.ToList();
var components = rootComponentGroup
.Elements(Xmlns + "Component")
.Where(f =>
f.Attribute("Directory").Value == directoryGuid &&
fileNames.Contains(Path.GetFileName(f.Element(Xmlns + "File").Attribute("Source").Value)) == false)
.ToList();
foreach (var currentComponent in components)
{
if (!allowIgnoreUpdatesToComponent(currentComponent))
{
Console.WriteLine($"(ISSUE) Removed component: {currentComponent.Element(Xmlns + "File").Attribute("Source").Value}");
}
currentComponent.Remove();
}
return components.Any(f => allowIgnoreUpdatesToComponent(f) is false);
}
private static bool AddComponentsForFiles(string directoryGuid, string directoryName, XElement rootComponentGroup, IList<string> files, Action<string, XElement> componentModifier, Func<XElement, bool> allowIgnoreUpdatesToComponent)
{
var baseSource = Path.Combine("Files", directoryName);
bool FileIsInWxs(string f)
{
return rootComponentGroup
.Elements(Xmlns + "Component")
.Any(d =>
d.Attribute("Directory").Value == directoryGuid &&
d.Element(Xmlns + "File").Attribute("Source").Value == Path.Combine(baseSource, Path.GetFileName(f)));
}
var missingFiles = files
.Select(f => Path.GetFileName(f))
.Where(f => FileIsInWxs(f) == false)
.ToList();
var addedComponents = new List<XElement>();
foreach (var currentFile in missingFiles)
{
var fileSourcePath = Path.Combine(baseSource, currentFile);
var component = new XElement(Xmlns + "Component",
new XAttribute("Id", $"cmp{Guid.NewGuid().ToString("N").ToUpper()}"),
new XAttribute("Directory", directoryGuid),
new XAttribute("Guid", $"{Guid.NewGuid().ToString().ToUpper()}"),
new XElement(Xmlns + "File",
new XAttribute("Id", $"fil{Guid.NewGuid().ToString("N").ToUpper()}"),
new XAttribute("KeyPath", "yes"),
new XAttribute("Source", fileSourcePath)));
componentModifier?.Invoke(fileSourcePath, component);
rootComponentGroup.Add(component);
addedComponents.Add(component);
if (!allowIgnoreUpdatesToComponent(component))
{
Console.WriteLine($"(ISSUE) Missing component in heat file: {component.Element(Xmlns + "File").Attribute("Source").Value}");
}
}
return addedComponents.Any(f => allowIgnoreUpdatesToComponent(f) is false);
}
private static void OrderComponents(XElement componentGroupFragment)
{
var components = componentGroupFragment
.Elements(Xmlns + "Component")
.OrderBy(f => f.Element(Xmlns + "File").Attribute("Source").Value)
.ToList();
foreach (var component in components)
{
component.Remove();
componentGroupFragment.Add(component);
}
}
}
}