Development Blog

 Saturday, December 22, 2007

One of the first things we did when we got a dedicated development server was set up a continuous integration server. The natural choice at the time was CruiseControl.NET, so Jacob set off to get CC.NET installed and configured. I can't speak to that experience first hand, but I know it wasn't a fun one. Lots of XML hell and other non-fun issues. The end result wasn't too bad--it'd email us when builds failed with a log, and we could review logs on our CC.NET site. There was really nothing WOW about it--it got the job done. It was frustrating at times because the logs it spat out were difficult to visually parse; it was basically just a verbose msbuild log with a failure mixed in somewhere... and that was if we were lucky enough to get a text log and not have to filter through the build xml log. I'm sure there are some things we could have done to filter that log down, apply xslt to our build.xml to make it more readable, or configure things to be nicer, but I'm also sure it would have involved hard work and more XML hell.

Enter the free Professional Edition of TeamCity 3.0. We'd tried out TC briefly in the past and it seriously lacked in the .NET support arena. Now they tout support for msbuild (2.0 and 3.5), sln files from 2003-2008, and even a nifty .NET duplicate code finder. They also have some pretty slick support for NUnit which I'll get to in a moment.

I decided to give it a shot again, so I set up the TC server on our Ubuntu VM. Why there? Well that's where most of our server tools are: SVN, Mingle, FogBugz and now TC. But wait, aren't we a .NET shop? Yep. The build doesn't happen on our Ubuntu VM. See, TC is set up to have a server + build agents. A build agent is basically a machine that will check out code, do the build when the server tells it to, and report back results and artifacts to the build server. This way you can have multiple Build Agents--different Operating Systems, system configurations, physical locations, etc.

Installing both the Server and the Agent were relatively straight forward. There were a few annoyances with the Agent's configuration (like I couldn't get it to use a network drive as a working directory and it took some tweaking to get things to work with our directory structure,) but after a while I got things up and running.

