diff --git a/.gitignore b/.gitignore index 55986f9..447b6d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +src/.idea/ src/.vs/ temp/ src2/ @@ -13,3 +14,4 @@ bin obj src/Ormico.DbPatchManager.CLI/deb/ +src/Ormico.DbPatchManager.CLI/Properties/launchSettings.json diff --git a/src/Ormico.DbPatchManager.CLI/Ormico.DbPatchManager.CLI.csproj b/src/Ormico.DbPatchManager.CLI/Ormico.DbPatchManager.CLI.csproj index eefcf72..54bf89d 100644 --- a/src/Ormico.DbPatchManager.CLI/Ormico.DbPatchManager.CLI.csproj +++ b/src/Ormico.DbPatchManager.CLI/Ormico.DbPatchManager.CLI.csproj @@ -1,16 +1,19 @@ - + Exe - net6.0 - false + net8.0 + true LICENSE - https://github.com/ormico/dbpatchmanager + true + https://github.com/ncino/dbpatchmanager Copyright (c) 2020 Zack Moore Zack Moore Ormico Ormico DB Patch Manager CLI + true + dbpatch dbpatch Ormico.DbPatchManager.CLI https://dbpatch.dev/ @@ -44,7 +47,7 @@ - + diff --git a/src/Ormico.DbPatchManager.CLI/Program.cs b/src/Ormico.DbPatchManager.CLI/Program.cs index af33596..3f9b591 100644 --- a/src/Ormico.DbPatchManager.CLI/Program.cs +++ b/src/Ormico.DbPatchManager.CLI/Program.cs @@ -1,5 +1,6 @@ using System; using CommandLine; +using Newtonsoft.Json; using Ormico.DbPatchManager.Logic; using Ormico.DbPatchManager.CLI.CommandLineOptions; @@ -12,7 +13,8 @@ static int Main(string[] args) int rc = 0; try { - rc = CommandLine.Parser.Default.ParseArguments(args, typeof(InitCmdLineOptions), typeof(AddPatchCmdLineOptions), typeof(BuildCmdLineOptions)) + rc = CommandLine.Parser.Default.ParseArguments(args, typeof(InitCmdLineOptions), + typeof(AddPatchCmdLineOptions), typeof(BuildCmdLineOptions)) .MapResult( (InitCmdLineOptions o) => InitBuildSettings(o), (AddPatchCmdLineOptions o) => AddPatch(o), @@ -20,6 +22,15 @@ static int Main(string[] args) err => 1 ); } + catch (JsonException jsonException) + { + Console.WriteLine("{0}", jsonException.Message); + Console.WriteLine("{0}", jsonException.StackTrace); + if (jsonException.InnerException != null) + { + Console.WriteLine("{0}", jsonException.InnerException.Message); + } + } catch (Exception ex) { Console.WriteLine($"{ex.Message}"); @@ -54,19 +65,29 @@ static int InitBuildSettings(InitCmdLineOptions options) static int AddPatch(AddPatchCmdLineOptions options) { int rc = 0; + var startTime = DateTimeOffset.Now; + Console.WriteLine("{0:O} - Database Add Patch Started", startTime); PatchManager manager = new PatchManager(_patchFileName, _patchLocalFileName); //todo: pass all settings manager.AddPatch(options.Name, new PatchOptions() { }); + var endTime = DateTimeOffset.Now; + Console.WriteLine("{0:O} - Database Add Patch Completed", endTime); + Console.WriteLine("{0:g} - Add Patch Time", endTime.Subtract(startTime)); return rc; } static int Build(BuildCmdLineOptions options) { int rc = 0; + var startTime = DateTimeOffset.Now; + Console.WriteLine("{0:O} - Database Build Started", startTime); PatchManager manager = new PatchManager(_patchFileName, _patchLocalFileName); manager.Build(); + var endTime = DateTimeOffset.Now; + Console.WriteLine("{0:O} - Database Build Completed", endTime); + Console.WriteLine("{0:g} - Build Time", endTime.Subtract(startTime)); return rc; } } diff --git a/src/Ormico.DbPatchManager.Common/Ormico.DbPatchManager.Common.csproj b/src/Ormico.DbPatchManager.Common/Ormico.DbPatchManager.Common.csproj index 6194af5..1547253 100644 --- a/src/Ormico.DbPatchManager.Common/Ormico.DbPatchManager.Common.csproj +++ b/src/Ormico.DbPatchManager.Common/Ormico.DbPatchManager.Common.csproj @@ -1,7 +1,6 @@  - netstandard2.0 https://github.com/ormico/dbpatchmanager LICENSE Zack Moore @@ -14,6 +13,7 @@ Copyright (c) 2020 Zack Moore https://dbpatch.dev/ 2.1.2 + net8.0;netstandard2.1 @@ -27,4 +27,8 @@ + + + + diff --git a/src/Ormico.DbPatchManager.Common/PatchFileModel.cs b/src/Ormico.DbPatchManager.Common/PatchFileModel.cs new file mode 100644 index 0000000..a87a04c --- /dev/null +++ b/src/Ormico.DbPatchManager.Common/PatchFileModel.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Ormico.DbPatchManager.Common +{ + public partial class PatchFile + { + [JsonProperty("DatabaseType")] public string DatabaseType { get; set; } + + [JsonProperty("ConnectionString")] public string ConnectionString { get; set; } + + [JsonProperty("CodeFolder")] public string CodeFolder { get; set; } + + [JsonProperty("CodeFiles")] public List CodeFiles { get; set; } + + [JsonProperty("PatchFolder")] public string PatchFolder { get; set; } + + [JsonProperty("Options")] public Dictionary Options { get; set; } + + [JsonProperty("patches")] public List Patches { get; set; } + + public PatchFile() + { + Patches = new List(); + Options = new Dictionary(); + CodeFiles = new List(); + } + } + + public partial class PatchFromFile + { + [JsonProperty("id")] public string Id { get; set; } + + [JsonProperty("dependsOn")] public List DependsOn { get; set; } + + public PatchFromFile() + { + DependsOn = new List(); + } + } + + public partial class PatchFile + { + public static PatchFile FromJson(string json) => + JsonConvert.DeserializeObject(json, PatchFileConverter.Settings); + } + + public static class PatchFileSerializer + { + public static string ToJson(this PatchFile self) => + JsonConvert.SerializeObject(self, PatchFileConverter.Settings); + } + + internal static class PatchFileConverter + { + public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings + { + MetadataPropertyHandling = MetadataPropertyHandling.Ignore, + DateParseHandling = DateParseHandling.None, + Converters = + { + new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal } + }, + Formatting = Formatting.Indented + }; + } +} \ No newline at end of file diff --git a/src/Ormico.DbPatchManager.Logic.Tests/Ormico.DbPatchManager.Logic.Tests.csproj b/src/Ormico.DbPatchManager.Logic.Tests/Ormico.DbPatchManager.Logic.Tests.csproj deleted file mode 100644 index 054a8ea..0000000 --- a/src/Ormico.DbPatchManager.Logic.Tests/Ormico.DbPatchManager.Logic.Tests.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - net6.0 - - false - - - - - - - - - diff --git a/src/Ormico.DbPatchManager.Logic.Tests/UnitTest1.cs b/src/Ormico.DbPatchManager.Logic.Tests/UnitTest1.cs deleted file mode 100644 index a0c3692..0000000 --- a/src/Ormico.DbPatchManager.Logic.Tests/UnitTest1.cs +++ /dev/null @@ -1,18 +0,0 @@ -using NUnit.Framework; - -namespace Ormico.DbPatchManager.Logic.Tests -{ - public class Tests - { - [SetUp] - public void Setup() - { - } - - [Test] - public void Test1() - { - Assert.Pass(); - } - } -} \ No newline at end of file diff --git a/src/Ormico.DbPatchManager.Logic/BuildConfigurationWriter.cs b/src/Ormico.DbPatchManager.Logic/BuildConfigurationWriter.cs index dba9bb5..f221042 100644 --- a/src/Ormico.DbPatchManager.Logic/BuildConfigurationWriter.cs +++ b/src/Ormico.DbPatchManager.Logic/BuildConfigurationWriter.cs @@ -9,10 +9,7 @@ namespace Ormico.DbPatchManager.Logic { - /// - /// Readn and Write DatabaseBuildConfiguration to storage. - /// - public class BuildConfigurationWriter + public class BuildConfigurationWriter : IBuildConfigurationWriter { public BuildConfigurationWriter(string filePath, string localFilePath) { @@ -46,14 +43,17 @@ public BuildConfigurationWriter(string filePath, string localFilePath) public DatabaseBuildConfiguration Read() { DatabaseBuildConfiguration rc = null; - if(_io.File.Exists(_filePath)) + if (_io.File.Exists(_filePath)) { //rc = JsonConvert.DeserializeObject(_io.File.ReadAllText(_filePath), _jsonSettings); - var o = (Newtonsoft.Json.Linq.JObject)Newtonsoft.Json.Linq.JToken.Parse(_io.File.ReadAllText(_filePath)); + var o = (Newtonsoft.Json.Linq.JObject)Newtonsoft.Json.Linq.JToken.Parse( + _io.File.ReadAllText(_filePath)); if (_io.File.Exists(_localFilePath)) { - var localO = (Newtonsoft.Json.Linq.JObject)Newtonsoft.Json.Linq.JToken.Parse(_io.File.ReadAllText(_localFilePath)); + var localO = + (Newtonsoft.Json.Linq.JObject)Newtonsoft.Json.Linq.JToken.Parse( + _io.File.ReadAllText(_localFilePath)); o.Merge(localO, new JsonMergeSettings() { MergeArrayHandling = MergeArrayHandling.Union @@ -76,30 +76,31 @@ public DatabaseBuildConfiguration Read() rc.patches = new List(); } else - { + { var patches = (from p in o["patches"] - select new Patch() - { - Id = (string)p["id"] - }).ToList(); + select new Patch() + { + Id = (string)p["id"] + }).ToList(); // populate DependsOn - foreach(var p in patches) + foreach (var p in patches) { var cur = from x in o["patches"] - from d in x["dependsOn"] - from a in patches - where (string)x["id"] == p.Id && - a.Id == (string)d - select a; + from d in x["dependsOn"] + from a in patches + where (string)x["id"] == p.Id && + a.Id == (string)d + select a; p.DependsOn = cur.Distinct(new PatchComparer()).ToList(); //todo: double check this query var children = from x in o["patches"] - from d in x["dependsOn"] - from a in patches - where (string)d == p.Id && (string)x["id"] == a.Id - select a; + from d in x["dependsOn"] + from a in patches + where (string)d == p.Id && (string)x["id"] == a.Id + select a; p.Children = children.Distinct(new PatchComparer()).ToList(); } + rc.patches = patches.ToList(); } } @@ -107,6 +108,7 @@ from a in patches { throw new ApplicationException("Configuration file does not exist. Call init first."); } + return rc; } @@ -117,7 +119,9 @@ public void Write(DatabaseBuildConfiguration buildConfiguration) //todo: if local file exists, don't write values to patches.json if value exists in patches.local.json if (_io.File.Exists(_localFilePath)) { - var localO = (Newtonsoft.Json.Linq.JObject)Newtonsoft.Json.Linq.JToken.Parse(_io.File.ReadAllText(_localFilePath)); + var localO = + (Newtonsoft.Json.Linq.JObject)Newtonsoft.Json.Linq.JToken.Parse( + _io.File.ReadAllText(_localFilePath)); data = new JObject(); //todo: find a way to do this that isn't manual. can you loop over all values in buildConfiguration? @@ -136,8 +140,9 @@ public void Write(DatabaseBuildConfiguration buildConfiguration) data["CodeFolder"] = buildConfiguration.CodeFolder; } - if (localO["CodeFiles"] == null && buildConfiguration.CodeFiles != null) + if (localO["CodeFiles"] == null || localO["CodeFiles"].HasValues == false) { + buildConfiguration.CodeFiles = buildConfiguration.CodeFiles ?? new List(); data["CodeFiles"] = JArray.FromObject(buildConfiguration.CodeFiles); } @@ -146,20 +151,31 @@ public void Write(DatabaseBuildConfiguration buildConfiguration) data["PatchFolder"] = buildConfiguration.PatchFolder; } - if (localO["Options"] == null && buildConfiguration.Options != null) + try + { + if (localO["Options"] == null || localO["Options"].HasValues == false) + { + buildConfiguration.Options = buildConfiguration.Options ?? new Dictionary(); + data["Options"] = JObject.FromObject(buildConfiguration.Options); + } + } + catch (JsonException e) { - data["Options"] = JArray.FromObject(buildConfiguration.Options); + Console.WriteLine(e.Message); + data["Options"] = JObject.FromObject(new Dictionary()); } if (localO["patches"] == null && buildConfiguration.patches != null) { data["patches"] = JArray.FromObject(from p in buildConfiguration.patches - select new - { - id = p.Id, - dependsOn = p.DependsOn != null ? (from d in p.DependsOn - select d.Id) : null - }); + select new + { + id = p.Id, + dependsOn = p.DependsOn != null + ? (from d in p.DependsOn + select d.Id) + : null + }); } } else @@ -174,16 +190,18 @@ public void Write(DatabaseBuildConfiguration buildConfiguration) PatchFolder = buildConfiguration.PatchFolder, Options = buildConfiguration.Options, patches = from p in buildConfiguration.patches - select new - { - id = p.Id, - dependsOn = p.DependsOn != null?(from d in p.DependsOn.Distinct(new PatchComparer()) - select d.Id):null - } + select new + { + id = p.Id, + dependsOn = p.DependsOn != null + ? (from d in p.DependsOn.Distinct(new PatchComparer()) + select d.Id) + : null + } }); } _io.File.WriteAllText(_filePath, data.ToString()); } } -} +} \ No newline at end of file diff --git a/src/Ormico.DbPatchManager.Logic/DatabaseBuildConfiguration.cs b/src/Ormico.DbPatchManager.Logic/DatabaseBuildConfiguration.cs index 46482bf..1f4cfc2 100644 --- a/src/Ormico.DbPatchManager.Logic/DatabaseBuildConfiguration.cs +++ b/src/Ormico.DbPatchManager.Logic/DatabaseBuildConfiguration.cs @@ -12,6 +12,7 @@ public DatabaseBuildConfiguration() { //defaults PatchFolder = "Patches"; + Options = new Dictionary(); CodeFolder = "Code"; patches = new List(); CodeFiles = new List() diff --git a/src/Ormico.DbPatchManager.Logic/IBuildConfigurationWriter.cs b/src/Ormico.DbPatchManager.Logic/IBuildConfigurationWriter.cs new file mode 100644 index 0000000..e690789 --- /dev/null +++ b/src/Ormico.DbPatchManager.Logic/IBuildConfigurationWriter.cs @@ -0,0 +1,13 @@ +namespace Ormico.DbPatchManager.Logic +{ + public interface IBuildConfigurationWriter + { + /// + /// Read DatabaseBuildConfiguration data from file path passed to constructor. + /// + /// + DatabaseBuildConfiguration Read(); + + void Write(DatabaseBuildConfiguration buildConfiguration); + } +} \ No newline at end of file diff --git a/src/Ormico.DbPatchManager.Logic/NewBuildConfigurationWriter.cs b/src/Ormico.DbPatchManager.Logic/NewBuildConfigurationWriter.cs new file mode 100644 index 0000000..d2e7e9b --- /dev/null +++ b/src/Ormico.DbPatchManager.Logic/NewBuildConfigurationWriter.cs @@ -0,0 +1,223 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Abstractions; +using System.Linq; +using Ormico.DbPatchManager.Common; + + +namespace Ormico.DbPatchManager.Logic +{ + /// + /// Read and Write DatabaseBuildConfiguration to storage. + /// + public class NewBuildConfigurationWriter : IBuildConfigurationWriter + { + private readonly Random _rand; + public NewBuildConfigurationWriter(string filePath, string localFilePath) + { + _filePath = filePath; + _localFilePath = localFilePath; + _rand = new Random(); + } + + /// + /// Use System.IO.Abstraction to make testing easier. + /// + FileSystem _io = new FileSystem(); + + /// + /// Path and name of file to read and write. + /// + string _filePath; + + /// + /// Path and name of secondary file to read and write. + /// The local file is intended to contains settings which the user does not want to + /// place in the main file. For example, a user may not wish to put the connection string + /// in the main file which is checked into source control. The connection string could + /// be placed in he local file which is not checked into source control. + /// + string _localFilePath; + + /// + /// Read DatabaseBuildConfiguration data from file path passed to constructor. + /// + /// + public DatabaseBuildConfiguration Read() + { + var rc = new DatabaseBuildConfiguration(); + if (_io.File.Exists(_filePath)) + { + var localPatchFileContents = new PatchFile(); + if (File.Exists(_localFilePath)) + { + localPatchFileContents = PatchFile.FromJson(_io.File.ReadAllText(_localFilePath)); + } + + // var primaryPatchFileStream = File.OpenText(_filePath); + var patchFileContents = PatchFile.FromJson(_io.File.ReadAllText(_filePath)); + rc.CodeFiles = localPatchFileContents.CodeFiles.Count > 0 + ? localPatchFileContents.CodeFiles + : (patchFileContents.CodeFiles ?? new List()); + rc.Options = localPatchFileContents.Options.Count > 0 + ? localPatchFileContents.Options + : (patchFileContents.Options ?? new Dictionary()); + rc.CodeFolder = localPatchFileContents.CodeFolder ?? patchFileContents.CodeFolder; + rc.DatabaseType = localPatchFileContents.DatabaseType ?? patchFileContents.DatabaseType; + rc.PatchFolder = localPatchFileContents.PatchFolder ?? patchFileContents.PatchFolder; + rc.ConnectionString = localPatchFileContents.ConnectionString ?? patchFileContents.ConnectionString; + + //Merge local patches + + foreach (var localPatch in localPatchFileContents.Patches) + { + patchFileContents.Patches.RemoveAll(p => p.Id == localPatch.Id); + patchFileContents.Patches.Add(localPatch); + } + + var distinctPatchesFromFile = patchFileContents.Patches.Distinct().ToList(); + var patches = distinctPatchesFromFile.Select(pff => new Patch(pff.Id, new List())).ToList(); + + var dependsOn = new Dictionary>(); + + foreach (var patchFromFile in distinctPatchesFromFile) + { + var dependents = patchFromFile.DependsOn.Select(dependent => patches.Find(p => p.Id == dependent)) + .ToList(); + if (!dependsOn.ContainsKey(patchFromFile.Id)) + { + dependsOn.Add(patchFromFile.Id, dependents); + } + } + + foreach (var patchToUpdate in patches) + { + patchToUpdate.DependsOn = dependsOn[patchToUpdate.Id]; + } + + var children = new Dictionary>(); + // 1. Loop thru all of the patches. Named childPatch here in order to maintain + // logical consistency with it's parents (ie. contents of it's [DependsOn] prop + foreach (var childPatch in patches) + { + // 2. Loop thur "child's" parents + foreach (var parentPatch in childPatch.DependsOn) + { + // 3a. If dictionary already contains an entry for the parent + // then add this child if necessary + if (children.ContainsKey(parentPatch.Id)) + { + if (!children[parentPatch.Id].Contains(childPatch)) + { + children[parentPatch.Id].Add(childPatch); + } + } + // 3b. otherwise add an entry for this parent and current child + else + { + children.Add(parentPatch.Id, new List() + { + childPatch + }); + } + } + } + + // Now look thru all the patches again and update the Children property as needed + foreach (var patchToUpdate in patches) + { + patchToUpdate.Children = new List(); + if (children.ContainsKey(patchToUpdate.Id) && children[patchToUpdate.Id].Any()) + { + patchToUpdate.Children.AddRange(children[patchToUpdate.Id]); + }; + } + + rc.patches = patches; + } + else + { + throw new ApplicationException("Configuration file does not exist. Call init first."); + } + + return rc; + } + + public void Write(DatabaseBuildConfiguration buildConfiguration) + { + var patchFileContent = new PatchFile(); + var localPatchFileContents = new PatchFile(); + if (_io.File.Exists(_localFilePath)) + { + localPatchFileContents = PatchFile.FromJson(_io.File.ReadAllText(_localFilePath)); + } + + patchFileContent.CodeFiles = + localPatchFileContents.CodeFiles.Count == 0 && buildConfiguration.CodeFiles?.Count > 0 + ? (buildConfiguration.CodeFiles ?? new List()) + : new List(); + patchFileContent.Options = localPatchFileContents.Options.Count == 0 && buildConfiguration.Options?.Count > 0 + ? (buildConfiguration.Options ?? new Dictionary()) + : new Dictionary(); + patchFileContent.CodeFolder = + string.IsNullOrWhiteSpace(localPatchFileContents.CodeFolder) && + !string.IsNullOrWhiteSpace(buildConfiguration.CodeFolder) + ? buildConfiguration.CodeFolder + : null; + patchFileContent.DatabaseType = + string.IsNullOrWhiteSpace(localPatchFileContents.DatabaseType) && + !string.IsNullOrWhiteSpace(buildConfiguration.DatabaseType) + ? buildConfiguration.DatabaseType + : null; + patchFileContent.PatchFolder = + string.IsNullOrWhiteSpace(localPatchFileContents.PatchFolder) && + !string.IsNullOrWhiteSpace(buildConfiguration.PatchFolder) + ? buildConfiguration.PatchFolder + : null; + patchFileContent.ConnectionString = + string.IsNullOrWhiteSpace(localPatchFileContents.ConnectionString) && + !string.IsNullOrWhiteSpace(buildConfiguration.ConnectionString) + ? buildConfiguration.ConnectionString + : null; + + // Attempt to move the last patch to Random spot 2 to 10 patches from bottom + // This is to try and help git merge conflicts. + var patchCount = buildConfiguration.patches.Count; + if (patchCount > 10) + { + // We only want to move the patch if there are more than 10 patches. This number is + // arbitrary but 10 should be enough + var mergeTargetIndex = _rand.Next(patchCount - 10, patchCount - 2); + var lastPatch = buildConfiguration.patches.Last(); + var lastPatchId = lastPatch.Id; + var lastPatchDependsOn = lastPatch.DependsOn; + var lastPatchChildren = lastPatch.Children; + + buildConfiguration.patches.Remove(lastPatch); + buildConfiguration.patches.Insert(mergeTargetIndex, new Patch() + { + Id = lastPatchId, + Children = lastPatchChildren, + DependsOn = lastPatchDependsOn + }); + + } + foreach (var buildConfigurationPatch in buildConfiguration.patches) + { + var pff = new PatchFromFile() + { + Id = buildConfigurationPatch.Id, + }; + foreach (var dependant in buildConfigurationPatch.DependsOn) + { + pff.DependsOn.Add(dependant.Id); + } + + patchFileContent.Patches.Add(pff); + } + + _io.File.WriteAllText(_filePath, patchFileContent.ToJson()); + } + } +} \ No newline at end of file diff --git a/src/Ormico.DbPatchManager.Logic/OdbcDatabase.cs b/src/Ormico.DbPatchManager.Logic/OdbcDatabase.cs index eeb6464..8efc384 100644 --- a/src/Ormico.DbPatchManager.Logic/OdbcDatabase.cs +++ b/src/Ormico.DbPatchManager.Logic/OdbcDatabase.cs @@ -4,8 +4,6 @@ using System.Collections.Generic; using System.Data.Odbc; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Ormico.DbPatchManager.Logic { diff --git a/src/Ormico.DbPatchManager.Logic/Ormico.DbPatchManager.Logic.csproj b/src/Ormico.DbPatchManager.Logic/Ormico.DbPatchManager.Logic.csproj index 1bb2ffa..13b3e0f 100644 --- a/src/Ormico.DbPatchManager.Logic/Ormico.DbPatchManager.Logic.csproj +++ b/src/Ormico.DbPatchManager.Logic/Ormico.DbPatchManager.Logic.csproj @@ -1,7 +1,6 @@  - netstandard2.0 false true Copyright (c) 2020 Zack Moore @@ -15,10 +14,11 @@ dbpatch-manager-profile.png Logic Library for Database Change managment designed for multi-dev/multi-branch. 2.1.2.0 + net8.0;netstandard2.1 - + diff --git a/src/Ormico.DbPatchManager.Logic/PatchManager.cs b/src/Ormico.DbPatchManager.Logic/PatchManager.cs index 783c39b..d8e8b24 100644 --- a/src/Ormico.DbPatchManager.Logic/PatchManager.cs +++ b/src/Ormico.DbPatchManager.Logic/PatchManager.cs @@ -14,6 +14,9 @@ public PatchManager(string ConfigFile, string LocalConfigFile) { _configFileName = ConfigFile; _configLocalFileName = LocalConfigFile; + _pm = new PluginManager(); + // _buildConfigWriter = "OriginalBuildConfig"; + _buildConfigWriter = "NewBuildConfig"; _rand = new Random(); _io = new FileSystem(); } @@ -36,12 +39,14 @@ public PatchManager(string ConfigFile, Random Rand, FileSystem Io) DatabaseBuildConfiguration _configuration; string ScriptOverridesFolder = @"ScriptOverrides"; //todo: not sure if making these .sql is best since not all databases are sql + private readonly PluginManager _pm; string AddInstalledPatchFileName = "AddInstalledPatch.sql"; string GetInstalledPatchesFileName = "GetInstalledPatches.sql"; string InitPatchTableFileName = "InitPatchTable.sql"; readonly string _configFileName; readonly string _configLocalFileName; + private readonly string _buildConfigWriter; readonly Random _rand; /// @@ -51,7 +56,8 @@ public PatchManager(string ConfigFile, Random Rand, FileSystem Io) public void InitConfig(InitOptions Options) { - var cfgWriter = new BuildConfigurationWriter(_configFileName, _configLocalFileName); + + var cfgWriter = _pm.LoadBuildConfigurationWriterPlugin(_buildConfigWriter, _configFileName, _configLocalFileName); DatabaseBuildConfiguration databaseBuildConfiguration = new DatabaseBuildConfiguration() { DatabaseType = Options?.DbType, @@ -81,7 +87,7 @@ public void AddPatch(string patchName, PatchOptions Options = null) } else { - var cfgWriter = new BuildConfigurationWriter(_configFileName, _configLocalFileName); + var cfgWriter = _pm.LoadBuildConfigurationWriterPlugin(_buildConfigWriter, _configFileName, _configLocalFileName); var cfg = cfgWriter.Read(); // load options @@ -106,6 +112,7 @@ public void AddPatch(string patchName, PatchOptions Options = null) var openPatches = cfg.GetOpenPatches(); cfg.patches.Add(new Patch(finalId, openPatches)); cfgWriter.Write(cfg); + Console.WriteLine("Your patch folder: {0}", patchPath); } else { @@ -117,7 +124,7 @@ public void AddPatch(string patchName, PatchOptions Options = null) public void Build() { - var cfgWriter = new BuildConfigurationWriter(_configFileName, _configLocalFileName); + var cfgWriter = _pm.LoadBuildConfigurationWriterPlugin(_buildConfigWriter, _configFileName, _configLocalFileName); _configuration = cfgWriter.Read(); // load options @@ -128,9 +135,7 @@ public void Build() var first = _configuration.GetFirstPatch(); if(first != null) { - PluginManager pm = new PluginManager(); - - using (var db = pm.LoadDatabasePlugin(_configuration.DatabaseType)) + using (var db = _pm.LoadDatabasePlugin(_configuration.DatabaseType)) { db.Connect(dbopt); var installedPatches = db.GetInstalledPatches(); @@ -270,7 +275,7 @@ private void InstallPatch(Patch patch, IDatabase db, List in } // add children of current - foreach (var c in current.Children) + foreach (var c in (current.Children ?? new List())) { if (graph.Any(i => string.Equals(i.Id, c.Id)) == false) { diff --git a/src/Ormico.DbPatchManager.Logic/PluginManager.cs b/src/Ormico.DbPatchManager.Logic/PluginManager.cs index 079734e..cfc6ae1 100644 --- a/src/Ormico.DbPatchManager.Logic/PluginManager.cs +++ b/src/Ormico.DbPatchManager.Logic/PluginManager.cs @@ -13,9 +13,9 @@ class PluginManager public IDatabase LoadDatabasePlugin(string PluginType) { IDatabase rc = null; - if(string.Equals(PluginType, "TestDatabase", StringComparison.OrdinalIgnoreCase) || - string.Equals(PluginType, "test", StringComparison.OrdinalIgnoreCase) || - string.Equals(PluginType, typeof(TestDatabase).ToString(), StringComparison.OrdinalIgnoreCase)) + if (string.Equals(PluginType, "TestDatabase", StringComparison.OrdinalIgnoreCase) || + string.Equals(PluginType, "test", StringComparison.OrdinalIgnoreCase) || + string.Equals(PluginType, typeof(TestDatabase).ToString(), StringComparison.OrdinalIgnoreCase)) { rc = new TestDatabase(); } @@ -27,7 +27,8 @@ public IDatabase LoadDatabasePlugin(string PluginType) } else if (string.Equals(PluginType, "SqlDatabase", StringComparison.OrdinalIgnoreCase) || string.Equals(PluginType, "sqlserver", StringComparison.OrdinalIgnoreCase) || - string.Equals(PluginType, typeof(SqlServer.SqlDatabase).ToString(), StringComparison.OrdinalIgnoreCase)) + string.Equals(PluginType, typeof(SqlServer.SqlDatabase).ToString(), + StringComparison.OrdinalIgnoreCase)) { rc = new SqlServer.SqlDatabase(); } @@ -35,10 +36,10 @@ public IDatabase LoadDatabasePlugin(string PluginType) { string[] parts = PluginType.Split(','); string fileName, typeName; - if(parts != null && parts.Length > 0) + if (parts != null && parts.Length > 0) { fileName = parts[0]; - if(parts.Length > 1) + if (parts.Length > 1) { typeName = parts[1]; //todo: what to do if null? @@ -51,12 +52,52 @@ public IDatabase LoadDatabasePlugin(string PluginType) { rc = pa.CreateInstance(typeName) as IDatabase; } - } } } return rc; } + + public IBuildConfigurationWriter LoadBuildConfigurationWriterPlugin(string pluginType, string filePath, + string localFilePath) + { + IBuildConfigurationWriter rc = null; + + if (string.Equals(pluginType, "NewBuildConfig", StringComparison.OrdinalIgnoreCase) || + string.Equals(pluginType, "new", StringComparison.OrdinalIgnoreCase) || + string.Equals(pluginType, typeof(NewBuildConfigurationWriter).ToString(), + StringComparison.OrdinalIgnoreCase)) + { + rc = new NewBuildConfigurationWriter(filePath, localFilePath); + } + else if (string.Equals(pluginType, "OriginalBuildConfig", StringComparison.OrdinalIgnoreCase) || + string.Equals(pluginType, "og", StringComparison.OrdinalIgnoreCase) || + string.Equals(pluginType, typeof(BuildConfigurationWriter).ToString(), + StringComparison.OrdinalIgnoreCase)) + + { + rc = new BuildConfigurationWriter(filePath, localFilePath); + } + else + { + var parts = pluginType.Split(','); + if (parts.Length <= 1) return null; + var fileName = parts[0]; + var typeName = parts[1]; + //todo: what to do if null? + + // LoadFile() or LoadFrom() + var pa = Assembly.LoadFrom(fileName); + var t = pa.GetType(typeName); + var interfaceName = typeof(IBuildConfigurationWriter).ToString(); + if (t.IsPublic && t.IsClass && t.GetInterface(interfaceName) != null) + { + rc = pa.CreateInstance(typeName) as IBuildConfigurationWriter; + } + } + + return rc; + } } -} +} \ No newline at end of file diff --git a/src/Ormico.DbPatchManager.sln b/src/Ormico.DbPatchManager.sln index 62c3e3c..2094db2 100644 --- a/src/Ormico.DbPatchManager.sln +++ b/src/Ormico.DbPatchManager.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28803.352 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35013.160 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ormico.DbPatchManager.CLI", "Ormico.DbPatchManager.CLI\Ormico.DbPatchManager.CLI.csproj", "{97E6E000-7E95-4067-BA10-8E9D188C211A}" EndProject @@ -9,8 +9,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ormico.DbPatchManager.Logic EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ormico.DbPatchManager.Common", "Ormico.DbPatchManager.Common\Ormico.DbPatchManager.Common.csproj", "{EB7E7876-55E8-4B9D-82BA-387B90F363F6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ormico.DbPatchManager.Logic.Tests", "Ormico.DbPatchManager.Logic.Tests\Ormico.DbPatchManager.Logic.Tests.csproj", "{6ECAE8E2-1FC5-4BFF-9C45-24C0215E35B6}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -29,10 +27,6 @@ Global {EB7E7876-55E8-4B9D-82BA-387B90F363F6}.Debug|Any CPU.Build.0 = Debug|Any CPU {EB7E7876-55E8-4B9D-82BA-387B90F363F6}.Release|Any CPU.ActiveCfg = Release|Any CPU {EB7E7876-55E8-4B9D-82BA-387B90F363F6}.Release|Any CPU.Build.0 = Release|Any CPU - {6ECAE8E2-1FC5-4BFF-9C45-24C0215E35B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6ECAE8E2-1FC5-4BFF-9C45-24C0215E35B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6ECAE8E2-1FC5-4BFF-9C45-24C0215E35B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6ECAE8E2-1FC5-4BFF-9C45-24C0215E35B6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE