diff --git a/tools/NetCorePsd1Sync/NetCorePsd1Sync/NetCoreDefinitionGenerator.cs b/tools/NetCorePsd1Sync/NetCorePsd1Sync/NetCoreDefinitionGenerator.cs index 085ed2f793ae..3553d4425c64 100644 --- a/tools/NetCorePsd1Sync/NetCorePsd1Sync/NetCoreDefinitionGenerator.cs +++ b/tools/NetCorePsd1Sync/NetCorePsd1Sync/NetCoreDefinitionGenerator.cs @@ -19,6 +19,7 @@ using System.IO; using System.Linq; using System.Management.Automation; +using System.Reflection; using NetCorePsd1Sync.Model; using NetCorePsd1Sync.Utility; using static NetCorePsd1Sync.Model.PsDefinitionConstants; @@ -87,18 +88,18 @@ public static Hashtable GetHashtable(PowerShell powershell, string path) return hashtable; } - public static IEnumerable GetDesktopHashtables(IEnumerable desktopFilePaths) + public static IEnumerable GetHashtables(IEnumerable filePaths) { using (var powershell = PowerShell.Create()) { - foreach (var path in desktopFilePaths) + foreach (var path in filePaths) { yield return GetHashtable(powershell, path); } } } - public static PsDefinition CreateNetCoreDefinition(Hashtable desktopData) + public static PsDefinition CreateNewNetCoreDefinition(Hashtable desktopData) { var psData = new PsData(); var desktopPsData = desktopData.GetValueAsHashtable("PrivateData").GetValueAsHashtable("PSData"); @@ -138,5 +139,79 @@ public static PsDefinition CreateNetCoreDefinition(Hashtable desktopData) PrivateData = new PrivateData { PsData = psData } }; } + + private static ModuleReference CreateModuleReferenceFromHashtable(Hashtable data) + { + var guid = data.GetValueAsStringOrDefault("GUID"); + return new ModuleReference + { + ModuleName = data.GetValueAsStringOrDefault("ModuleName"), + ModuleVersion = data.GetValueAsVersionOrDefault("ModuleVersion"), + Guid = String.IsNullOrEmpty(guid) ? (Guid?)null : Guid.Parse(guid) + }; + } + + private static List CreateModuleReferenceList(Hashtable data, string key) => + data.GetValueAsArrayOrDefault(key)?.OfType().Select(CreateModuleReferenceFromHashtable).ToList(); + + public static PsDefinition CreateDefinitionFromExisting(Hashtable existingDefinition, PsDefinitionHeader existingHeader) + { + var psData = new PsData(); + var existingPsData = existingDefinition.GetValueAsHashtable("PrivateData").GetValueAsHashtable("PSData"); + if (existingPsData.Any()) + { + var licenseUri = existingPsData.GetValueAsStringOrDefault("LicenseUri"); + var projectUri = existingPsData.GetValueAsStringOrDefault("ProjectUri"); + var iconUri = existingPsData.GetValueAsStringOrDefault("IconUri"); + var requireLicenseAcceptance = existingPsData.GetValueAsStringOrDefault("RequireLicenseAcceptance"); + psData = new PsData + { + Tags = existingPsData.GetValueAsStringListOrDefault("Tags"), + LicenseUri = String.IsNullOrEmpty(licenseUri) ? null : new Uri(licenseUri), + ProjectUri = String.IsNullOrEmpty(projectUri) ? null : new Uri(projectUri), + IconUri = String.IsNullOrEmpty(iconUri) ? null : new Uri(iconUri), + ReleaseNotes = existingPsData.GetValueAsStringOrDefault("ReleaseNotes"), + Prerelease = existingPsData.GetValueAsStringOrDefault("Prerelease"), + RequireLicenseAcceptance = String.IsNullOrEmpty(requireLicenseAcceptance) ? (bool?)null : Boolean.Parse(requireLicenseAcceptance), + ExternalModuleDependencies = CreateModuleReferenceList(existingPsData, "ExternalModuleDependencies") + }; + } + + var processorArchitecture = existingDefinition.GetValueAsStringOrDefault("ProcessorArchitecture"); + return new PsDefinition + { + ManifestHeader = existingHeader, + RootModule = existingDefinition.GetValueAsStringOrDefault("RootModule"), + ModuleVersion = existingDefinition.GetValueAsVersionOrDefault("ModuleVersion"), + CompatiblePsEditions = existingDefinition.GetValueAsStringListOrDefault("CompatiblePSEditions"), + Guid = Guid.Parse(existingDefinition.GetValueAsStringOrDefault("GUID")), + Author = existingDefinition.GetValueAsStringOrDefault("Author"), + CompanyName = existingDefinition.GetValueAsStringOrDefault("CompanyName"), + Copyright = existingDefinition.GetValueAsStringOrDefault("Copyright"), + Description = existingDefinition.GetValueAsStringOrDefault("Description"), + PowerShellVersion = existingDefinition.GetValueAsVersionOrDefault("PowerShellVersion"), + PowerShellHostName = existingDefinition.GetValueAsStringOrDefault("PowerShellHostName"), + PowerShellHostVersion = existingDefinition.GetValueAsVersionOrDefault("PowerShellHostVersion"), + DotNetFrameworkVersion = existingDefinition.GetValueAsVersionOrDefault("DotNetFrameworkVersion"), + ClrVersion = existingDefinition.GetValueAsVersionOrDefault("CLRVersion"), + ProcessorArchitecture = processorArchitecture != null ? Enum.Parse(processorArchitecture) : (ProcessorArchitecture?)null, + RequiredModules = CreateModuleReferenceList(existingDefinition, "RequiredModules"), + RequiredAssemblies = existingDefinition.GetValueAsStringListOrDefault("RequiredAssemblies"), + ScriptsToProcess = existingDefinition.GetValueAsStringListOrDefault("ScriptsToProcess"), + TypesToProcess = existingDefinition.GetValueAsStringListOrDefault("TypesToProcess"), + FormatsToProcess = existingDefinition.GetValueAsStringListOrDefault("FormatsToProcess"), + NestedModules = existingDefinition.GetValueAsStringListOrDefault("NestedModules")?.Select(m => new ModuleReference { ModuleName = m }).ToList(), + FunctionsToExport = existingDefinition.GetValueAsStringListOrDefault("FunctionsToExport"), + CmdletsToExport = existingDefinition.GetValueAsStringListOrDefault("CmdletsToExport"), + VariablesToExport = existingDefinition.GetValueAsStringListOrDefault("VariablesToExport"), + AliasesToExport = existingDefinition.GetValueAsStringListOrDefault("AliasesToExport"), + DscResourcesToExport = existingDefinition.GetValueAsStringListOrDefault("DscResourcesToExport"), + ModuleList = CreateModuleReferenceList(existingDefinition, "ModuleList"), + FileList = existingDefinition.GetValueAsStringListOrDefault("FileList"), + PrivateData = new PrivateData { PsData = psData }, + HelpInfoUri = existingDefinition.GetValueAsStringOrDefault("HelpInfoURI"), + DefaultCommandPrefix = existingDefinition.GetValueAsStringOrDefault("DefaultCommandPrefix") + }; + } } } diff --git a/tools/NetCorePsd1Sync/NetCorePsd1Sync/Program.cs b/tools/NetCorePsd1Sync/NetCorePsd1Sync/Program.cs index d1de9edde12c..8881713c6d05 100644 --- a/tools/NetCorePsd1Sync/NetCorePsd1Sync/Program.cs +++ b/tools/NetCorePsd1Sync/NetCorePsd1Sync/Program.cs @@ -18,6 +18,7 @@ using System.IO; using System.Linq; using System.Management.Automation; +using NetCorePsd1Sync.Model; using NetCorePsd1Sync.Utility; using static NetCorePsd1Sync.NetCoreDefinitionGenerator; @@ -27,29 +28,75 @@ public static class Program { private const string Validate = "-v"; private const string Create = "-c"; + private const string UpdateVersion = "-u"; private static readonly Dictionary> ModeMap = new Dictionary> { { Validate, ValidateDefinitionFiles }, - { Create, CreateDefinitionFiles } + { Create, CreateDefinitionFiles }, + { UpdateVersion, p => UpdateModuleVersions(p, _newVersion) } }; + + private static Version _newVersion; + public static void Main(string[] args) { - var rmPath = args.FirstOrDefault(a => !ModeMap.ContainsKey(a)) ?? @"..\..\..\src\ResourceManager"; + var rmPath = args.FirstOrDefault(a => !ModeMap.ContainsKey(a.ToLower()) && !Version.TryParse(a, out var _)) ?? @"..\..\..\src\ResourceManager"; if (!Directory.Exists(rmPath)) { throw new ArgumentException($"Directory [{rmPath}] does not exist"); } //https://stackoverflow.com/a/17563994/294804 - var mode = args.Any(a => a.IndexOf(Create, StringComparison.InvariantCultureIgnoreCase) >= 0) ? Create : Validate; + var mode = ModeMap.Keys.FirstOrDefault(k => args.Any(a => a.IndexOf(k, StringComparison.InvariantCultureIgnoreCase) >= 0)) ?? Validate; + if(mode == UpdateVersion) + { + var newVersion = args.FirstOrDefault(a => Version.TryParse(a, out var _)); + if (newVersion == null) + { + throw new ArgumentException($"Module update version must be provided"); + } + _newVersion = Version.Parse(newVersion); + } ModeMap[mode](rmPath); } + private static void UpdateModuleVersions(string rmPath, Version newVersion) + { + var modulePaths = GetModulePaths(rmPath, true); + var desktopFilePaths = GetDesktopFilePaths(modulePaths); + var netCoreFilePaths = desktopFilePaths.Select(ConvertDesktopToNetCorePath).Where(File.Exists).ToList(); + netCoreFilePaths.Add(Path.Combine(rmPath, @"..\Storage\Azure.Storage.Netcore.psd1")); + netCoreFilePaths.Add(Path.Combine(rmPath, @"..\..\tools\AzureRM.Netcore\AzureRM.Netcore.psd1")); + netCoreFilePaths.Add(Path.Combine(rmPath, @"AnalysisServices\Commands.AnalysisServices.Dataplane\Azure.AnalysisServices.Netcore.psd1")); + var netCoreHashTables = GetHashtables(netCoreFilePaths); + + foreach (var netCoreHashtable in netCoreHashTables) + { + var netCoreFilePath = netCoreHashtable.GetValueAsString("FilePath"); + var definitionContent = File.ReadAllLines(netCoreFilePath); + const string moduleNameLeader = "# Module manifest for module "; + var headerModuleName = definitionContent.First(c => c.StartsWith(moduleNameLeader)).Replace(moduleNameLeader, String.Empty).Replace("'", String.Empty); + const string authorLeader = "# Generated by: "; + var headerAuthor = definitionContent.First(c => c.StartsWith(authorLeader)).Replace(authorLeader, String.Empty); + const string dateLeader = "# Generated on: "; + var headerDate = DateTime.Parse(definitionContent.First(c => c.StartsWith(dateLeader)).Replace(dateLeader, String.Empty)); + Console.WriteLine($"Updating {netCoreFilePath} to version {newVersion}"); + var netCoreDefinition = CreateDefinitionFromExisting(netCoreHashtable, new PsDefinitionHeader { ModuleName = headerModuleName, Author = headerAuthor, Date = headerDate }); + netCoreDefinition.ModuleVersion = newVersion; + foreach (var requiredModule in netCoreDefinition.RequiredModules ?? Enumerable.Empty()) + { + requiredModule.ModuleVersion = newVersion; + } + + File.WriteAllLines(netCoreFilePath, netCoreDefinition.ToDefinitionEntry()); + } + } + private static void ValidateDefinitionFiles(string rmPath) { var modulePaths = GetModulePaths(rmPath, true); var desktopFilePaths = GetDesktopFilePaths(modulePaths); - var desktopHashtables = GetDesktopHashtables(desktopFilePaths); + var desktopHashtables = GetHashtables(desktopFilePaths); foreach (var desktopHashtable in desktopHashtables) { var netCorePath = ConvertDesktopToNetCorePath(desktopHashtable.GetValueAsString("FilePath")); @@ -109,18 +156,18 @@ private static void CreateDefinitionFiles(string rmPath) { var modulePaths = GetModulePaths(rmPath); var desktopFilePaths = GetDesktopFilePaths(modulePaths); - var desktopHashtables = GetDesktopHashtables(desktopFilePaths); + var desktopHashtables = GetHashtables(desktopFilePaths); foreach (var desktopHashtable in desktopHashtables) { var netCoreFilePath = ConvertDesktopToNetCorePath(desktopHashtable.GetValueAsString("FilePath")); Console.WriteLine($"Creating {netCoreFilePath}"); - var netCoreDefinition = CreateNetCoreDefinition(desktopHashtable); + var netCoreDefinition = CreateNewNetCoreDefinition(desktopHashtable); if (File.Exists(netCoreFilePath)) { using (var powershell = PowerShell.Create()) { var netCoreHashtable = GetHashtable(powershell, netCoreFilePath); - netCoreDefinition = CreateNetCoreDefinition(netCoreHashtable); + netCoreDefinition = CreateNewNetCoreDefinition(netCoreHashtable); var rootModule = netCoreHashtable.GetValueAsString("RootModule"); netCoreDefinition.RootModule = rootModule == String.Empty ? null : rootModule; netCoreDefinition.Guid = new Guid(netCoreHashtable.GetValueAsString("GUID")); diff --git a/tools/NetCorePsd1Sync/NetCorePsd1Sync/Utility/HashtableExtensions.cs b/tools/NetCorePsd1Sync/NetCorePsd1Sync/Utility/HashtableExtensions.cs index 99bcd659ca45..330592b85491 100644 --- a/tools/NetCorePsd1Sync/NetCorePsd1Sync/Utility/HashtableExtensions.cs +++ b/tools/NetCorePsd1Sync/NetCorePsd1Sync/Utility/HashtableExtensions.cs @@ -21,19 +21,34 @@ namespace NetCorePsd1Sync.Utility { internal static class HashtableExtensions { - public static string GetValueAsString(this Hashtable hashtable, string key) => hashtable[key]?.ToString() ?? String.Empty; + public static string GetValueAsString(this Hashtable hashtable, string key) => GetValueAsStringOrDefault(hashtable, key) ?? String.Empty; - public static object[] GetValueAsArray(this Hashtable hashtable, string key) => hashtable[key] as object[] ?? new object[]{}; + public static string GetValueAsStringOrDefault(this Hashtable hashtable, string key) => hashtable[key]?.ToString(); + + public static Version GetValueAsVersion(this Hashtable hashtable, string key) => GetValueAsVersionOrDefault(hashtable, key) ?? new Version(); + + public static Version GetValueAsVersionOrDefault(this Hashtable hashtable, string key) + { + var valueAsString = GetValueAsStringOrDefault(hashtable, key); + if (valueAsString == null || !Version.TryParse(valueAsString, out var version)) return null; + return version; + } + + public static object[] GetValueAsArray(this Hashtable hashtable, string key) => GetValueAsArrayOrDefault(hashtable, key) ?? new object[]{}; + + public static object[] GetValueAsArrayOrDefault(this Hashtable hashtable, string key) => hashtable[key] as object[]; public static Hashtable GetValueAsHashtable(this Hashtable hashtable, string key) => hashtable[key] as Hashtable ?? new Hashtable(); - public static List GetValueAsStringList(this Hashtable hashtable, string key) + public static List GetValueAsStringList(this Hashtable hashtable, string key) => GetValueAsStringListOrDefault(hashtable, key) ?? new List(); + + public static List GetValueAsStringListOrDefault(this Hashtable hashtable, string key) { if (hashtable[key] is string stringValue) { - return new List{stringValue}; + return new List { stringValue }; } - return hashtable.GetValueAsArray(key).OfType().ToList(); + return hashtable.GetValueAsArrayOrDefault(key)?.OfType().ToList(); } public static bool Any(this Hashtable hashtable) => hashtable.Count > 0;