Pawel Pabich has a good way to run DbUp as part of AppHarbor’s build process which looks interesting but I wanted to run the migrations manually. So here is a quick recipe for setting up DbUp with a web hook.
Something important to note is that there is no security around this. It relies on an obfuscated controller path, so it is only really useful for bootstrapping an early project. The controller should be moved behind security as soon as possible.
I’m assuming that DbUp has been set up as per my post on resetting the world. Specifically, the DbUp DLL should have an upgrader that looks like this:
namespace ResetTheWorld
{
public class DatabaseUpgrader
{
private const string ConnectionStringName = "xyz";
public static void Upgrade(IUpgradeLog upgradeLog = null)
{
var connectionString = ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString;
var upgrader = DeployChanges.To
.SqlDatabase(connectionString)
.WithScriptsEmbeddedInAssembly(Assembly.GetAssembly(typeof (DatabaseUpgrader)))
.LogTo(upgradeLog ?? new ConsoleUpgradeLog())
.Build();
var result = upgrader.PerformUpgrade();
if (!result.Successful)
{
throw new Exception("DB Upgrade failed", result.Error);
}
}
public static void SetConnectionString(string connectionString)
{
typeof (ConfigurationElementCollection).GetField("bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(ConfigurationManager.ConnectionStrings, false);
ConfigurationManager.ConnectionStrings.Add(new ConnectionStringSettings(ConnectionStringName,
connectionString,
"System.Data.SqlClient"));
}
}
}
I’ve just created a controller with a long random name:
public class njvfdklnvfjdkslndsieController : Controller
{
public ActionResult UpdateTheWorld()
{
var log = new HtmlLog();
DatabaseUpgrader.Upgrade(log);
ViewBag.Log = log.ToString();
return View();
}
}
The HtmlLog
is a basic replacement for DbUp’s console log that just builds up a HTML string:
class HtmlLog : IUpgradeLog
{
readonly StringBuilder _stringBuilder = new StringBuilder();
public override string ToString()
{
return _stringBuilder.ToString();
}
public void WriteError(string format, params object[] args)
{
_stringBuilder.AppendFormat("<p class='error'>" + format + "</p>", args);
}
public void WriteInformation(string format, params object[] args)
{
_stringBuilder.AppendFormat("<p class='information'>" + format + "</p>", args);
}
public void WriteWarning(string format, params object[] args)
{
_stringBuilder.AppendFormat("<p class='warning'>" + format + "</p>", args);
}
}
And the view for the UpdateTheWorld()
action just writes out the log, with added colours:
@model dynamic
@{
ViewBag.Title = "UpdateTheWorld";
}
<style>
p.error{color:red}
p.information{color:#000}
p.warning{color:orange}
</style>
<h1>UpdateTheWorld</h1>
@Html.Raw(ViewBag.Log)
Now browsing to https://<app>.apphb.com/njvfdklnvfjdkslndsie/UpdateTheWorld
will update the existing database. A ResetTheWorld
action could be added which drops the database before running the scripts.