From 4116b9d0944d81b73d0f231c4101aa00cb11b850 Mon Sep 17 00:00:00 2001 From: Damien Date: Mon, 16 Sep 2024 18:17:51 +0200 Subject: [PATCH] =?UTF-8?q?[modgen]=20V=C3=A9rification=20des=20derni?= =?UTF-8?q?=C3=A8res=20versions=20de=20modgen=20+=20des=20g=C3=A9n=C3=A9ra?= =?UTF-8?q?teurs=20au=20lancement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TopModel.Generator/ModgenDependency.cs | 2 + TopModel.Generator/Program.cs | 55 +++++----- TopModel.Generator/TopModel.Generator.csproj | 2 - TopModel.Utils/ModuleLatestVersion.cs | 3 + TopModel.Utils/NugetUtils.cs | 107 +++++++++++++++++++ TopModel.Utils/TopModel.Utils.csproj | 1 + 6 files changed, 144 insertions(+), 26 deletions(-) create mode 100644 TopModel.Utils/ModuleLatestVersion.cs create mode 100644 TopModel.Utils/NugetUtils.cs diff --git a/TopModel.Generator/ModgenDependency.cs b/TopModel.Generator/ModgenDependency.cs index c5beaf24..8fd33e57 100644 --- a/TopModel.Generator/ModgenDependency.cs +++ b/TopModel.Generator/ModgenDependency.cs @@ -5,4 +5,6 @@ namespace TopModel.Generator; public record ModgenDependency(string ConfigKey, TopModelLockModule Version) { public string FullName => $"TopModel.Generator.{ConfigKey.ToFirstUpper()}"; + + public string? LatestVersion { get; set; } } diff --git a/TopModel.Generator/Program.cs b/TopModel.Generator/Program.cs index 9e9e0957..fc65271f 100644 --- a/TopModel.Generator/Program.cs +++ b/TopModel.Generator/Program.cs @@ -8,11 +8,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using NuGet.Common; -using NuGet.Packaging; using NuGet.Packaging.Core; -using NuGet.Protocol; -using NuGet.Protocol.Core.Types; -using NuGet.Versioning; using TopModel.Core; using TopModel.Core.Loaders; using TopModel.Generator; @@ -145,6 +141,16 @@ void HandleFile(FileInfo file) Console.WriteLine($"========= TopModel.Generator v{version} ========="); Console.WriteLine(); +var latestVersion = await NugetUtils.GetLatestVersionAsync("TopModel.Generator"); +if (latestVersion != null && latestVersion.Version != version) +{ + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine($"Nouvelle version disponible : {latestVersion.Version}"); + Console.WriteLine("Vous pouvez lancer la commande `dotnet tool update -g TopModel.Generator` pour effectuer la mise à jour."); + Console.ForegroundColor = ConsoleColor.Gray; + Console.WriteLine(); +} + if (excludedTags.Length > 0) { Console.Write("Tags"); @@ -161,6 +167,8 @@ void HandleFile(FileInfo file) Console.Write(" update "); Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine($"activé pour : {updateMode}."); + + await NugetUtils.ClearAsync(); } if (watchMode) @@ -317,12 +325,6 @@ void HandleFile(FileInfo file) continue; } - var ct = CancellationToken.None; - - var nugetCache = new SourceCacheContext(); - var nugetRepository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json"); - var nugetResource = await nugetRepository.GetResourceAsync(); - var resolvedConfigKeys = new Dictionary(); foreach (var configKey in config.Generators.Keys) @@ -337,17 +339,15 @@ void HandleFile(FileInfo file) if (!topModelLock.Modules.TryGetValue(configKey, out var moduleVersion)) { - var moduleVersions = await nugetResource.GetAllVersionsAsync(fullModuleName, nugetCache, NullLogger.Instance, ct); + moduleVersion = await NugetUtils.GetLatestVersionAsync(fullModuleName); - if (!moduleVersions.Any()) + if (moduleVersion == null) { logger.LogError($"Aucun module de générateurs trouvé pour '{configKey}'."); returnCode = 1; continue; } - var nugetVersion = moduleVersions.Last().Version; - moduleVersion = new() { Version = $"{nugetVersion.Major}.{nugetVersion.Minor}.{nugetVersion.Build}" }; topModelLock.Modules.Add(configKey, moduleVersion); } @@ -390,7 +390,7 @@ void HandleFile(FileInfo file) logger.LogInformation($"({dep.ConfigKey}) Installation de {dep.FullName}@{depVersion} en cours..."); - if (!await nugetResource.DoesPackageExistAsync(dep.FullName, new NuGetVersion(depVersion), nugetCache, NullLogger.Instance, ct)) + if (!await NugetUtils.DoesPackageExistsAsync(dep.FullName, depVersion)) { logger.LogError($"({dep.ConfigKey}) Le package {dep.FullName}@{depVersion} est introuvable."); returnCode = 1; @@ -399,10 +399,8 @@ void HandleFile(FileInfo file) Directory.CreateDirectory(moduleFolder); - using var packageStream = new MemoryStream(); - 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); + using var packageReader = await NugetUtils.DownloadPackageAsync(dep.FullName, depVersion); + var nuspecReader = await packageReader.GetNuspecReaderAsync(CancellationToken.None); var dependencies = nuspecReader.GetDependencyGroups() .Single(dg => dg.TargetFramework.ToString() == framework) @@ -423,10 +421,7 @@ void HandleFile(FileInfo file) var newDeps = new List(); foreach (var otherDep in dependencies) { - using var packageStreamDep = new MemoryStream(); - await nugetResource.CopyNupkgToStreamAsync(otherDep.Id, otherDep.VersionRange.MinVersion, packageStreamDep, nugetCache, NullLogger.Instance, ct); - - using var packageReaderDep = new PackageArchiveReader(packageStreamDep); + using var packageReaderDep = await NugetUtils.DownloadPackageAsync(otherDep.Id, otherDep.VersionRange.MinVersion!.ToString()); var file = packageReaderDep.GetFiles().SingleOrDefault(f => f.StartsWith($"lib/{framework}") && f.EndsWith(".dll") && !f.EndsWith(".resources.dll")); if (file != null) { @@ -434,7 +429,7 @@ void HandleFile(FileInfo file) installedDependencies.Add(otherDep.Id); - var nuspecReaderDep = await packageReaderDep.GetNuspecReaderAsync(ct); + var nuspecReaderDep = await packageReaderDep.GetNuspecReaderAsync(CancellationToken.None); if (nuspecReaderDep.GetDependencyGroups().Any()) { newDeps.AddRange(nuspecReaderDep.GetDependencyGroups() @@ -480,8 +475,20 @@ void HandleFile(FileInfo file) topModelLock.Write(); + foreach (var dep in deps) + { + dep.LatestVersion = (await NugetUtils.GetLatestVersionAsync(dep.FullName))?.Version; + } + logger.LogInformation($"Générateurs utilisés :{Environment.NewLine} {string.Join($"{Environment.NewLine} ", resolvedConfigKeys.Select(rck => $"- {rck.Key}: {rck.Value}"))}"); + var depsToUpdate = deps.Where(dep => dep.LatestVersion != null && dep.LatestVersion != dep.Version.Version); + if (depsToUpdate.Any()) + { + logger.LogWarning($"Il existe une mise à jour pour les générateurs suivants :{Environment.NewLine} {string.Join($"{Environment.NewLine} ", depsToUpdate.Select(dep => $"- {dep.ConfigKey}: {dep.Version.Version} -> {dep.LatestVersion}"))}"); + logger.LogWarning($"Vous pouvez lancer la commande `modgen --update {(depsToUpdate.Count() == 1 ? depsToUpdate.Single().ConfigKey : "all")}` pour effectuer la mise à jour."); + } + if (schemaMode || hasInstalled) { logger.LogInformation("Génération du schéma de configuration..."); diff --git a/TopModel.Generator/TopModel.Generator.csproj b/TopModel.Generator/TopModel.Generator.csproj index 908b86ca..0271e980 100644 --- a/TopModel.Generator/TopModel.Generator.csproj +++ b/TopModel.Generator/TopModel.Generator.csproj @@ -41,8 +41,6 @@ - - diff --git a/TopModel.Utils/ModuleLatestVersion.cs b/TopModel.Utils/ModuleLatestVersion.cs new file mode 100644 index 00000000..ee4beae3 --- /dev/null +++ b/TopModel.Utils/ModuleLatestVersion.cs @@ -0,0 +1,3 @@ +namespace TopModel.Utils; + +public record ModuleLatestVersion(string Version, DateTime CheckDate); diff --git a/TopModel.Utils/NugetUtils.cs b/TopModel.Utils/NugetUtils.cs new file mode 100644 index 00000000..b8275947 --- /dev/null +++ b/TopModel.Utils/NugetUtils.cs @@ -0,0 +1,107 @@ +using System.Text.Json; +using NuGet.Common; +using NuGet.Packaging; +using NuGet.Protocol; +using NuGet.Protocol.Core.Types; +using NuGet.Versioning; + +namespace TopModel.Utils; + +public static class NugetUtils +{ + private static readonly string CacheFile = Path.Combine(NuGetEnvironment.GetFolderPath(NuGetFolderPath.Temp), "topmodel-cache.json"); + private static readonly CancellationToken Ct = CancellationToken.None; + private static readonly SourceCacheContext NugetCache = new(); + private static readonly Dictionary Versions = []; + + private static bool cantCheckVersion; + private static FindPackageByIdResource? nugetResource; + + static NugetUtils() + { + if (File.Exists(CacheFile)) + { + Versions = JsonSerializer.Deserialize>(File.ReadAllText(CacheFile))!; + } + } + + public static async Task ClearAsync() + { + Versions.Clear(); + await WriteAsync(); + } + + public static async Task DoesPackageExistsAsync(string id, string version) + { + var nugetResource = await GetNugetResourceAsync(); + return await nugetResource.DoesPackageExistAsync(id, new NuGetVersion(version), NugetCache, NullLogger.Instance, Ct); + } + + public static async Task DownloadPackageAsync(string id, string version) + { + var nugetResource = await GetNugetResourceAsync(); + var packageStream = new MemoryStream(); + await nugetResource.CopyNupkgToStreamAsync(id, new NuGetVersion(version), packageStream, NugetCache, NullLogger.Instance, Ct); + return new PackageArchiveReader(packageStream); + } + + public static async Task GetLatestVersionAsync(string id) + { + if (cantCheckVersion) + { + return null; + } + + if (Versions.TryGetValue(id, out var cachedVersion)) + { + if (cachedVersion.CheckDate.AddHours(6) < DateTime.UtcNow) + { + Versions.Remove(id); + } + else + { + return new TopModelLockModule { Version = cachedVersion.Version }; + } + } + + try + { + var nugetResource = await GetNugetResourceAsync(); + var moduleVersions = await nugetResource.GetAllVersionsAsync(id, NugetCache, NullLogger.Instance, Ct); + + if (!moduleVersions.Any()) + { + return null; + } + + var nugetVersion = moduleVersions.Last().Version; + var version = new TopModelLockModule { Version = $"{nugetVersion.Major}.{nugetVersion.Minor}.{nugetVersion.Build}" }; + + Versions[id] = new(version.Version, DateTime.UtcNow); + await WriteAsync(); + return version; + } + catch (FatalProtocolException) + { + // Si on a pas internet par exemple. + cantCheckVersion = true; + return null; + } + } + + private static async Task GetNugetResourceAsync() + { + if (nugetResource == null) + { + var nugetRepository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json"); + nugetResource = await nugetRepository.GetResourceAsync(); + } + + return nugetResource; + } + + private static async Task WriteAsync() + { + await File.WriteAllTextAsync(CacheFile, JsonSerializer.Serialize(Versions)); + } +} diff --git a/TopModel.Utils/TopModel.Utils.csproj b/TopModel.Utils/TopModel.Utils.csproj index ac0ce329..2afb4f57 100644 --- a/TopModel.Utils/TopModel.Utils.csproj +++ b/TopModel.Utils/TopModel.Utils.csproj @@ -43,6 +43,7 @@ +