From cfcef813c3a3f02bae44d1dfb8cbe3ce1bd9f1df Mon Sep 17 00:00:00 2001 From: Hadi Eskandari Date: Tue, 16 Feb 2021 10:36:00 +1100 Subject: [PATCH] dotnet tool for script generation --- .../RuntimeSagaDefinitionReader.cs | 11 +- src/CommandLine/.editorconfig | 4 + src/CommandLine/DialectTypes.cs | 34 ++++ src/CommandLine/ExceptionExtensions.cs | 45 +++++ src/CommandLine/Generator.cs | 29 ++++ ...viceBus.Persistence.Sql.CommandLine.csproj | 20 +++ src/CommandLine/Program.cs | 61 +++++++ src/NServiceBus.Persistence.Sql.sln | 6 + .../APIApprovals.Approve.approved.txt | 19 ++- .../Saga/AllSagaDefinitionReader.cs | 18 +- src/ScriptBuilder/ScriptGenerator.cs | 157 ++++++++++++++++++ src/ScriptBuilder/ScriptWriter.cs | 96 ----------- src/ScriptBuilder/Writers/OutboxWriter.cs | 25 ++- src/ScriptBuilder/Writers/SagaWriter.cs | 47 +++--- src/ScriptBuilder/Writers/ScriptWriter.cs | 47 ++++++ .../Writers/SubscriptionWriter.cs | 26 ++- src/ScriptBuilder/Writers/TimeoutWriter.cs | 25 ++- .../AssemblyInfo.cs | 2 +- ...nnerTaskTests.IntegrationTest.approved.txt | 112 +++++++------ src/ScriptBuilderTask.Tests/InnerTaskTests.cs | 13 +- src/ScriptBuilderTask.Tests/PathExtensions.cs | 9 + src/ScriptBuilderTask/InnerTask.cs | 13 +- 22 files changed, 577 insertions(+), 242 deletions(-) create mode 100644 src/CommandLine/.editorconfig create mode 100644 src/CommandLine/DialectTypes.cs create mode 100644 src/CommandLine/ExceptionExtensions.cs create mode 100644 src/CommandLine/Generator.cs create mode 100644 src/CommandLine/NServiceBus.Persistence.Sql.CommandLine.csproj create mode 100644 src/CommandLine/Program.cs create mode 100644 src/ScriptBuilder/ScriptGenerator.cs delete mode 100644 src/ScriptBuilder/ScriptWriter.cs create mode 100644 src/ScriptBuilder/Writers/ScriptWriter.cs create mode 100644 src/ScriptBuilderTask.Tests/PathExtensions.cs diff --git a/src/AcceptanceTestHelper/RuntimeSagaDefinitionReader.cs b/src/AcceptanceTestHelper/RuntimeSagaDefinitionReader.cs index d5247424b..c62d2ce2e 100644 --- a/src/AcceptanceTestHelper/RuntimeSagaDefinitionReader.cs +++ b/src/AcceptanceTestHelper/RuntimeSagaDefinitionReader.cs @@ -21,20 +21,11 @@ public static IEnumerable GetSagaDefinitions(EndpointConfigurati return Enumerable.Empty(); } var sagaAssembly = sagaTypes.First().Assembly; - var exceptions = new List(); //Validate the saga definitions using script builder compile-time validation using (var moduleDefinition = ModuleDefinition.ReadModule(sagaAssembly.Location, new ReaderParameters(ReadingMode.Deferred))) { var compileTimeReader = new AllSagaDefinitionReader(moduleDefinition); - - compileTimeReader.GetSagas((e, d) => - { - exceptions.Add(e); - }); - } - if (exceptions.Any()) - { - throw new AggregateException(exceptions); + compileTimeReader.GetSagas(); } return sagaTypes.Select(sagaType => GetSagaDefinition(sagaType, sqlDialect)); } diff --git a/src/CommandLine/.editorconfig b/src/CommandLine/.editorconfig new file mode 100644 index 000000000..aff82c003 --- /dev/null +++ b/src/CommandLine/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# Justification: Application synchronization contexts don't require ConfigureAwait(false) +dotnet_diagnostic.CA2007.severity = none diff --git a/src/CommandLine/DialectTypes.cs b/src/CommandLine/DialectTypes.cs new file mode 100644 index 000000000..b020bf939 --- /dev/null +++ b/src/CommandLine/DialectTypes.cs @@ -0,0 +1,34 @@ +namespace NServiceBus.Persistence.Sql.CommandLine +{ + using System.Collections; + using System.Collections.Generic; + using System.Linq; + using ScriptBuilder; + + public enum DialectTypes + { + SqlServer, + MySql, + Oracle, + PostgreSql, + } + + public static class DialectTypesExtensions + { + static readonly Dictionary DialectMap = new Dictionary + { + [DialectTypes.Oracle] = BuildSqlDialect.Oracle, + [DialectTypes.MySql] = BuildSqlDialect.MySql, + [DialectTypes.MySql] = BuildSqlDialect.MySql, + [DialectTypes.PostgreSql] = BuildSqlDialect.PostgreSql, + [DialectTypes.SqlServer] = BuildSqlDialect.MsSqlServer, + }; + + public static BuildSqlDialect ToBuildSqlDialect(this DialectTypes dialect) => DialectMap[dialect]; + + public static IReadOnlyList ToBuildSqlDialects(this IReadOnlyList dialects) + { + return dialects?.Select(d => d.ToBuildSqlDialect()).ToList().AsReadOnly(); + } + } +} \ No newline at end of file diff --git a/src/CommandLine/ExceptionExtensions.cs b/src/CommandLine/ExceptionExtensions.cs new file mode 100644 index 000000000..85f1450da --- /dev/null +++ b/src/CommandLine/ExceptionExtensions.cs @@ -0,0 +1,45 @@ +using System; +using System.Text; + +static class ExceptionExtensions +{ + public static string ToFriendlyString(this Exception exception) + { + + var stringBuilder = new StringBuilder(); + stringBuilder.Append("Exception:"); + stringBuilder.Append(Environment.NewLine); + while (exception != null) + { + stringBuilder.Append(exception.Message); + stringBuilder.Append(Environment.NewLine); + + foreach (var i in exception.Data) + { + stringBuilder.Append("Data :"); + stringBuilder.Append(i); + stringBuilder.Append(Environment.NewLine); + } + + if (exception.StackTrace != null) + { + stringBuilder.Append("StackTrace:"); + stringBuilder.Append(Environment.NewLine); + stringBuilder.Append(exception.StackTrace); + stringBuilder.Append(Environment.NewLine); + } + + if (exception.Source != null) + { + stringBuilder.Append("Source:"); + stringBuilder.Append(Environment.NewLine); + stringBuilder.Append(exception.Source); + stringBuilder.Append(Environment.NewLine); + } + + exception = exception.InnerException; + } + + return stringBuilder.ToString(); + } +} \ No newline at end of file diff --git a/src/CommandLine/Generator.cs b/src/CommandLine/Generator.cs new file mode 100644 index 000000000..7889dbee4 --- /dev/null +++ b/src/CommandLine/Generator.cs @@ -0,0 +1,29 @@ +namespace NServiceBus.Persistence.Sql.CommandLine +{ + using System; + using System.IO; + using System.Threading.Tasks; + using McMaster.Extensions.CommandLineUtils; + + static class Generator + { + public static Task Run( + CommandArgument assemblyPath, + CommandOption outputPath, + CommandOption overwriteOption, + CommandOption cleanOption, + CommandOption dialectOption) + { + var output = outputPath.HasValue() ? outputPath.Value() : Directory.GetCurrentDirectory(); + + var writer = new ScriptGenerator(assemblyPath.Value, output, + cleanOption.ParsedValue, overwriteOption.ParsedValue, + dialectOption.ParsedValues.ToBuildSqlDialects()); + + writer.Generate(); + + Console.WriteLine($"Script for {assemblyPath.Value} was generated at {output}."); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/src/CommandLine/NServiceBus.Persistence.Sql.CommandLine.csproj b/src/CommandLine/NServiceBus.Persistence.Sql.CommandLine.csproj new file mode 100644 index 000000000..72a67dc9b --- /dev/null +++ b/src/CommandLine/NServiceBus.Persistence.Sql.CommandLine.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp3.1 + Exe + sql-persistence + True + .NET Core global tool to generate scripts for NServiceBus Sql persistence + false + + + + + + + + + + + diff --git a/src/CommandLine/Program.cs b/src/CommandLine/Program.cs new file mode 100644 index 000000000..ca273e501 --- /dev/null +++ b/src/CommandLine/Program.cs @@ -0,0 +1,61 @@ +namespace NServiceBus.Persistence.Sql.CommandLine +{ + using System; + using System.Collections.Generic; + using McMaster.Extensions.CommandLineUtils; + using ScriptBuilder; + + class Program + { + internal const string AppName = "sql-persistence"; + + static int Main(string[] args) + { + var app = new CommandLineApplication + { + Name = AppName + }; + + var verboseOption = app.Option("-v | --verbose", "Verbose logging", CommandOptionType.NoValue, inherited: true); + + app.HelpOption(inherited: true); + + app.Command("script", cmd => + { + var assemblyPath = cmd.Argument("assembly", "File path to the endpoint assembly.").IsRequired(); + var outputOption = cmd.Option("-o | --output-dir", "Path to the output directory.", CommandOptionType.SingleOrNoValue); + var cleanOption = cmd.Option("--clean", "Removes existing files in the output directory.", CommandOptionType.SingleOrNoValue); + var overwriteOption = cmd.Option("--overwrite", "Overwrites existing files in the output if they match the files to be generated.", CommandOptionType.SingleOrNoValue); + var dialectOption = cmd.Option("--dialect", "Specifies a dialect to generate", CommandOptionType.SingleOrNoValue); + + cmd.OnExecuteAsync(async ct => + { + await Generator.Run(assemblyPath, outputOption, overwriteOption, cleanOption, dialectOption); + }); + }); + + app.OnExecute(() => + { + Console.WriteLine("Specify a subcommand"); + app.ShowHelp(); + return 1; + }); + + try + { + return app.Execute(args); + } + catch (Exception exception) + { + var error = exception.ToFriendlyString(); + Console.Error.WriteLine($"{AppName} failed: {exception.Message}"); + + if (verboseOption.HasValue()) + { + Console.Error.WriteLine(error); + } + return 1; + } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.Persistence.Sql.sln b/src/NServiceBus.Persistence.Sql.sln index ca08842b3..397b8d9da 100644 --- a/src/NServiceBus.Persistence.Sql.sln +++ b/src/NServiceBus.Persistence.Sql.sln @@ -48,6 +48,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MsSqlMicrosoftDataClientSql EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MsSqlMicrosoftDataClientAcceptanceTests", "MsSqlMicrosoftDataClientAcceptanceTests\MsSqlMicrosoftDataClientAcceptanceTests.csproj", "{7C16BE00-7198-4B6C-A094-C0B62A539F77}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NServiceBus.Persistence.Sql.CommandLine", "CommandLine\NServiceBus.Persistence.Sql.CommandLine.csproj", "{DC682A5A-3ECD-4FB1-826E-A2BD988F40E3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -122,6 +124,10 @@ Global {7C16BE00-7198-4B6C-A094-C0B62A539F77}.Debug|Any CPU.Build.0 = Debug|Any CPU {7C16BE00-7198-4B6C-A094-C0B62A539F77}.Release|Any CPU.ActiveCfg = Release|Any CPU {7C16BE00-7198-4B6C-A094-C0B62A539F77}.Release|Any CPU.Build.0 = Release|Any CPU + {DC682A5A-3ECD-4FB1-826E-A2BD988F40E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DC682A5A-3ECD-4FB1-826E-A2BD988F40E3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DC682A5A-3ECD-4FB1-826E-A2BD988F40E3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DC682A5A-3ECD-4FB1-826E-A2BD988F40E3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/ScriptBuilder.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt b/src/ScriptBuilder.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt index d7281e194..30674653f 100644 --- a/src/ScriptBuilder.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt +++ b/src/ScriptBuilder.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt @@ -70,10 +70,6 @@ namespace NServiceBus.Persistence.Sql.ScriptBuilder public static void BuildDropScript(NServiceBus.Persistence.Sql.ScriptBuilder.SagaDefinition saga, NServiceBus.Persistence.Sql.ScriptBuilder.BuildSqlDialect sqlDialect, System.IO.TextWriter writer) { } public static string BuildDropScript(NServiceBus.Persistence.Sql.ScriptBuilder.SagaDefinition saga, NServiceBus.Persistence.Sql.ScriptBuilder.BuildSqlDialect sqlDialect) { } } - public class static ScriptWriter - { - public static void Write(string assemblyPath, string targetDirectory, System.Action logError, System.Func promotionPathFinder) { } - } public class static SubscriptionScriptBuilder { public static void BuildCreateScript(System.IO.TextWriter writer, NServiceBus.Persistence.Sql.ScriptBuilder.BuildSqlDialect sqlDialect) { } @@ -88,4 +84,19 @@ namespace NServiceBus.Persistence.Sql.ScriptBuilder public static void BuildDropScript(System.IO.TextWriter writer, NServiceBus.Persistence.Sql.ScriptBuilder.BuildSqlDialect sqlDialect) { } public static string BuildDropScript(NServiceBus.Persistence.Sql.ScriptBuilder.BuildSqlDialect sqlDialect) { } } +} +public class ScriptGenerator +{ + public ScriptGenerator(string assemblyPath, string destinationDirectory, bool clean = True, bool overwrite = True, System.Collections.Generic.IReadOnlyList dialectOptions = null, System.Func promotionFinder = null, System.Action logError = null) { } + public static void Generate(string assemblyPath, string targetDirectory, System.Action logError, System.Func promotionPathFinder) { } + public void Generate() { } +} +public abstract class ScriptWriter +{ + protected ScriptWriter(bool clean, bool overwrite, string scriptPath) { } + protected bool Clean { get; } + protected bool Overwrite { get; } + protected string ScriptPath { get; } + protected void WriteScript(string fileName, System.Action action) { } + public abstract void WriteScripts(NServiceBus.Persistence.Sql.ScriptBuilder.BuildSqlDialect dialect); } \ No newline at end of file diff --git a/src/ScriptBuilder/Saga/AllSagaDefinitionReader.cs b/src/ScriptBuilder/Saga/AllSagaDefinitionReader.cs index 873827474..d35d304f2 100644 --- a/src/ScriptBuilder/Saga/AllSagaDefinitionReader.cs +++ b/src/ScriptBuilder/Saga/AllSagaDefinitionReader.cs @@ -6,16 +6,18 @@ class AllSagaDefinitionReader { - ModuleDefinition module; + readonly ModuleDefinition module; public AllSagaDefinitionReader(ModuleDefinition module) { this.module = module; } - public IEnumerable GetSagas(Action logError) + public IList GetSagas(Action logger = null) { var sagas = new List(); + var errors = new List(); + foreach (var type in module.AllClasses()) { try @@ -27,12 +29,16 @@ public IEnumerable GetSagas(Action 0 && logger == null) + { + throw new AggregateException(errors); + } + return sagas; + } } \ No newline at end of file diff --git a/src/ScriptBuilder/ScriptGenerator.cs b/src/ScriptBuilder/ScriptGenerator.cs new file mode 100644 index 000000000..4d4cb584f --- /dev/null +++ b/src/ScriptBuilder/ScriptGenerator.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Mono.Cecil; +using NServiceBus.Persistence.Sql; +using NServiceBus.Persistence.Sql.ScriptBuilder; + +public class ScriptGenerator +{ + string assemblyPath; + string scriptBasePath; + bool overwrite; + bool clean; + Func promotionFinder; + Action logError; + IReadOnlyList dialectOptions; + + public ScriptGenerator(string assemblyPath, string destinationDirectory, + bool clean = true, bool overwrite = true, + IReadOnlyList dialectOptions = null, + Func promotionFinder = null, + Action logError = null) + { + this.assemblyPath = assemblyPath; + this.overwrite = overwrite; + this.clean = clean; + this.dialectOptions = dialectOptions; + this.logError = logError; + this.promotionFinder = promotionFinder; + this.scriptBasePath = Path.Combine(destinationDirectory, "NServiceBus.Persistence.Sql"); + } + + public static void Generate(string assemblyPath, string targetDirectory, + Action logError, Func promotionPathFinder) + { + var writer = new ScriptGenerator(assemblyPath, targetDirectory, promotionFinder: promotionPathFinder, logError: logError); + writer.Generate(); + } + + public void Generate() + { + if (clean) + { + PurgeDialectDirs(scriptBasePath); + } + + CreateDirectories(); + + Settings settings; + using (var module = ModuleDefinition.ReadModule(assemblyPath, new ReaderParameters(ReadingMode.Deferred))) + { + settings = SettingsAttributeReader.Read(module); + foreach (var dialect in settings.BuildDialects) + { + if (!ShouldGenerateDialect(dialect)) + { + continue; + } + + var dialectPath = Path.Combine(scriptBasePath, dialect.ToString()); + + CreateDialectDirectory(dialectPath); + + if (settings.ProduceSagaScripts) + { + new SagaWriter(clean, overwrite, dialectPath, module, logError).WriteScripts(dialect); + } + + if (settings.ProduceTimeoutScripts) + { + new TimeoutWriter(clean, overwrite, dialectPath).WriteScripts(dialect); + } + + if (settings.ProduceSubscriptionScripts) + { + new SubscriptionWriter(clean, overwrite, dialectPath).WriteScripts(dialect); + } + + if (settings.ProduceOutboxScripts) + { + new OutboxWriter(clean, overwrite, dialectPath).WriteScripts(dialect); + } + } + } + + var scriptPromotionPath = settings.ScriptPromotionPath; + if (scriptPromotionPath == null || promotionFinder == null) + { + return; + } + var replicationPath = promotionFinder(scriptPromotionPath); + Promote(replicationPath); + } + + bool ShouldGenerateDialect(BuildSqlDialect dialect) + { + return dialectOptions == null || (dialectOptions.Count > 0 && dialectOptions.Contains(dialect)); + } + + void CreateDialectDirectory(string dialectPath) => Directory.CreateDirectory(dialectPath); + + void CreateDirectories() => Directory.CreateDirectory(scriptBasePath); + + void PurgeDialectDirs(string scriptPath) + { + foreach (var dialect in GetSelectedDialects()) + { + var dialectDirectory = Path.Combine(scriptPath, dialect); + if (!Directory.Exists(dialectDirectory)) + { + continue; + } + foreach (var file in GetKnownScripts(dialectDirectory)) + { + File.Delete(file); + } + var sagaDirectory = Path.Combine(dialectDirectory, "Sagas"); + if (!Directory.Exists(sagaDirectory)) + { + continue; + } + foreach (var file in GetKnownScripts(sagaDirectory)) + { + File.Delete(file); + } + } + } + + IEnumerable GetSelectedDialects() + { + return dialectOptions != null ? dialectOptions.Select(d => d.ToString()) : Enum.GetNames(typeof(BuildSqlDialect)); + } + + static IEnumerable GetKnownScripts(string dialectDirectory) + { + return Directory.EnumerateFiles(dialectDirectory, "*_Drop.sql") + .Concat(Directory.EnumerateFiles(dialectDirectory, "*_Create.sql")); + } + + void Promote(string replicationPath) + { + if (clean) + { + PurgeDialectDirs(replicationPath); + } + + try + { + DirectoryExtensions.DuplicateDirectory(scriptBasePath, replicationPath); + } + catch (Exception exception) + { + throw new ErrorsException($"Failed to promote scripts to '{replicationPath}'. Error: {exception.Message}"); + } + } +} \ No newline at end of file diff --git a/src/ScriptBuilder/ScriptWriter.cs b/src/ScriptBuilder/ScriptWriter.cs deleted file mode 100644 index 109422939..000000000 --- a/src/ScriptBuilder/ScriptWriter.cs +++ /dev/null @@ -1,96 +0,0 @@ -namespace NServiceBus.Persistence.Sql.ScriptBuilder -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using Mono.Cecil; - - public static class ScriptWriter - { - public static void Write(string assemblyPath, string targetDirectory, Action logError, Func promotionPathFinder) - { - var scriptPath = Path.Combine(targetDirectory, "NServiceBus.Persistence.Sql"); - PurgeDialectDirs(scriptPath); - Directory.CreateDirectory(scriptPath); - Settings settings; - using (var module = ModuleDefinition.ReadModule(assemblyPath, new ReaderParameters(ReadingMode.Deferred))) - { - settings = SettingsAttributeReader.Read(module); - foreach (var dialect in settings.BuildDialects) - { - var dialectPath = Path.Combine(scriptPath, dialect.ToString()); - Directory.CreateDirectory(dialectPath); - if (settings.ProduceSagaScripts) - { - SagaWriter.WriteSagaScripts(dialectPath, module, dialect, logError); - } - if (settings.ProduceTimeoutScripts) - { - TimeoutWriter.WriteTimeoutScript(dialectPath, dialect); - } - if (settings.ProduceSubscriptionScripts) - { - SubscriptionWriter.WriteSubscriptionScript(dialectPath, dialect); - } - if (settings.ProduceOutboxScripts) - { - OutboxWriter.WriteOutboxScript(dialectPath, dialect); - } - } - } - - var scriptPromotionPath = settings.ScriptPromotionPath; - if (scriptPromotionPath == null) - { - return; - } - var replicationPath = promotionPathFinder(scriptPromotionPath); - Promote(replicationPath, scriptPath); - } - - static void PurgeDialectDirs(string scriptPath) - { - foreach (var dialect in Enum.GetNames(typeof(BuildSqlDialect))) - { - var dialectDirectory = Path.Combine(scriptPath, dialect); - if (!Directory.Exists(dialectDirectory)) - { - continue; - } - foreach (var file in GetKnownScripts(dialectDirectory)) - { - File.Delete(file); - } - var sagaDirectory = Path.Combine(dialectDirectory, "Sagas"); - if (!Directory.Exists(sagaDirectory)) - { - continue; - } - foreach (var file in GetKnownScripts(sagaDirectory)) - { - File.Delete(file); - } - } - } - - static IEnumerable GetKnownScripts(string dialectDirectory) - { - return Directory.EnumerateFiles(dialectDirectory, "*_Drop.sql") - .Concat(Directory.EnumerateFiles(dialectDirectory, "*_Create.sql")); - } - - static void Promote(string replicationPath, string scriptPath) - { - try - { - PurgeDialectDirs(replicationPath); - DirectoryExtensions.DuplicateDirectory(scriptPath, replicationPath); - } - catch (Exception exception) - { - throw new ErrorsException($"Failed to promote scripts to '{replicationPath}'. Error: {exception.Message}"); - } - } - } -} \ No newline at end of file diff --git a/src/ScriptBuilder/Writers/OutboxWriter.cs b/src/ScriptBuilder/Writers/OutboxWriter.cs index af7b99e11..2b0be0aa4 100644 --- a/src/ScriptBuilder/Writers/OutboxWriter.cs +++ b/src/ScriptBuilder/Writers/OutboxWriter.cs @@ -1,21 +1,16 @@ -using System.IO; using NServiceBus.Persistence.Sql.ScriptBuilder; -class OutboxWriter +class OutboxWriter : ScriptWriter { - public static void WriteOutboxScript(string scriptPath, BuildSqlDialect sqlDialect) + public OutboxWriter(bool clean, bool overwrite, string scriptPath) + : base(clean, overwrite, scriptPath) { - var createPath = Path.Combine(scriptPath, "Outbox_Create.sql"); - File.Delete(createPath); - using (var writer = File.CreateText(createPath)) - { - OutboxScriptBuilder.BuildCreateScript(writer, sqlDialect); - } - var dropPath = Path.Combine(scriptPath, "Outbox_Drop.sql"); - File.Delete(dropPath); - using (var writer = File.CreateText(dropPath)) - { - OutboxScriptBuilder.BuildDropScript(writer, sqlDialect); - } + } + + public override void WriteScripts(BuildSqlDialect dialect) + { + WriteScript("Outbox_Create.sql", writer => OutboxScriptBuilder.BuildCreateScript(writer, dialect)); + + WriteScript("Outbox_Drop.sql", writer => OutboxScriptBuilder.BuildDropScript(writer, dialect)); } } \ No newline at end of file diff --git a/src/ScriptBuilder/Writers/SagaWriter.cs b/src/ScriptBuilder/Writers/SagaWriter.cs index 7131e4896..dcbfa2cf8 100644 --- a/src/ScriptBuilder/Writers/SagaWriter.cs +++ b/src/ScriptBuilder/Writers/SagaWriter.cs @@ -3,40 +3,43 @@ using Mono.Cecil; using NServiceBus.Persistence.Sql.ScriptBuilder; -class SagaWriter +class SagaWriter : ScriptWriter { - public static void WriteSagaScripts(string scriptPath, ModuleDefinition moduleDefinition, BuildSqlDialect sqlDialect, Action logError) + ModuleDefinition moduleDefinition; + Action logError; + string sagaPath; + + public SagaWriter(bool clean, bool overwrite, string scriptPath, + ModuleDefinition moduleDefinition, Action logError = null) + : base(clean, overwrite, scriptPath) + { + this.moduleDefinition = moduleDefinition; + this.logError = logError; + this.sagaPath = Path.Combine(scriptPath, "Sagas"); + } + + public override void WriteScripts(BuildSqlDialect dialect) { + Directory.CreateDirectory(sagaPath); + var metaDataReader = new AllSagaDefinitionReader(moduleDefinition); - var sagasScriptPath = Path.Combine(scriptPath, "Sagas"); - Directory.CreateDirectory(sagasScriptPath); + var index = 0; - foreach (var saga in metaDataReader.GetSagas( - logError: (exception, type) => - { - logError($"Error in '{type.FullName}'. Error:{exception.Message}", type.GetFileName()); - })) + foreach (var saga in metaDataReader.GetSagas(logError)) { var sagaFileName = saga.TableSuffix; - var maximumNameLength = 244 - sagasScriptPath.Length; + var maximumNameLength = 244 - ScriptPath.Length; if (sagaFileName.Length > maximumNameLength) { sagaFileName = $"{sagaFileName.Substring(0, maximumNameLength)}_{index}"; index++; } - var createPath = Path.Combine(sagasScriptPath, $"{sagaFileName}_Create.sql"); - File.Delete(createPath); - using (var writer = File.CreateText(createPath)) - { - SagaScriptBuilder.BuildCreateScript(saga, sqlDialect, writer); - } - var dropPath = Path.Combine(sagasScriptPath, $"{sagaFileName}_Drop.sql"); - File.Delete(dropPath); - using (var writer = File.CreateText(dropPath)) - { - SagaScriptBuilder.BuildDropScript(saga, sqlDialect, writer); - } + var createPath = Path.Combine(sagaPath, $"{sagaFileName}_Create.sql"); + WriteScript(createPath, writer => SagaScriptBuilder.BuildCreateScript(saga, dialect, writer)); + + var dropPath = Path.Combine(sagaPath, $"{sagaFileName}_Drop.sql"); + WriteScript(dropPath, writer => SagaScriptBuilder.BuildDropScript(saga, dialect, writer)); } } } \ No newline at end of file diff --git a/src/ScriptBuilder/Writers/ScriptWriter.cs b/src/ScriptBuilder/Writers/ScriptWriter.cs new file mode 100644 index 000000000..e4cc5c164 --- /dev/null +++ b/src/ScriptBuilder/Writers/ScriptWriter.cs @@ -0,0 +1,47 @@ +using System; +using System.IO; +using NServiceBus.Persistence.Sql.ScriptBuilder; + +public abstract class ScriptWriter +{ + protected ScriptWriter(bool clean, bool overwrite, string scriptPath) + { + Clean = clean; + Overwrite = overwrite; + ScriptPath = scriptPath; + } + + protected bool Clean { get; } + + protected bool Overwrite { get; } + + protected string ScriptPath { get; } + + public abstract void WriteScripts(BuildSqlDialect dialect); + + protected void WriteScript(string fileName, Action action) + { + var filePath = EnsureFile(fileName); + + using (var writer = File.CreateText(filePath)) + { + action(writer); + } + } + + string EnsureFile(string file) + { + var filePath = Path.Combine(ScriptPath, file); + if (Clean && File.Exists(file)) + { + File.Delete(filePath); + } + + if (!Overwrite && File.Exists(filePath)) + { + throw new Exception($"File '{filePath}' already exists."); + } + + return filePath; + } +} \ No newline at end of file diff --git a/src/ScriptBuilder/Writers/SubscriptionWriter.cs b/src/ScriptBuilder/Writers/SubscriptionWriter.cs index 88e2f9aa9..a456533d0 100644 --- a/src/ScriptBuilder/Writers/SubscriptionWriter.cs +++ b/src/ScriptBuilder/Writers/SubscriptionWriter.cs @@ -1,21 +1,15 @@ -using System.IO; -using NServiceBus.Persistence.Sql.ScriptBuilder; +using NServiceBus.Persistence.Sql.ScriptBuilder; -class SubscriptionWriter +class SubscriptionWriter : ScriptWriter { - public static void WriteSubscriptionScript(string scriptPath, BuildSqlDialect sqlDialect) + public SubscriptionWriter(bool clean, bool overwrite, string scriptPath) + : base(clean, overwrite, scriptPath) { - var createPath = Path.Combine(scriptPath, "Subscription_Create.sql"); - File.Delete(createPath); - using (var writer = File.CreateText(createPath)) - { - SubscriptionScriptBuilder.BuildCreateScript(writer, sqlDialect); - } - var dropPath = Path.Combine(scriptPath, "Subscription_Drop.sql"); - File.Delete(dropPath); - using (var writer = File.CreateText(dropPath)) - { - SubscriptionScriptBuilder.BuildDropScript(writer, sqlDialect); - } + } + + public override void WriteScripts(BuildSqlDialect dialect) + { + WriteScript("Subscription_Create.sql", writer => SubscriptionScriptBuilder.BuildCreateScript(writer, dialect)); + WriteScript("Subscription_Drop.sql", writer => SubscriptionScriptBuilder.BuildDropScript(writer, dialect)); } } \ No newline at end of file diff --git a/src/ScriptBuilder/Writers/TimeoutWriter.cs b/src/ScriptBuilder/Writers/TimeoutWriter.cs index e0e174fd2..1fa66ba1a 100644 --- a/src/ScriptBuilder/Writers/TimeoutWriter.cs +++ b/src/ScriptBuilder/Writers/TimeoutWriter.cs @@ -1,21 +1,16 @@ -using System.IO; using NServiceBus.Persistence.Sql.ScriptBuilder; -class TimeoutWriter +class TimeoutWriter : ScriptWriter { - public static void WriteTimeoutScript(string scriptPath, BuildSqlDialect sqlDialect) + public TimeoutWriter(bool clean, bool overwrite, string scriptPath) + : base(clean, overwrite, scriptPath) { - var createPath = Path.Combine(scriptPath, "Timeout_Create.sql"); - File.Delete(createPath); - using (var writer = File.CreateText(createPath)) - { - TimeoutScriptBuilder.BuildCreateScript(writer, sqlDialect); - } - var dropPath = Path.Combine(scriptPath, "Timeout_Drop.sql"); - File.Delete(dropPath); - using (var writer = File.CreateText(dropPath)) - { - TimeoutScriptBuilder.BuildDropScript(writer, sqlDialect); - } + } + + public override void WriteScripts(BuildSqlDialect dialect) + { + WriteScript("Timeout_Create.sql", writer => TimeoutScriptBuilder.BuildCreateScript(writer, dialect)); + + WriteScript("Timeout_Drop.sql", writer => TimeoutScriptBuilder.BuildDropScript(writer, dialect)); } } \ No newline at end of file diff --git a/src/ScriptBuilderTask.Tests.Target/AssemblyInfo.cs b/src/ScriptBuilderTask.Tests.Target/AssemblyInfo.cs index 6f7c0433e..8120bd624 100644 --- a/src/ScriptBuilderTask.Tests.Target/AssemblyInfo.cs +++ b/src/ScriptBuilderTask.Tests.Target/AssemblyInfo.cs @@ -6,4 +6,4 @@ MySqlScripts = true, PostgreSqlScripts = true, OracleScripts = true, - ScriptPromotionPath = "$(SolutionDir)$(ProjectDir)Postfix")] \ No newline at end of file + ScriptPromotionPath = "$(SolutionDir)$(ProjectDir)Postfix")] diff --git a/src/ScriptBuilderTask.Tests/ApprovalFiles/InnerTaskTests.IntegrationTest.approved.txt b/src/ScriptBuilderTask.Tests/ApprovalFiles/InnerTaskTests.IntegrationTest.approved.txt index aaf46baa1..eab00cf59 100644 --- a/src/ScriptBuilderTask.Tests/ApprovalFiles/InnerTaskTests.IntegrationTest.approved.txt +++ b/src/ScriptBuilderTask.Tests/ApprovalFiles/InnerTaskTests.IntegrationTest.approved.txt @@ -1,50 +1,66 @@ [ - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\MsSqlServer\\Outbox_Create.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\MsSqlServer\\Outbox_Drop.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\MsSqlServer\\Subscription_Create.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\MsSqlServer\\Subscription_Drop.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\MsSqlServer\\Timeout_Create.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\MsSqlServer\\Timeout_Drop.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\MsSqlServer\\Sagas\\OrderSaga_Create.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\MsSqlServer\\Sagas\\OrderSaga_Drop.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\MySql\\Outbox_Create.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\MySql\\Outbox_Drop.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\MySql\\Subscription_Create.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\MySql\\Subscription_Drop.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\MySql\\Timeout_Create.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\MySql\\Timeout_Drop.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\MySql\\Sagas\\OrderSaga_Create.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\MySql\\Sagas\\OrderSaga_Drop.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\Oracle\\Outbox_Create.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\Oracle\\Outbox_Drop.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\Oracle\\Subscription_Create.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\Oracle\\Subscription_Drop.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\Oracle\\Timeout_Create.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\Oracle\\Timeout_Drop.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\Oracle\\Sagas\\OrderSaga_Create.sql", - "temp\\IntermediatePath\\NServiceBus.Persistence.Sql\\Oracle\\Sagas\\OrderSaga_Drop.sql", - "temp\\PromotePathTheProjectDirPostfix\\MsSqlServer\\Outbox_Create.sql", - "temp\\PromotePathTheProjectDirPostfix\\MsSqlServer\\Outbox_Drop.sql", - "temp\\PromotePathTheProjectDirPostfix\\MsSqlServer\\Subscription_Create.sql", - "temp\\PromotePathTheProjectDirPostfix\\MsSqlServer\\Subscription_Drop.sql", - "temp\\PromotePathTheProjectDirPostfix\\MsSqlServer\\Timeout_Create.sql", - "temp\\PromotePathTheProjectDirPostfix\\MsSqlServer\\Timeout_Drop.sql", - "temp\\PromotePathTheProjectDirPostfix\\MsSqlServer\\Sagas\\OrderSaga_Create.sql", - "temp\\PromotePathTheProjectDirPostfix\\MsSqlServer\\Sagas\\OrderSaga_Drop.sql", - "temp\\PromotePathTheProjectDirPostfix\\MySql\\Outbox_Create.sql", - "temp\\PromotePathTheProjectDirPostfix\\MySql\\Outbox_Drop.sql", - "temp\\PromotePathTheProjectDirPostfix\\MySql\\Subscription_Create.sql", - "temp\\PromotePathTheProjectDirPostfix\\MySql\\Subscription_Drop.sql", - "temp\\PromotePathTheProjectDirPostfix\\MySql\\Timeout_Create.sql", - "temp\\PromotePathTheProjectDirPostfix\\MySql\\Timeout_Drop.sql", - "temp\\PromotePathTheProjectDirPostfix\\MySql\\Sagas\\OrderSaga_Create.sql", - "temp\\PromotePathTheProjectDirPostfix\\MySql\\Sagas\\OrderSaga_Drop.sql", - "temp\\PromotePathTheProjectDirPostfix\\Oracle\\Outbox_Create.sql", - "temp\\PromotePathTheProjectDirPostfix\\Oracle\\Outbox_Drop.sql", - "temp\\PromotePathTheProjectDirPostfix\\Oracle\\Subscription_Create.sql", - "temp\\PromotePathTheProjectDirPostfix\\Oracle\\Subscription_Drop.sql", - "temp\\PromotePathTheProjectDirPostfix\\Oracle\\Timeout_Create.sql", - "temp\\PromotePathTheProjectDirPostfix\\Oracle\\Timeout_Drop.sql", - "temp\\PromotePathTheProjectDirPostfix\\Oracle\\Sagas\\OrderSaga_Create.sql", - "temp\\PromotePathTheProjectDirPostfix\\Oracle\\Sagas\\OrderSaga_Drop.sql" + "temp/IntermediatePath/NServiceBus.Persistence.Sql/MsSqlServer/Outbox_Create.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/MsSqlServer/Outbox_Drop.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/MsSqlServer/Sagas/OrderSaga_Create.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/MsSqlServer/Sagas/OrderSaga_Drop.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/MsSqlServer/Subscription_Create.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/MsSqlServer/Subscription_Drop.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/MsSqlServer/Timeout_Create.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/MsSqlServer/Timeout_Drop.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/MySql/Outbox_Create.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/MySql/Outbox_Drop.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/MySql/Sagas/OrderSaga_Create.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/MySql/Sagas/OrderSaga_Drop.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/MySql/Subscription_Create.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/MySql/Subscription_Drop.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/MySql/Timeout_Create.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/MySql/Timeout_Drop.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/Oracle/Outbox_Create.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/Oracle/Outbox_Drop.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/Oracle/Sagas/OrderSaga_Create.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/Oracle/Sagas/OrderSaga_Drop.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/Oracle/Subscription_Create.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/Oracle/Subscription_Drop.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/Oracle/Timeout_Create.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/Oracle/Timeout_Drop.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/PostgreSql/Outbox_Create.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/PostgreSql/Outbox_Drop.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/PostgreSql/Sagas/OrderSaga_Create.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/PostgreSql/Sagas/OrderSaga_Drop.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/PostgreSql/Subscription_Create.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/PostgreSql/Subscription_Drop.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/PostgreSql/Timeout_Create.sql", + "temp/IntermediatePath/NServiceBus.Persistence.Sql/PostgreSql/Timeout_Drop.sql", + "temp/PromotePath/TheProjectDir/Postfix/MsSqlServer/Outbox_Create.sql", + "temp/PromotePath/TheProjectDir/Postfix/MsSqlServer/Outbox_Drop.sql", + "temp/PromotePath/TheProjectDir/Postfix/MsSqlServer/Sagas/OrderSaga_Create.sql", + "temp/PromotePath/TheProjectDir/Postfix/MsSqlServer/Sagas/OrderSaga_Drop.sql", + "temp/PromotePath/TheProjectDir/Postfix/MsSqlServer/Subscription_Create.sql", + "temp/PromotePath/TheProjectDir/Postfix/MsSqlServer/Subscription_Drop.sql", + "temp/PromotePath/TheProjectDir/Postfix/MsSqlServer/Timeout_Create.sql", + "temp/PromotePath/TheProjectDir/Postfix/MsSqlServer/Timeout_Drop.sql", + "temp/PromotePath/TheProjectDir/Postfix/MySql/Outbox_Create.sql", + "temp/PromotePath/TheProjectDir/Postfix/MySql/Outbox_Drop.sql", + "temp/PromotePath/TheProjectDir/Postfix/MySql/Sagas/OrderSaga_Create.sql", + "temp/PromotePath/TheProjectDir/Postfix/MySql/Sagas/OrderSaga_Drop.sql", + "temp/PromotePath/TheProjectDir/Postfix/MySql/Subscription_Create.sql", + "temp/PromotePath/TheProjectDir/Postfix/MySql/Subscription_Drop.sql", + "temp/PromotePath/TheProjectDir/Postfix/MySql/Timeout_Create.sql", + "temp/PromotePath/TheProjectDir/Postfix/MySql/Timeout_Drop.sql", + "temp/PromotePath/TheProjectDir/Postfix/Oracle/Outbox_Create.sql", + "temp/PromotePath/TheProjectDir/Postfix/Oracle/Outbox_Drop.sql", + "temp/PromotePath/TheProjectDir/Postfix/Oracle/Sagas/OrderSaga_Create.sql", + "temp/PromotePath/TheProjectDir/Postfix/Oracle/Sagas/OrderSaga_Drop.sql", + "temp/PromotePath/TheProjectDir/Postfix/Oracle/Subscription_Create.sql", + "temp/PromotePath/TheProjectDir/Postfix/Oracle/Subscription_Drop.sql", + "temp/PromotePath/TheProjectDir/Postfix/Oracle/Timeout_Create.sql", + "temp/PromotePath/TheProjectDir/Postfix/Oracle/Timeout_Drop.sql", + "temp/PromotePath/TheProjectDir/Postfix/PostgreSql/Outbox_Create.sql", + "temp/PromotePath/TheProjectDir/Postfix/PostgreSql/Outbox_Drop.sql", + "temp/PromotePath/TheProjectDir/Postfix/PostgreSql/Sagas/OrderSaga_Create.sql", + "temp/PromotePath/TheProjectDir/Postfix/PostgreSql/Sagas/OrderSaga_Drop.sql", + "temp/PromotePath/TheProjectDir/Postfix/PostgreSql/Subscription_Create.sql", + "temp/PromotePath/TheProjectDir/Postfix/PostgreSql/Subscription_Drop.sql", + "temp/PromotePath/TheProjectDir/Postfix/PostgreSql/Timeout_Create.sql", + "temp/PromotePath/TheProjectDir/Postfix/PostgreSql/Timeout_Drop.sql" ] \ No newline at end of file diff --git a/src/ScriptBuilderTask.Tests/InnerTaskTests.cs b/src/ScriptBuilderTask.Tests/InnerTaskTests.cs index c82576573..f74f68437 100644 --- a/src/ScriptBuilderTask.Tests/InnerTaskTests.cs +++ b/src/ScriptBuilderTask.Tests/InnerTaskTests.cs @@ -12,11 +12,11 @@ public void IntegrationTest() { var testDirectory = TestContext.CurrentContext.TestDirectory; var temp = Path.Combine(testDirectory, "InnerTaskTemp"); - if (!Directory.Exists(temp)) + if (Directory.Exists(temp)) { - return; + Directory.Delete(temp, true); } - Directory.Delete(temp, true); + var intermediatePath = Path.Combine(temp, "IntermediatePath"); Directory.CreateDirectory(temp); Directory.CreateDirectory(intermediatePath); @@ -26,9 +26,12 @@ public void IntegrationTest() intermediateDirectory: intermediatePath, projectDirectory: "TheProjectDir", solutionDirectory: Path.Combine(temp, "PromotePath"), - logError: (error, s1) => throw new Exception(error)); + logError: (error, type) => throw new Exception(error)); innerTask.Execute(); - var files = Directory.EnumerateFiles(temp, "*.*", SearchOption.AllDirectories).Select(s => s.Replace(temp, "temp")).ToList(); + var files = Directory.EnumerateFiles(temp, "*.*", SearchOption.AllDirectories) + .Select(s => s.Replace(temp, "temp").ConvertPathSeparators("/")) + .OrderBy(f => f) // Deterministic order + .ToList(); Assert.IsNotEmpty(files); Approver.Verify(files); diff --git a/src/ScriptBuilderTask.Tests/PathExtensions.cs b/src/ScriptBuilderTask.Tests/PathExtensions.cs new file mode 100644 index 000000000..f58d6bc4b --- /dev/null +++ b/src/ScriptBuilderTask.Tests/PathExtensions.cs @@ -0,0 +1,9 @@ +using System.IO; + +public static class PathExtensions +{ + public static string ConvertPathSeparators(this string path, string toChar) + { + return path?.Replace("\\", toChar); + } +} \ No newline at end of file diff --git a/src/ScriptBuilderTask/InnerTask.cs b/src/ScriptBuilderTask/InnerTask.cs index 8dd72cc7f..7310e0025 100644 --- a/src/ScriptBuilderTask/InnerTask.cs +++ b/src/ScriptBuilderTask/InnerTask.cs @@ -1,6 +1,6 @@ using System; +using System.IO; using NServiceBus.Persistence.Sql; -using NServiceBus.Persistence.Sql.ScriptBuilder; class InnerTask { @@ -21,13 +21,13 @@ public InnerTask(string assemblyPath, string intermediateDirectory, string proje public void Execute() { - ScriptWriter.Write(assemblyPath, intermediateDirectory, logError, FindPromotionPath); + ScriptGenerator.Generate(assemblyPath, intermediateDirectory, logError, FindPromotionPath); } string FindPromotionPath(string promotionPath) { promotionPath = promotionPath - .Replace("$(ProjectDir)", projectDirectory); + .Replace("$(ProjectDir)", GetFullPathWithEndingSlashes(projectDirectory)); if (!promotionPath.Contains("$(SolutionDir)")) { @@ -46,8 +46,13 @@ string FindPromotionPath(string promotionPath) } promotionPath = promotionPath - .Replace("$(SolutionDir)", solutionDirectory); + .Replace("$(SolutionDir)", GetFullPathWithEndingSlashes(solutionDirectory)); return promotionPath; } + + static string GetFullPathWithEndingSlashes(string input) + { + return input.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar; + } } \ No newline at end of file