Skip to content

Commit

Permalink
[modgen] Ajout hash des modules dans le lockfile
Browse files Browse the repository at this point in the history
  • Loading branch information
JabX committed Sep 11, 2024
1 parent 0e083d4 commit 90ab7ff
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 43 deletions.
2 changes: 1 addition & 1 deletion TopModel.Generator/ModgenDependency.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace TopModel.Generator;

public record ModgenDependency(string ConfigKey, string Version)
public record ModgenDependency(string ConfigKey, TopModelLockModule Version)
{
public string FullName => $"TopModel.Generator.{ConfigKey.ToFirstUpper()}";
}
81 changes: 59 additions & 22 deletions TopModel.Generator/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.CommandLine;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json.Nodes;
using System.Xml;
Expand Down Expand Up @@ -90,7 +92,7 @@ void HandleFile(FileInfo file)
{
var dir = Directory.GetCurrentDirectory();
var pattern = "topmodel*.config";
foreach (var fileName in Directory.GetFiles(dir, pattern, SearchOption.AllDirectories))
foreach (var fileName in Directory.EnumerateFiles(dir, pattern, SearchOption.AllDirectories))
{
HandleFile(new FileInfo(fileName));
}
Expand All @@ -103,7 +105,7 @@ void HandleFile(FileInfo file)
dir = Directory.GetParent(dir)?.FullName;
if (dir != null)
{
foreach (var fileName in Directory.GetFiles(dir, pattern))
foreach (var fileName in Directory.EnumerateFiles(dir, pattern))
{
HandleFile(new FileInfo(fileName));
found = true;
Expand Down Expand Up @@ -277,9 +279,9 @@ void HandleFile(FileInfo file)
var configKey = dep.Key.Split('.').Last().ToLower();
if (!topModelLock.Modules.TryGetValue(configKey, out var ev))
{
topModelLock.Modules.Add(configKey, dep.Value);
topModelLock.Modules.Add(configKey, new() { Version = dep.Value });
}
else if (ev != dep.Value)
else if (ev.Version != dep.Value)
{
logger.LogError($"Le module personalisé '{cg}' référence le module '{configKey}' en version '{dep.Value}', ce qui n'est pas la version du lockfile ('{ev}').");
returnCode = 1;
Expand Down Expand Up @@ -339,7 +341,7 @@ void HandleFile(FileInfo file)
}

var nugetVersion = moduleVersions.Last().Version;
moduleVersion = $"{nugetVersion.Major}.{nugetVersion.Minor}.{nugetVersion.Build}";
moduleVersion = new() { Version = $"{nugetVersion.Major}.{nugetVersion.Minor}.{nugetVersion.Build}" };
topModelLock.Modules.Add(configKey, moduleVersion);
}

Expand Down Expand Up @@ -367,33 +369,40 @@ void HandleFile(FileInfo file)

foreach (var dep in deps)
{
var moduleFolder = Path.Combine(modgenRoot, $"{dep.ConfigKey}.{dep.Version}");
var depVersion = dep.Version.Version;
var moduleFolder = Path.Combine(modgenRoot, $"{dep.ConfigKey}.{depVersion}");

if (!Directory.Exists(moduleFolder))
var depHash = GetFolderHash(moduleFolder);

if (depHash == null || depHash != dep.Version.Hash)
{
logger.LogInformation($"({dep.ConfigKey}) Installation de {dep.FullName}@{dep.Version} en cours...");
if (Directory.Exists(moduleFolder))
{
logger.LogInformation($"({dep.ConfigKey}) Module corrompu, réinstallation...");
Directory.Delete(moduleFolder, true);
}

if (!await nugetResource.DoesPackageExistAsync(dep.FullName, new NuGetVersion(dep.Version), nugetCache, NullLogger.Instance, ct))
logger.LogInformation($"({dep.ConfigKey}) Installation de {dep.FullName}@{depVersion} en cours...");

if (!await nugetResource.DoesPackageExistAsync(dep.FullName, new NuGetVersion(depVersion), nugetCache, NullLogger.Instance, ct))
{
logger.LogError($"({dep.ConfigKey}) Le package {dep.FullName}@{dep.Version} est introuvable.");
logger.LogError($"({dep.ConfigKey}) Le package {dep.FullName}@{depVersion} est introuvable.");
returnCode = 1;
continue;
}

Directory.CreateDirectory(moduleFolder);

using var packageStream = new MemoryStream();
await nugetResource.CopyNupkgToStreamAsync(dep.FullName, new NuGetVersion(dep.Version), packageStream, nugetCache, NullLogger.Instance, ct);
await nugetResource.CopyNupkgToStreamAsync(dep.FullName, new NuGetVersion(depVersion), packageStream, nugetCache, NullLogger.Instance, ct);
using var packageReader = new PackageArchiveReader(packageStream);
var nuspecReader = await packageReader.GetNuspecReaderAsync(ct);

var dependencies = nuspecReader.GetDependencyGroups()
.Single(dg => dg.TargetFramework.ToString() == framework)
.Packages;

var minVersion = dependencies.Single(d => d.Id == "TopModel.Generator.Core").VersionRange.MinVersion!.ToString();

File.WriteAllText(Path.Combine(moduleFolder, "min-version"), minVersion);
File.WriteAllText(Path.Combine(moduleFolder, "min-version"), dependencies.Single(d => d.Id == "TopModel.Generator.Core").VersionRange.MinVersion!.ToString());

foreach (var file in packageReader.GetFiles().Where(f => f == $"lib/{framework}/{dep.FullName}.dll" || f.EndsWith("config.json")))
{
Expand Down Expand Up @@ -434,26 +443,27 @@ void HandleFile(FileInfo file)
}

hasInstalled = true;
logger.LogInformation($"({dep.ConfigKey}) Installation de {dep.FullName}@{dep.Version} terminée avec succès.");
logger.LogInformation($"({dep.ConfigKey}) Installation de {dep.FullName}@{depVersion} terminée avec succès.");
dep.Version.Hash = GetFolderHash(moduleFolder);
}

var depVersionText = File.ReadAllText(Path.Combine(moduleFolder, "min-version"));
var depVersion = depVersionText.Split('.').Select(int.Parse).ToArray();
if (depVersion[0] != fullVersion.Major)
var minVersionText = File.ReadAllText(Path.Combine(moduleFolder, "min-version"));
var minVersion = minVersionText.Split('.').Select(int.Parse).ToArray();
if (minVersion[0] != fullVersion.Major)
{
logger.LogError($"Le module '{dep.ConfigKey}' ne référence pas la bonne version majeure de TopModel ({dep.Version} < {version}).");
logger.LogError($"Le module '{dep.ConfigKey}' ne référence pas la bonne version majeure de TopModel ({depVersion} < {version}).");
returnCode = 1;
continue;
}
else if (depVersion[1] > fullVersion.Minor)
else if (minVersion[1] > fullVersion.Minor)
{
logger.LogError($"Le module '{dep.ConfigKey}' référence une version plus récente de TopModel ({depVersionText} > {version}).");
logger.LogError($"Le module '{dep.ConfigKey}' référence une version plus récente de TopModel ({minVersionText} > {version}).");
returnCode = 1;
continue;
}

generators.AddRange(Directory.GetFiles(moduleFolder, "*.dll").SelectMany(a => Assembly.LoadFrom(a).GetExportedTypes().Where(t => GetIGenRegInterface(t) != null)));
resolvedConfigKeys.Add(dep.ConfigKey, dep.Version);
resolvedConfigKeys.Add(dep.ConfigKey, depVersion);
}
}

Expand Down Expand Up @@ -613,3 +623,30 @@ void HandleFile(FileInfo file)
}

