diff --git a/util/Migrator/DbMigrator.cs b/util/Migrator/DbMigrator.cs index 2dca5006a..24e78aaee 100644 --- a/util/Migrator/DbMigrator.cs +++ b/util/Migrator/DbMigrator.cs @@ -2,6 +2,7 @@ using System.Reflection; using Bit.Core; using DbUp; +using DbUp.Helpers; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Logging; @@ -24,6 +25,8 @@ public class DbMigrator } public bool MigrateMsSqlDatabaseWithRetries(bool enableLogging = true, + bool repeatable = false, + string folderName = MigratorConstants.DefaultMigrationsFolderName, CancellationToken cancellationToken = default(CancellationToken)) { var attempt = 1; @@ -32,7 +35,7 @@ public class DbMigrator { try { - var success = MigrateDatabase(enableLogging, cancellationToken); + var success = MigrateDatabase(enableLogging, repeatable, folderName, cancellationToken); return success; } catch (SqlException ex) @@ -54,6 +57,8 @@ public class DbMigrator } public bool MigrateDatabase(bool enableLogging = true, + bool repeatable = false, + string folderName = MigratorConstants.DefaultMigrationsFolderName, CancellationToken cancellationToken = default(CancellationToken)) { if (_logger != null) @@ -98,12 +103,20 @@ public class DbMigrator cancellationToken.ThrowIfCancellationRequested(); var builder = DeployChanges.To .SqlDatabase(_connectionString) - .JournalToSqlTable("dbo", "Migration") .WithScriptsAndCodeEmbeddedInAssembly(Assembly.GetExecutingAssembly(), - s => s.Contains($".DbScripts.") && !s.Contains(".Archive.")) + s => s.Contains($".{folderName}.") && !s.Contains(".Archive.")) .WithTransaction() .WithExecutionTimeout(new TimeSpan(0, 5, 0)); + if (repeatable) + { + builder.JournalTo(new NullJournal()); + } + else + { + builder.JournalToSqlTable("dbo", MigratorConstants.SqlTableJournalName); + } + if (enableLogging) { if (_logger != null) diff --git a/util/Migrator/Migrator.csproj b/util/Migrator/Migrator.csproj index c7f00465d..e18f5cc1f 100644 --- a/util/Migrator/Migrator.csproj +++ b/util/Migrator/Migrator.csproj @@ -2,6 +2,7 @@ + diff --git a/util/Migrator/MigratorConstants.cs b/util/Migrator/MigratorConstants.cs new file mode 100644 index 000000000..1ffcfcfe2 --- /dev/null +++ b/util/Migrator/MigratorConstants.cs @@ -0,0 +1,8 @@ +namespace Bit.Migrator; + +public static class MigratorConstants +{ + public const string SqlTableJournalName = "Migration"; + public const string DefaultMigrationsFolderName = "DbScripts"; + public const string TransitionMigrationsFolderName = "DbScripts_data_migration"; +} diff --git a/util/Migrator/SqlServerDbMigrator.cs b/util/Migrator/SqlServerDbMigrator.cs index 219a3ff39..3885a6f6c 100644 --- a/util/Migrator/SqlServerDbMigrator.cs +++ b/util/Migrator/SqlServerDbMigrator.cs @@ -70,7 +70,7 @@ public class SqlServerDbMigrator : IDbMigrator cancellationToken.ThrowIfCancellationRequested(); var builder = DeployChanges.To .SqlDatabase(_connectionString) - .JournalToSqlTable("dbo", "Migration") + .JournalToSqlTable("dbo", MigratorConstants.SqlTableJournalName) .WithScriptsAndCodeEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains($".DbScripts.") && !s.Contains(".Archive.")) .WithTransaction() diff --git a/util/MsSqlMigratorUtility/MsSqlMigratorUtility.csproj b/util/MsSqlMigratorUtility/MsSqlMigratorUtility.csproj index 0e423b150..7f19d6745 100644 --- a/util/MsSqlMigratorUtility/MsSqlMigratorUtility.csproj +++ b/util/MsSqlMigratorUtility/MsSqlMigratorUtility.csproj @@ -10,6 +10,7 @@ + diff --git a/util/MsSqlMigratorUtility/Program.cs b/util/MsSqlMigratorUtility/Program.cs index 517b9ecd9..681225ca3 100644 --- a/util/MsSqlMigratorUtility/Program.cs +++ b/util/MsSqlMigratorUtility/Program.cs @@ -1,55 +1,51 @@ using Bit.Migrator; +using CommandDotNet; using Microsoft.Extensions.Logging; internal class Program { + private static IDictionary Parameters { get; set; } + private static int Main(string[] args) { - if (args.Length == 0) - { - Console.WriteLine("Please enter a database connection string argument."); - WriteUsageToConsole(); - return 1; - } - - if (args.Length == 1 && (args[0] == "--verbose" || args[0] == "-v")) - { - Console.WriteLine($"Please enter a database connection string argument before {args[0]} option."); - WriteUsageToConsole(); - return 1; - } - - var databaseConnectionString = args[0]; - - var verbose = false; - - if (args.Length == 2 && (args[1] == "--verbose" || args[1] == "-v")) - { - verbose = true; - } - - var success = MigrateDatabase(databaseConnectionString, verbose); - - if (!success) - { - return -1; - } - - return 0; + return new AppRunner().Run(args); } + [DefaultCommand] + public void Execute( + [Operand(Description = "Database connection string")] + string databaseConnectionString, + [Option('v', "verbose", Description = "Enable verbose output of migrator logs")] + bool verbose = false, + [Option('r', "repeatable", Description = "Mark scripts as repeatable")] + bool repeatable = false, + [Option('f', "folder", Description = "Folder name of database scripts")] + string folderName = MigratorConstants.DefaultMigrationsFolderName) => MigrateDatabase(databaseConnectionString, verbose, repeatable, folderName); + private static void WriteUsageToConsole() { Console.WriteLine("Usage: MsSqlMigratorUtility "); Console.WriteLine("Usage: MsSqlMigratorUtility -v|--verbose (for verbose output of migrator logs)"); + Console.WriteLine("Usage: MsSqlMigratorUtility -r|--repeatable (for marking scripts as repeatable) -f|--folder (for specifying folder name of scripts)"); + Console.WriteLine("Usage: MsSqlMigratorUtility -v|--verbose (for verbose output of migrator logs) -r|--repeatable (for marking scripts as repeatable) -f|--folder (for specifying folder name of scripts)"); } - private static bool MigrateDatabase(string databaseConnectionString, bool verbose = false, int attempt = 1) + private static bool MigrateDatabase(string databaseConnectionString, bool verbose = false, bool repeatable = false, string folderName = "") { var logger = CreateLogger(verbose); + logger.LogInformation($"Migrating database with repeatable: {repeatable} and folderName: {folderName}."); + var migrator = new DbMigrator(databaseConnectionString, logger); - var success = migrator.MigrateMsSqlDatabaseWithRetries(verbose); + bool success = false; + if (!string.IsNullOrWhiteSpace(folderName)) + { + success = migrator.MigrateMsSqlDatabaseWithRetries(verbose, repeatable, folderName); + } + else + { + success = migrator.MigrateMsSqlDatabaseWithRetries(verbose, repeatable); + } return success; } diff --git a/util/MsSqlMigratorUtility/packages.lock.json b/util/MsSqlMigratorUtility/packages.lock.json index e39912d7d..7a1316c0f 100644 --- a/util/MsSqlMigratorUtility/packages.lock.json +++ b/util/MsSqlMigratorUtility/packages.lock.json @@ -2,6 +2,15 @@ "version": 1, "dependencies": { "net6.0": { + "CommandDotNet": { + "type": "Direct", + "requested": "[7.0.2, )", + "resolved": "7.0.2", + "contentHash": "sFfqn4T6Ux4AbGnhS+BZvf9iVeP6b9p9bwaMT8nsefVcYH2tC9MHj3AoY+GG0nnBPmFawRqdgud08cBjBdPByQ==", + "dependencies": { + "Microsoft.CSharp": "4.7.0" + } + }, "Microsoft.Extensions.Logging": { "type": "Direct", "requested": "[6.0.0, )", diff --git a/util/Setup/Program.cs b/util/Setup/Program.cs index e6eae0749..93304c6bb 100644 --- a/util/Setup/Program.cs +++ b/util/Setup/Program.cs @@ -190,7 +190,12 @@ public class Program var vaultConnectionString = Helpers.GetValueFromEnvFile("global", "globalSettings__sqlServer__connectionString"); var migrator = new DbMigrator(vaultConnectionString, null); - migrator.MigrateMsSqlDatabaseWithRetries(false); + + var log = false; + + migrator.MigrateMsSqlDatabaseWithRetries(log); + + migrator.MigrateMsSqlDatabaseWithRetries(log, true, MigratorConstants.TransitionMigrationsFolderName); } private static bool ValidateInstallation()