Tuesday, August 12, 2008

Build Promotion with Hudson

I can't say enough good things about hudson. It's become a crucial part of our development platform. For those not familiar, Hudson is a continuous build server, much like cruisecontrol, but on steroids.

Our build process looked a little like this.

  1. Developer makes changes locally, commits to subversion
  2. Hudson detects changes, kicks off the compilation
  3. 3 Times a day, hudson would run the junit tests (about 25 minutes)
  4. 3 Times a day, hudson would run our xml api tests (about 10 minutes)
  5. 3 Times a day, hudson would run our Webtest tests
  6. Every two weeks or so, development would ask me to make a release build which would be published to our QA department.
This process was ok, but it is pretty inefficient. Each testsuite run could be executed on a different svn revision. If QA received a build with a serious problem, it became a big hassle to get them a new one.

My goal is to be able to turn around development changes in a much quicker fashion and get them into the hands of the qa group.

Enter hudson and build promotion. The idea here would be that for each checkin made by development, that subversion revision will go through a promotion process. Here is the process:

  1. Developer checks in code ->
  2. Hudson compiles code and builds our the ear ->
  3. Hudson zips up that project workspace and publishes it as an artifact for other jobs to consume
  4. The junit job is kicked off against the zipped workspace ->
  5. The api tests are kicked off against the zipped workspace ->
  6. The webtests are kicked off against the zipped workspace
  7. If all testsuites pass with zero errors, the build is "promoted" and is available for qa to test
  8. There may be several promoted builds per day/there may be 0.
How do I accomplish this in hudson?

  1. Install the build promotion plugin
  2. Configure your main compilation job use the build promotion plugin. You'll also need to add an ant target to zip the workspace and publish it as an artifact.
  3. In your child jobs, add a bootstrap target which downloads and explodes the zipped workspace. Remove any scm configurations from them.
  4. Finally, link your child jobs to the parent jobs via the promotion plugin configuration.



TODO:

  1. After things are promoted, they simply get a start next to them. I'm looking into the modifiying the promotion plugin so that it can perform more actions such as scping the artifacts to a webserver after they are promoted.
  2. I need to figure out a way to associate the sum of defects changed from 1 build to another and report them.

Monday, August 11, 2008

Managing your database schema - upgrading

One of the first problems I encountered at Pointserve (my current place of employment), was how to manage our database schema. Our software comes packaged as an ear, but relies on an everchanging database schema.

My predecessors had setup a file in cvs called nextBuildPatchUpdates.sql. Developers would commit into that file. At release time, that file was dynamically appended with an insert statement which included the release number. This was ok, but it didn't play well our continuous integration servers. Developers hated it as it was hard for them to keep track of changes during a development cycle.

Here is what we have now.

In subversion, we maintain an sql directory.

The sql directory contains these folders:
1) functions
2) procedures
3) upgrades
4) definitions
5) includes
6) triggers
7) views

These should be self explanatory. All of our database scripts are version controlled. That is key. Includes are just some commonly used data snippets.

The upgrades folder is the piece that we will look at. This folder contains two important pieces:

1) A collection of upgrade scripts in the format FROMVERSION-TOVERSION-upgrade.sql. It also contains an xml file called upgradePaths.xml.

Here is a sample of the upgradePaths.xml

<upgradePaths>
<upgradePath>
<fromVersion>0.0.0</fromVersion>
<toVersion>39.0.0</toVersion>
<fileName>0.0.0-39.0.0-upgrade.sql</fileName>
</upgradePath>
<upgradePath>
<fromVersion>39.0.0</fromVersion>
<toVersion>40.0.0</toVersion>
<fileName>39.0.0-40.0.0-upgrade.sql</fileName>
</upgradePath>
</upgradePaths>

What we have defined here, is an xml file that describes a collection of upgrade paths. Imagine that there are several hundred upgrade path definitions here. How do then figure out how to upgrade a schema?

First, you must figure out what version your db is currently at. Next, you must figure out which version you want to upgrade to. Then, you must compute a path between your start version and your end version. This is easy to do. Using the upgradePaths.xml, we transform it into a tree (complete with nodes and vertices). We locate the start node and end node and then use dijkstra's shortest path algorithm to find the path. We new have a listing of all nodes required (and more importantly, the upgrade scripts for each node). We then execute all of these scripts in order and voila! An upgraded database.

This logic was written once as a POJO. Its been wrapped into an ant task for use in both our build system and by our product installers.

The ant definition looks like this:

<taskdef name="dbupgrade" classname="com.pointserve.ant.tasks.DatabaseUpgrade" classpathref="buildtasks"/>
<!-- build customized db upgrade script -->
<dbupgrade servername="${JDBC_SERVER}" dbname="${JDBC_DATABASE}" username="${JDBC_LOGIN}" password="${JDBC_PASSWORD}" driver="${JDBC_DRIVER}" upgradetoversion="${JDBC_DATABASE_VERSION}" upgradeXML="${basedir}/source/sql/upgrades/upgradePaths.xml" upgradesDir="${basedir}/source/sql/upgrades" upgradeScript="${basedir}/source/sql/dbupgrade.sql"/>


That's a quick overview of the process. I'm happy to discuss this more as I haven't seen many standard solutions out there.

First post

Just a brief intro before we get to the good stuff. This purpose of this blog is talk about configuration management primarily in the java software space.

I'll be posting tidbits I find interesting and tips or tricks that I use as part of my daily work.