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
191 lines
7.9 KiB
C#
191 lines
7.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net.Http;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using System.Xml.Linq;
|
|
using static Centron.Scripts.RunHelper;
|
|
using static Centron.Scripts.CentronPaths;
|
|
|
|
namespace Centron.Scripts;
|
|
|
|
public static class DependencyGraph
|
|
{
|
|
public record Dependency(string Name, string Version, string ProjectUrl, string License);
|
|
|
|
public static List<Dependency> ReadDependencyGraphs(params string[] projectFilePaths)
|
|
{
|
|
return projectFilePaths.SelectMany(ReadDependencyGraph).Distinct().ToList();
|
|
}
|
|
public static List<Dependency> ReadDependencyGraph(string projectFilePath)
|
|
{
|
|
var projectAssetsJsonFilePath = Path.Combine(Path.GetDirectoryName(projectFilePath), "obj", "project.assets.json");
|
|
var projectAssetsJsonContent = File.ReadAllText(projectAssetsJsonFilePath);
|
|
|
|
var filter = new Func<Dependency, bool>[]
|
|
{
|
|
IsMicrosoftPackage,
|
|
IsCentronPackage,
|
|
IsRiverbirdPackage,
|
|
IsRuntimePackage,
|
|
IsLanguagePackage,
|
|
IsNetStandardLibraryPackage,
|
|
};
|
|
|
|
var dependencies = JsonDocument.Parse(projectAssetsJsonContent)
|
|
.RootElement.GetProperty("targets")
|
|
.EnumerateObject()
|
|
.SelectMany(f => f.Value.EnumerateObject()
|
|
.Select(f => f.Name.Split('/'))
|
|
.Select(f => new Dependency(f[0], f[1], null, null)))
|
|
.Where(f => filter.All(d => d(f) is false))
|
|
.Select(f => LoadAdditionalInfo(f))
|
|
.Distinct() // We might have duplicates if multiple targets are found in the project.assets.json file, so remove them
|
|
.ToList();
|
|
|
|
return dependencies;
|
|
}
|
|
|
|
private static readonly HttpClient _httpClient = new();
|
|
private static Dependency LoadAdditionalInfo(Dependency dependency)
|
|
{
|
|
if (dependency.Name.StartsWith("DevExpress"))
|
|
return dependency with { ProjectUrl = "https://www.devexpress.com/", License = "DevExpress" };
|
|
|
|
if (dependency.Name.StartsWith("FastReport"))
|
|
return dependency with { ProjectUrl = "https://www.fast-report.com/", License = "FastReport" };
|
|
|
|
try
|
|
{
|
|
// See: https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-manifest-nuspec
|
|
using var request = new HttpRequestMessage(HttpMethod.Get, $"https://api.nuget.org/v3-flatcontainer/{dependency.Name.ToLower()}/{dependency.Version}/{dependency.Name.ToLower()}.nuspec");
|
|
using var response = _httpClient.Send(request);
|
|
using var responseStream = response.Content.ReadAsStream();
|
|
using var reader = new StreamReader(responseStream);
|
|
|
|
var nuspecXml = reader.ReadToEnd();
|
|
|
|
var xmlDocument = XDocument.Parse(nuspecXml);
|
|
var xmlNamespace = "{" + xmlDocument.Root.GetDefaultNamespace() + "}";
|
|
var metadataElement = xmlDocument.Root.Element($"{xmlNamespace}metadata");
|
|
|
|
var license = metadataElement.Element($"{xmlNamespace}license")?.Value;
|
|
var licenseUrl = metadataElement.Element($"{xmlNamespace}licenseUrl")?.Value;
|
|
license = TryGuessLicense(dependency, license, licenseUrl) ?? license;
|
|
|
|
var projectUrl = metadataElement.Element($"{xmlNamespace}projectUrl")?.Value;
|
|
var sourceCodeUrl = metadataElement.Element($"{xmlNamespace}repository")?.Attribute("url")?.Value;
|
|
var url = string.IsNullOrWhiteSpace(projectUrl) is false ? projectUrl : sourceCodeUrl;
|
|
|
|
return dependency with { ProjectUrl = url, License = license };
|
|
}
|
|
catch
|
|
{
|
|
// Something went wrong, but we don't want the build to fail because of that
|
|
return dependency;
|
|
}
|
|
}
|
|
private static string TryGuessLicense(Dependency dependency, string license, string licenseUrl)
|
|
{
|
|
if (dependency is { Name: "Antlr3.Runtime", Version: "3.5.1" } &&
|
|
licenseUrl is "https://raw.githubusercontent.com/antlr/antlrcs/master/LICENSE.txt")
|
|
return "BSD-3-Clause";
|
|
|
|
if (dependency is { Name: "AutoMapper", Version: "8.1.1" } &&
|
|
licenseUrl is "https://github.com/AutoMapper/AutoMapper/blob/master/LICENSE.txt")
|
|
return "MIT";
|
|
|
|
if (dependency is { Name: "Castle.Core", Version: "4.4.1" } &&
|
|
licenseUrl is "http://www.apache.org/licenses/LICENSE-2.0.html")
|
|
return "Apache-2.0";
|
|
|
|
if (dependency is { Name: "FluentNHibernate", Version: "3.2.1" } &&
|
|
licenseUrl is "https://aka.ms/deprecateLicenseUrl")
|
|
return "BSD-3-Clause";
|
|
|
|
if (dependency is { Name: "Iesi.Collections", Version: "4.0.4" } &&
|
|
licenseUrl is null)
|
|
return "Public Domain";
|
|
|
|
if (dependency is { Name: "libfintx", Version: "1.1.0" } &&
|
|
licenseUrl is "https://opensource.org/license/LGPL-3.0")
|
|
return "LGPL-3.0";
|
|
|
|
if (dependency is { Name: "Remotion.Linq", Version: "2.2.0" } &&
|
|
licenseUrl is "http://opensource.org/licenses/Apache-2.0")
|
|
return "Apache-2.0";
|
|
|
|
if (dependency is { Name: "Remotion.Linq.EagerFetching", Version: "2.2.0" } &&
|
|
licenseUrl is "http://opensource.org/licenses/LGPL-2.1")
|
|
return "LGPL-2.1";
|
|
|
|
if (dependency is { Name: "SSH.NET", Version: "2020.0.2" } &&
|
|
licenseUrl is "https://github.com/sshnet/SSH.NET/blob/master/LICENSE")
|
|
return "MIT";
|
|
|
|
if (dependency is { Name: "SshNet.Security.Cryptography", Version: "1.3.0" } &&
|
|
licenseUrl is "https://github.com/sshnet/Cryptography/blob/master/LICENSE")
|
|
return "MIT";
|
|
|
|
if (dependency is { Name: "Castle.Windsor", Version: "5.0.1" } &&
|
|
licenseUrl is "http://www.apache.org/licenses/LICENSE-2.0.html")
|
|
return "Apache-2.0";
|
|
|
|
if (dependency is { Name: "OpenMcdf", Version: "2.3.0" } &&
|
|
licenseUrl is "https://opensource.org/licenses/MPL-2.0")
|
|
return "MPL-2.0";
|
|
|
|
if (dependency is { Name: "RtfPipe", Version: "2.0.7677.4303" } &&
|
|
licenseUrl is null)
|
|
return "MIT";
|
|
|
|
if (dependency is { Name: "Simple-MAPI.NET", Version: "1.2.1" } &&
|
|
licenseUrl is "https://aka.ms/deprecateLicenseUrl")
|
|
return "MIT";
|
|
|
|
return null;
|
|
}
|
|
|
|
private static bool IsMicrosoftPackage(Dependency package)
|
|
{
|
|
return package.Name.StartsWith("Microsoft") ||
|
|
package.Name.StartsWith("System");
|
|
}
|
|
private static bool IsCentronPackage(Dependency package)
|
|
{
|
|
return package.Name.StartsWith("Centron");
|
|
}
|
|
private static bool IsRiverbirdPackage(Dependency package)
|
|
{
|
|
return package.Name.StartsWith("Riverbird");
|
|
}
|
|
private static bool IsRuntimePackage(Dependency package)
|
|
{
|
|
return package.Name.StartsWith("runtime");
|
|
}
|
|
private static bool IsLanguagePackage(Dependency package)
|
|
{
|
|
return package.Name.EndsWith(".de");
|
|
}
|
|
private static bool IsNetStandardLibraryPackage(Dependency package)
|
|
{
|
|
return package.Name == "NETStandard.Library";
|
|
}
|
|
|
|
public static void Write(List<Dependency> dependencies, string filePath)
|
|
{
|
|
var content = new StringBuilder();
|
|
content.AppendLine($"{"Dependency",-50}{"Versionsnummer",-15}{"Lizenz",-15}{"URL",-100}");
|
|
|
|
foreach (var dependency in dependencies.OrderBy(f => f.Name).ThenBy(f => f.License))
|
|
{
|
|
content.AppendLine($"{dependency.Name,-50}{dependency.Version,-15}{dependency.License,-15}{dependency.ProjectUrl,-100}");
|
|
}
|
|
|
|
if (!Directory.Exists(Path.GetDirectoryName(filePath)))
|
|
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
|
|
File.WriteAllText(filePath, content.ToString());
|
|
}
|
|
} |