return returnCode;

static string? GetFolderHash(string path)
{
var md5 = MD5.Create();

var files = Directory.GetFiles(path, "*", SearchOption.AllDirectories).OrderBy(p => p).ToList();
foreach (var file in files)
{
var relativePath = file.Substring(path.Length + 1);
var pathBytes = Encoding.UTF8.GetBytes(relativePath.ToLower());
md5.TransformBlock(pathBytes, 0, pathBytes.Length, pathBytes, 0);

var contentBytes = File.ReadAllBytes(file);
if (files.IndexOf(file) == files.Count - 1)
{
md5.TransformFinalBlock(contentBytes, 0, contentBytes.Length);
}
else
{
md5.TransformBlock(contentBytes, 0, contentBytes.Length, contentBytes, 0);
}
}

return md5.Hash != null
? BitConverter.ToString(md5.Hash).Replace("-", string.Empty).ToLower()
: null;
}
25 changes: 6 additions & 19 deletions TopModel.Utils/TopModelLock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ public class TopModelLock : TopModelLockFile
private readonly ILogger _logger;
private readonly string _modelRoot;

private readonly ISerializer _serializer = new SerializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithIndentedSequences()
.Build();

[SetsRequiredMembers]
public TopModelLock(ILogger logger, string modelRoot, string lockFileName)
{
Expand Down Expand Up @@ -90,25 +95,7 @@ public void Write()
StartCommentToken = "#"
};

fw.WriteLine($"version: {Version}");

if (Modules.Count > 0)
{
fw.WriteLine("modules:");
foreach (var module in Modules)
{
fw.WriteLine($" {module.Key}: {module.Value}");
}
}

if (GeneratedFiles.Count > 0)
{
fw.WriteLine("generatedFiles:");
foreach (var genFile in GeneratedFiles)
{
fw.WriteLine($" - {genFile}");
}
}
fw.Write(_serializer.Serialize(this));
}
}
}
2 changes: 1 addition & 1 deletion TopModel.Utils/TopModelLockFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ public class TopModelLockFile
{
public required string Version { get; set; }

public Dictionary<string, string> Modules { get; set; } = [];
public Dictionary<string, TopModelLockModule> Modules { get; set; } = [];

public List<string> GeneratedFiles { get; set; } = [];
}
44 changes: 44 additions & 0 deletions TopModel.Utils/TopModelLockModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;

namespace TopModel.Utils;

public class TopModelLockModule : TopModelLockModuleBase, IYamlConvertible
{
/// <inheritdoc cref="IYamlConvertible.Read" />
public void Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer)
{
if (parser.TryConsume<Scalar>(out var version))
{
Version = version.Value;
}
else if (parser.Current is MappingStart)
{
var lol = (TopModelLockModuleBase)nestedObjectDeserializer(typeof(TopModelLockModuleBase))!;
Version = lol.Version;
Hash = lol.Hash;
}
}

/// <inheritdoc cref="IYamlConvertible.Write" />
public void Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer)
{
if (Hash != null)
{
nestedObjectSerializer(new TopModelLockModuleBase { Version = Version, Hash = Hash });
}
else
{
emitter.Emit(new Scalar(Version));
}
}
}

#pragma warning disable SA1402
public class TopModelLockModuleBase
{
public required string Version { get; set; }

public string? Hash { get; set; }
}

0 comments on commit 90ab7ff

Please sign in to comment.