From d925486db166f562868e71568760a09b6a9383d7 Mon Sep 17 00:00:00 2001 From: Nice3point Date: Fri, 30 Aug 2024 17:21:28 +0300 Subject: [PATCH] Multiuser update dependencies support --- .../DependenciesReport.csproj | 2 +- .../DependenciesReport/DependenciesTools.cs | 83 ++++++++----------- .../Models/AssemblyDescriptor.cs | 3 + .../Models/DirectoryDescriptor.cs | 6 ++ source/DependenciesReport/Program.cs | 46 +++++++--- source/DependenciesReport/TableFormater.cs | 64 +++++++++++--- 6 files changed, 133 insertions(+), 71 deletions(-) create mode 100644 source/DependenciesReport/Models/AssemblyDescriptor.cs create mode 100644 source/DependenciesReport/Models/DirectoryDescriptor.cs diff --git a/source/DependenciesReport/DependenciesReport.csproj b/source/DependenciesReport/DependenciesReport.csproj index e515c292..957aff93 100644 --- a/source/DependenciesReport/DependenciesReport.csproj +++ b/source/DependenciesReport/DependenciesReport.csproj @@ -4,7 +4,7 @@ enable Exe latest - net8.0 + net8.0-windows enable false true diff --git a/source/DependenciesReport/DependenciesTools.cs b/source/DependenciesReport/DependenciesTools.cs index d0831c62..cccf8609 100644 --- a/source/DependenciesReport/DependenciesTools.cs +++ b/source/DependenciesReport/DependenciesTools.cs @@ -1,81 +1,66 @@ -namespace DependenciesReport; +using System.Reflection; +using DependenciesReport.Models; + +namespace DependenciesReport; public static class DependenciesTools { - public static Dictionary> CreateDependenciesMap(string[] directories) + public static List CreateDependenciesMap(string[] directories) { - var dependenciesMap = new Dictionary>(); + var dependenciesMap = new List(); + foreach (var directory in directories) { + var directoryDescriptor = new DirectoryDescriptor(Path.GetFileName(directory), directory); var assemblies = Directory.GetFiles(directory, "*.dll"); foreach (var assembly in assemblies) { var assemblyName = Path.GetFileName(assembly); - var assemblyVersion = AssemblyUtils.GetAssemblyVersion(assembly); - - if (!dependenciesMap.TryGetValue(assemblyName, out var value)) - { - value = new Dictionary(); - dependenciesMap[assemblyName] = value; - } + var assemblyVersion = AssemblyName.GetAssemblyName(assembly).Version ?? new Version(); - if (assemblyVersion != null) - { - value[Path.GetFileName(directory)] = assemblyVersion; - } + var assemblyDescriptor = new AssemblyDescriptor(assemblyName, assembly, assemblyVersion); + directoryDescriptor.Assemblies.Add(assemblyDescriptor); } + + dependenciesMap.Add(directoryDescriptor); } return dependenciesMap; } - public static void UpgradeDependencies(Dictionary>> dependenciesMaps) + public static void UpgradeDependencies(Dictionary> dependenciesMaps) { - foreach (var (yearDirectory, dependenciesMap) in dependenciesMaps) + foreach (var (yearDirectory, directories) in dependenciesMaps) { - foreach (var assemblyName in dependenciesMap.Keys) + var assemblyGroups = directories + .SelectMany(dir => dir.Assemblies, (dir, assembly) => new { Directory = dir, Assembly = assembly }) + .GroupBy(x => x.Assembly.Name); + + foreach (var assemblyGroup in assemblyGroups) { - string? maxVersion = null; - string? maxVersionDirectory = null; + var maxAssembly = assemblyGroup.MaxBy(x => x.Assembly.Version); + if (maxAssembly is null) continue; - foreach (var directory in dependenciesMap[assemblyName].Keys) - { - var version = dependenciesMap[assemblyName][directory]; - if (maxVersion == null || CompareVersions(version, maxVersion) > 0) - { - maxVersion = version; - maxVersionDirectory = directory; - } - } - - if (maxVersionDirectory is not null && maxVersion is not null) - { - var maxVersionFilePath = Path.Combine(yearDirectory, maxVersionDirectory, assemblyName); + var maxVersion = maxAssembly.Assembly.Version; + var maxVersionFilePath = maxAssembly.Assembly.Path; - foreach (var directory in dependenciesMap[assemblyName].Keys) + foreach (var entry in assemblyGroup) + { + if (entry.Assembly.Version.CompareTo(maxVersion) < 0) { - if (directory != maxVersionDirectory) + try { - var targetFilePath = Path.Combine(yearDirectory, directory, assemblyName); - var targetVersion = dependenciesMap[assemblyName][directory]; - - if (CompareVersions(targetVersion, maxVersion) < 0) - { - File.Copy(maxVersionFilePath, targetFilePath, true); - Console.WriteLine($"Assembly {targetFilePath} was upgraded from version {targetVersion} to version {maxVersion}."); - } + File.Copy(maxVersionFilePath, entry.Assembly.Path, true); + Console.WriteLine($"Assembly {entry.Assembly.Path} was upgraded from version {entry.Assembly.Version.ToString(3)} to {maxVersion.ToString(3)}."); + } + catch (UnauthorizedAccessException) + { + Console.WriteLine($"No access to upgrade {entry.Assembly.Path}"); } } } } } } - - private static int CompareVersions(string version1, string version2) - { - var originVersion = new Version(version1); - var targetVersion = new Version(version2); - return originVersion.CompareTo(targetVersion); - } } \ No newline at end of file diff --git a/source/DependenciesReport/Models/AssemblyDescriptor.cs b/source/DependenciesReport/Models/AssemblyDescriptor.cs new file mode 100644 index 00000000..a9a1a5d6 --- /dev/null +++ b/source/DependenciesReport/Models/AssemblyDescriptor.cs @@ -0,0 +1,3 @@ +namespace DependenciesReport.Models; + +public sealed record AssemblyDescriptor(string Name, string Path, Version Version); \ No newline at end of file diff --git a/source/DependenciesReport/Models/DirectoryDescriptor.cs b/source/DependenciesReport/Models/DirectoryDescriptor.cs new file mode 100644 index 00000000..02a5a81f --- /dev/null +++ b/source/DependenciesReport/Models/DirectoryDescriptor.cs @@ -0,0 +1,6 @@ +namespace DependenciesReport.Models; + +public sealed record DirectoryDescriptor(string Name, string Path) +{ + public List Assemblies { get; init; } = []; +} \ No newline at end of file diff --git a/source/DependenciesReport/Program.cs b/source/DependenciesReport/Program.cs index 5c735bcf..fdf07e5b 100644 --- a/source/DependenciesReport/Program.cs +++ b/source/DependenciesReport/Program.cs @@ -1,27 +1,44 @@ using System.Diagnostics; using System.Globalization; +using System.Security.Principal; using DependenciesReport; +using DependenciesReport.Models; var userFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); -var addinsPath = Path.Combine(userFolder, "Autodesk", "Revit", "Addins"); -var yearsDirectories = Directory.GetDirectories(addinsPath); +var machineFolder = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData); +var userAddinsPath = Path.Combine(userFolder, "Autodesk", "Revit", "Addins"); +var machineAddinsPath = Path.Combine(machineFolder, "Autodesk", "Revit", "Addins"); +var yearsGroups = Directory.GetDirectories(userAddinsPath) + .Union(Directory.GetDirectories(machineAddinsPath)) + .GroupBy(Path.GetFileName) + .ToArray(); var reportName = $"DependenciesReport-{DateTime.Now.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo)}.txt"; var reportPath = Path.Combine(Path.GetTempPath(), reportName); var summaryWriter = new SummaryWriter(reportPath); -var dependenciesMaps = new Dictionary>>(); -foreach (var yearDirectory in yearsDirectories) +var dependenciesMaps = new Dictionary>(); +foreach (var yearsGroup in yearsGroups) { - summaryWriter.Write($"Revit version: {Path.GetFileName(yearDirectory)}"); + if (yearsGroup.Key is null) continue; + + summaryWriter.Write($"Revit version: {Path.GetFileName(yearsGroup.Key)}"); summaryWriter.WriteLine(); - var directories = Directory.GetDirectories(yearDirectory); + var directories = yearsGroup.SelectMany(Directory.GetDirectories).ToArray(); var dependenciesMap = DependenciesTools.CreateDependenciesMap(directories); - var dependenciesTable = TableFormater.CreateReportTable(directories, dependenciesMap); - dependenciesMaps.Add(yearDirectory, dependenciesMap); + var dependenciesTable = TableFormater.CreateReportTable(dependenciesMap); + + if (dependenciesTable.Rows.Count == 0) + { + summaryWriter.Write("No conflicts detected."); + } + else + { + dependenciesMaps.Add(yearsGroup.Key, dependenciesMap); + summaryWriter.Write(dependenciesTable.ToMinimalString()); + } - summaryWriter.Write(dependenciesTable.ToMinimalString()); summaryWriter.WriteLine(); } @@ -47,9 +64,18 @@ if (string.Compare(response, "Y", StringComparison.OrdinalIgnoreCase) == 0) { + var identity = WindowsIdentity.GetCurrent(); + var principal = new WindowsPrincipal(identity); + var isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator); + + if (!isAdmin) + { + Console.WriteLine("Permissions denied, only user dependencies will be updated. For a complete update, run application as administrator."); + } + DependenciesTools.UpgradeDependencies(dependenciesMaps); - Console.WriteLine("Success!"); } +Console.WriteLine(); Console.WriteLine("You can now close this terminal with Ctrl+C"); Console.ReadLine(); \ No newline at end of file diff --git a/source/DependenciesReport/TableFormater.cs b/source/DependenciesReport/TableFormater.cs index 0ce88603..258ce925 100644 --- a/source/DependenciesReport/TableFormater.cs +++ b/source/DependenciesReport/TableFormater.cs @@ -1,27 +1,69 @@ -using System.Collections; -using ConsoleTables; +using ConsoleTables; +using DependenciesReport.Models; namespace DependenciesReport; public static class TableFormater { - public static ConsoleTable CreateReportTable(string[] directories, Dictionary> dependenciesMap) + public static ConsoleTable CreateReportTable(List dependenciesMap) { - var conflictMap = dependenciesMap.Where(pair => pair.Value.Values.Distinct().Count() > 1); - var table = new ConsoleTable(directories.Select(Path.GetFileName).Prepend("Library").ToArray()); + var conflictMap = GetConflictMap(dependenciesMap); - foreach (var conflictPair in conflictMap) + var assemblyNames = conflictMap + .SelectMany(dir => dir.Assemblies) + .Select(assembly => assembly.Name) + .Distinct() + .OrderBy(name => name) + .ToList(); + + var table = new ConsoleTable(new[] { "Dependency" }.Concat(conflictMap.Select(dir => dir.Name)).ToArray()); + + foreach (var assemblyName in assemblyNames) { - var row = new ArrayList { conflictPair.Key }; - foreach (var directory in directories) + var row = new List { assemblyName }; + foreach (var dir in conflictMap) { - var directoryName = Path.GetFileName(directory); - row.Add(conflictPair.Value.GetValueOrDefault(directoryName, "-")); + var assembly = dir.Assemblies.FirstOrDefault(assembly => assembly.Name == assemblyName); + row.Add(assembly?.Version.ToString(3) ?? "-"); } table.AddRow(row.ToArray()); } - + return table; } + + private static List GetConflictMap(List directories) + { + var versionMap = directories + .SelectMany(directory => directory.Assemblies) + .GroupBy(assembly => assembly.Name) + .ToDictionary( + group => group.Key, + group => group.Select(assembly => assembly.Version).Distinct().ToList()); + + var uniqueAssemblies = new HashSet( + directories + .SelectMany(directory => directory.Assemblies) + .Where(assembly => versionMap[assembly.Name].Count > 1)); + + var conflictDirectories = new List(); + foreach (var directory in directories) + { + var conflictAssemblies = new List(); + foreach (var assembly in directory.Assemblies) + { + if (uniqueAssemblies.Contains(assembly)) conflictAssemblies.Add(assembly); + } + + var conflictDirectory = new DirectoryDescriptor(directory.Name, directory.Path) + { + Assemblies = conflictAssemblies + }; + + if (conflictDirectory.Assemblies.Count > 0) conflictDirectories.Add(conflictDirectory); + } + + return conflictDirectories; + } } \ No newline at end of file