Pain-Free Feature Branch Testing: Update

Posted on Jul 7, 2012

Refresher

A few months ago, I started writing about my personal requirements for simplifying the process of using testing feature branches at work. I'd made quite a bit of headway, but never got around to posting an update. Thankfully, David Ruttka decided to remind me that the article needed some resolution. 1

In order to get everyone to buy into the concept of feature branches, the process of being able to test a feature branch needed to provide next to no overhead. Thus, my goals were as follows:

  • Detect the addition of a new feature branch
  • Clone a plan on our Bamboo server
  • Create a new application in IIS on our test box
  • Create a new database on our database server
  • Update the build script to generate configuration files programatically
  • Add a link to the issue on our Jira server, pointing to the deployed feature branch

Detect the addition of a new feature branch / Clone a plan on our Bamboo server

Initially, my plan was to use Git's post-receive hook to fire off a script that, using an extended set of rest calls provided by the ModPlan plugin, would clone and reconfigure a build plan when every new branch was pushed. Before I managed to get this working, however, Atlassian announced version 4.1 of Bamboo. This version included the ability to detect and clone new branches automatically. I quickly upgraded our build server rather than continuing to spend time reinventing the wheel.

Create a new application in IIS on our test box

Here's where the bulk of the problem came into play. There is no real limit on the number of feature branches we could have, though there is a limit on the number of servers that we could install onto. Since there's almost always going to be more branches than servers, our application needed to be installed parallel to other instances of itself. The biggest implication to the build script was that we couldn't simply copy to the same location each time nor could we use the same url for access. However, if the system was to be conducive to use, there needed to be an intuitive relation between the url, the filesystem location, and the name of the branch itself. In this case, we appended the name of the branch onto one base for the filesystem and onto another base for the URL. This way, knowledge of one of the three names would logically reveal the other two. Looking at an example, we would deploy our branch Feature/1.1/ABC-100-Upgraded-Foobar-Library to Z:\MyApplication\Feature\1.1\ABC-100-Upgraded-Foobar-Library and have the URL http://example.com/Feature/1.1/ABC-100-Upgraded-Foobar-Library. The following macrodef made the calculated names available to the rest of the build script.

<macrodef name="calculateDeployDir">
  <sequential>
    <if>
      <not>
        <!-- Only call git if the name hasn't been specified in one of the .property files. -->
        <isset property="deploy.url" />
      </not>
      <then>
        <exec executable="git" outputproperty="deploy.versionName">
          <arg line="name-rev HEAD --name-only" />
        </exec>
        <property name="deploy.url" value="http://${deploy.remoteServer}/${deploy.versionName}" />
      </then>
    </if>
  </sequential>
</macrodef>

If the deploy url had not already been specified somewhere else, we would pipe the output of git name-rev HEAD --name-only into deploy.versionName. Assuming that the command ran properly, the variable would now contain the name of the feature branch. The next step was to use the name of the branch to create an IIS application on the remote server. This was accomplished by writing a batch wrapper around APPCMD and invoking it on the remote server via PsExec.

REM If a site already exists, an error would be thrown that causes the build to fail. We don't care about this, so we're effectively hiding the false failure.
ECHO OFF

REM Make sure filepath only has \ and that uripath only has / in the addresses.
SET server=%1
SET filepath=%2
SET filepath=%filepath:/=\%
SET uripath=%filepath:\=/%
SET uripath=/%uripath%

REM Attempt to create a new app. If one exists, we fail silently.
start /wait PsExec.exe -accepteula -s \\%server%  %systemroot%\system32\inetsrv\APPCMD add app /site.name:"Default Web Site" /path:%uripath% /physicalPath:"Z:\MyApplication\%filepath%" /applicationPool:"Awesome AppPool" >NUL

ECHO "Done"

The script started out by making sure that each of the variables has the correct version of either a forward slash or a backslash. After that, it filled the modified variables into our template. Separating this code into a batch file made the Ant script a bit more readable, bypassed the overly verbose method of dealing with regexs in Ant, and (most importantly) worked around an issue where Ant hangs forever when directly calling PsExec. Continuing the earlier example, assuming we deployed our files to Z:\MyApplication\Feature\1.1\ABC-100-Upgraded-Foobar-Library2, the following would create it as a web app at http://testserver/Feature/1.1/ABC-100-Upgraded-Foobar-Library in the “Awesome AppPool”.

createIISApp.bat testserver Feature/1.1/ABC-100-Upgraded-Foobar-Library

Create a new database on our database server

Because of a change to the way that we store configuration, rather than having to stand up a new database, add permissions, and populate the default configuration, we were able to simply copy a configuration file to each new instance. This greatly simplified the deployment process.

Update the build script to generate configuration files programatically3

In conjunction with the url that we calculated earlier, we used Ant's replaceregexp task to replace the placeholder values in our configuration file. Note that all entities in the replacement string must be html encoded.

<replaceregexp file="config/base.xml"
  match=".*server-url.*"
  replace="&#9;&lt;url name=&quot;server-url&quot;&gt;${deploy.url}/&lt;/url&gt;" />

This part still still isn't complete. One of the guys wrote a script to reindex the server periodically and generate a new listing of the various installed branches. That seems to satisfy everyone. As a bonus, one of the plugins on our Jira server automatically tags issues with the names of any branches that an issue's commits appear in. Thus, some issues don't even require consulting the index.


  1. As a note, if you aren't following David, you're missing an excellent Tech resource. ↩︎

  2. As a quick rant, how do you Windows users deal using a backslash for a seperator? It makes me feel like I'm trying to escape everything I type. ↩︎

  3. Firefox's spellcheck wanted me to “correct” programatically to melodramatically. I think it knows more than it's letting on. ↩︎