Search code examples
c#sqlcontinuous-integrationcontinuous-deploymentdbup

Force DBUP to rerun new scripts during development


We're using DBUP to handle db migrations. Each release, we would like to run the dbup console app with a command line switch so that during dev we can re-run our scripts while we're working on them, however we don't want it to re-run all the previous releases scripts which already appear in the database. How can this be achieved?


Solution

  • We added a '-debug' command line switch to our DbUp console application. If this is present we switch which Journal class is used when talking to the database.

    The Journal class (https://dbup.readthedocs.io/en/latest/more-info/journaling/) in DbUp is the class that interacts with the database to check and record which scripts have already been run (stored by default in the Schema Versions table). For Dev, we force this to use a read-only version of this, which can check which scripts are already present (to prevent you re-running everything each time) but prevents new records being recorded, so that next time it will attempt to re-run your new scripts again.

    The read only journal looks like this;

    public class ReadOnlyJournal : IJournal
    {
    
        private readonly IJournal _innerJournal;
    
        public ReadOnlyJournal(IJournal innerJournal)
        {
            _innerJournal = innerJournal;
        }
    
        public void EnsureTableExistsAndIsLatestVersion(Func<IDbCommand> dbCommandFactory)
        {
            _innerJournal.EnsureTableExistsAndIsLatestVersion(dbCommandFactory);
        }
    
        public string[] GetExecutedScripts()
        {
            return _innerJournal.GetExecutedScripts().ToArray();
        }
    
        public void StoreExecutedScript(SqlScript script, Func<IDbCommand> dbCommandFactory)
        {
            // don't store anything
        }
    }
    

    Then an extension method to allow the use of this new journal to be easier specified;

    public static class DbUpHelper
    {
        public static UpgradeEngineBuilder WithReadOnlyJournal(this UpgradeEngineBuilder builder, string schema, string table)
        {
            builder.Configure(c => c.Journal = new ReadOnlyJournal(new SqlTableJournal(() => c.ConnectionManager, () => c.Log, schema, table)));
            return builder;
        }
    }
    

    And then finally the change to your DbUp console app;

    var upgrader = debug 
                ? DeployChanges.To
                    .SqlDatabase(connectionString)
                    .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly())
                    .WithReadOnlyJournal("dbo", "SchemaVersions")
                    .LogToConsole()
                    .Build()
                : DeployChanges.To
                    .SqlDatabase(connectionString)
                    .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly())
                    .LogToConsole()
                    .Build();
    
    var result = upgrader.PerformUpgrade();
    
            if (!result.Successful)
            ....