buildconfiguration Setting up a build configuration in Team City is an absolute breeze. You basically have a 7 step Q&A about your configuration:

  1. General Settings - Name, Description, a few options and the build number format (we use our SVN revision #)
  2. Version Control Settings - You can set up multiple repos to check out with a number of tools (SVN, CVS, Perforce, StarTeam, ClearCase). I'm sure it wouldn't be too difficult to write plugins for Mecurial or Git. You can also tell it where to check out relative to your working directory and whether or not you want it to check out to the agent or on the server and then have the agent download it.
  3. Build Runner - Mainly .NET and Java stuff. No Rake yet, but it supports command line so you could just use that. With MSBuild you just specify your MSBuild file, the target you want to run, and any command line args you need. It's here that you specify your build artifacts as well. Build artifacts are kept along side your completed builds and easily accessible from the TC site.
  4. Build Triggering - You can trigger based on VCS changes, schedule, other build configurations completing, or automatic retry.
  5. Artifact dependencies - This makes it really easy to say you want Build A to use Build B's artifacts. For example, an installer configuration that will build an installer based on the last successful CI build.
  6. Properties and environment variables - Self explanatory.
  7. Agent requirements - Not all your agents can run all your build configurations necessarily. This will let you set requirements. You can set these based on environment variables, machine names, etc.

configs

Once you get things set up, you get to see the really cool stuff. TeamCity is just packed full of nerdgasm features that just work out of the box. No need to scour the web for plugins, deal with XML, or anything like that. Things just work. For example, you get full status of the build while it is happening--you can see what MSBuild step the build is on, you can see the important messages in the Build Log (it filters them quite intelligently), you can even see what threads/processes are running. If you use their NUnit runner you can even see how many tests have passed/failed/been ignored.

testduration That's not all you can see with their test runner though. You also get a full history of each test--how long each test took each time it was run, and if one fails you can see in what build it first failed, if it has been fixed yet, and you can even click to open it in your IDE if you have the Visual Studio plugin failedtextinstalled. The Tests tab even orders your tests by Duration so you have an idea which tests you may need to optimize.

statisticsThen there's the Statistics tab. This tab is a one stop shop for build health. You can see how long the build takes, how often it succeeds, how many tests you have, how many fail and even how big the artifacts are. You can see our build #s jump when we switched from a counter to SVN revision.

If there aren't enough features for you, don't worry, like with most JetBrains products you can extend them to your hearts content. That is if you can stomach the lack of API documentation (surprisingly, normal usage documentation is pretty good). One of our guys is working on a Campfire Notification plugin at the moment so we can get better build notifications in our Campfire. That's another post though.

All in all, we're very happy with TeamCity. It's just as free as CC.NET for a team of our size, it's much much easier to set up, and it has way more features. How could you go wrong? I'm sure its only a matter of time before we start to see rake runners, MBUnit test runners, and many other things to make TC even better. JetBrains has a winner here.

by Aaron on Saturday, December 22, 2007 2:13:34 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [5]  |  Trackback
 Saturday, December 15, 2007

I just profiled one of our test assemblies (Unit tests only, nothing hitting the db). Mocking accounts for 196 seconds out of 357 seconds. The culprit is System.Reflection.Emit.TypeBuilder.CreateType(). This doesn't come as that much of a surprise to me, but still... is there a faster way to do this? Oh and yeah, the results of CreateType are cached so its only the first time you create a mock of a type that you take this hit, but still... that can really slow down TDD.

And no, I'm not saying we should stop mocking... just raising awareness.

by Aaron on Saturday, December 15, 2007 11:17:03 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Thursday, December 13, 2007

I'm going to be putting together a series of Vim Screencast Tutorials. My goal is to raise awareness in the world of just how powerful Vim is and then get everyone comfortable using it. Here's a short trailer that shows a few of the things that make me love Vim. This is my first Screencast of this kind and, yea, I know its cheesy.

You need to upgrade your Flash Player to at
least version 8 to view this video content.

Click here to download the latest version of Flash Player.

Click here to save

You can get Vim here, but I'd ultimately recommend ViEmu, a Vim emulator plugin for Visual Studio.

by Aaron on Thursday, December 13, 2007 2:23:32 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [14]  |  Trackback
 Wednesday, December 05, 2007

Well today was a big day at Eleutian. We've been running MonoRail and standard WebForms together in the same web application for more than a year now. As new features came up we would port things to MonoRail and so slowly things have been migrating over. Last week I noticed we had about four big pages left to convert and decided that my new mission was converting everything to MonoRail once and for all. I'm happy to say it's completely done!

aspx

What's nice is this has drastically cleaned up the Eleutian code base as we were able to get rid of:

  • Six projects in our solution, including one full of old server controls.
  • Duplicated template code in our brail layout and the master page.
  • Old ASP.NET provider's for authentication and permissions.
  • Some complex NHibernate session handling code (as well as other similar code for supporting both scenarios)
  • Several old presenters from when we did MVP with WebForms.

Not to mention many other things I deleted in my svn del frenzy today. Now we get to start the process of introducing a little more consistency into things. It'll be nice being able to finally refactor some things that were left alone because nobody wanted to go back and rewrite all of the WebForms code. I love deleting code almost as much as writing it.

by Jacob on Wednesday, December 05, 2007 8:34:04 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Thursday, November 29, 2007

So the other day, my computer started acting up, swapping like mad and that sort of thing. Turns out one of my drives had just up and disappeared. Luckily, I didn't lose anything important. My life pretty much revolves around subversion repositories at work and my university. But, it's still annoying to have to rebuild that local "cache" that my hard drive acts as. Especially since the failure was obvious and appeared to be fixable. The male SATA connector on the drive had broken. The small piece of plastic that the contacts rest on had snapped off inside of the cable. This left the gold pins floating helplessly in the air. I tried everything to get those seven posts on the drive to make contact with the cable. Lots of careful positioning and heavy application of electrical tape, etc... I went to the local Electronics Warehouse and they told me I was out of luck. I gave up.

A week or so later my dad hands me the same drive:

He had snipped off the end of a SATA cable and soldered the conductors directly onto the drive. He said it took him about an hour. Hah! My dad is a retired electrician who now works on industrial automation for rock and batch plants - PLC's and that sort of thing, a very different kind of programming. I plugged it in and it worked perfectly! I didn't think keeping the drive in service was a good idea so I just copied the data off. Thanks dad!

by Jacob on Thursday, November 29, 2007 11:18:26 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Sunday, November 18, 2007

I haven't posted in a while and decided to write a post about one of the little side projects I've worked on recently. I've been lurking around on the ALT.NET mailing list and one of the discussions I take time to keep up on is on Dependency Injection. Something I worked on a few weeks ago has some applications in that area and so I decided to see what others thought.

I was talking with Aaron several months ago about how many problems constructors in static languages cause. Ruby people are fortunate in that their constructors are much more flexible than their C# counterparts. Want something to be a singleton? Just return the same instance from the constructor! Lifecycle management and dependencies can be the responsibility of the constructor itself, rather than a function of a container.

Over the weekend a few weeks ago I wrote a library and MsBuild task that factors constructor calls out of code and into a pluggable factory system. You write code like this:

public class EmailSender
{
  public void TellEveryone(string message)
  {
    TemplateRenderer renderer = new TemplateRenderer();
    Console.WriteLine(renderer.Render(message));
  }
}
public class EmailSenderFactory : IObjectFactory<EmailSender>
{
  public EmailSender Create()
  {
    return new EmailSender();
  } 
}
EmailSender emailer = new EmailSender();
emailer.TellEveryone("Hello, World!");

What the MsBuild task will do is this:

  1. Load the target Assemblies using Mono.Cecil
  2. Find implementations of IObjectFactory<T> and add them to the FactoryMap.
  3. Find all instances of new T() for each of those factories and replace them with a call to the Factories.Create<T>(), ignoring calls inside of factories themselves.
  4. Weave the FactoryMap (just the Factory Type and its Object Type) into the Assembly.

So during runtime we can use the FactoryMap that was serialized in step #4 to create the factories and use them to create instances as they're demanded. It is important to note that your objects need default constructors, even though they may never be called after the code is weaved. One can also replace the factory implementations with IoC backed versions, etc...

Why don't you just create a New.Instance<T> method and use that instead of new T() and save yourself the pain of weaving and having a task and all that, moron?

Good question! I mostly did this as an exercise in code weaving using Cecil. It's a great library, although testing code using it can be a little tedious. I'm tossing around some other projects that I'd love to give a shot and this was a good introduction. You could achieve the same thing using reflection to build your factory map and remember to use New.Instance<T>() instead of the new operator in your code. Granted, the manual approach plasters the concern all over the place and weaving keeps the original code free from that. Which may be an OK trade-off for the simplified process.

Another thing to consider, is that perhaps the weaving approach has its applications elsewhere? Use it specifically for creating domain classes to transparently provide domain services? Another idea I've been tossing around is if you're writing code for platforms where garbage collection isn't as performant (xbox for example) you may be able to transparently add object pooling and other patterns after the fact and not clutter up the original code.

I want to release the code anyway, but I'm tired of uploading zip files. All my future source releases will probably be via Google Code or some public svn repository. Stay tuned.... In the meantime, feel free to opine.

by Jacob on Sunday, November 18, 2007 3:49:46 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, November 02, 2007
by Aaron on Friday, November 02, 2007 2:31:28 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback