<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" version="2.0">
  <channel>
    <title>Eleutian SpeakENG Development Blog - continuous integration</title>
    <link>http://blog.eleutian.com/</link>
    <description />
    <language>en-us</language>
    <copyright>Eleutian Technology</copyright>
    <lastBuildDate>Sat, 08 Mar 2008 00:21:29 GMT</lastBuildDate>
    <generator>newtelligence dasBlog 2.0.7226.0</generator>
    <managingEditor>aaron@eleutian.com</managingEditor>
    <webMaster>aaron@eleutian.com</webMaster>
    <item>
      <trackback:ping>http://blog.eleutian.com/Trackback.aspx?guid=3d1771d8-9a20-4080-a03e-e8525c00e1d1</trackback:ping>
      <pingback:server>http://blog.eleutian.com/pingback.aspx</pingback:server>
      <pingback:target>http://blog.eleutian.com/PermaLink,guid,3d1771d8-9a20-4080-a03e-e8525c00e1d1.aspx</pingback:target>
      <dc:creator>Aaron</dc:creator>
      <wfw:comment>http://blog.eleutian.com/CommentView,guid,3d1771d8-9a20-4080-a03e-e8525c00e1d1.aspx</wfw:comment>
      <wfw:commentRss>http://blog.eleutian.com/SyndicationService.asmx/GetEntryCommentsRss?guid=3d1771d8-9a20-4080-a03e-e8525c00e1d1</wfw:commentRss>
      <slash:comments>8</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
          <a href="http://schambers.lostechies.com/">Sean Chambers</a> asked how we work with
a published branch so I figured I'd post on the topic since it's a somewhat interesting
one. It's not trivial and it took us a few tries to get it where it is now, and it's
still not quite right.
</p>
        <p>
The first step to using a published branch is to create the branch. You can do that
like this:
</p>
        <pre>svn cp https://yoursvnserver/svn/project/trunk \
https://yoursvnserver/svn/project/branches/published \
-m "Branching published"</pre>
        <p>
After that, we actually check out the whole branch. It's very useful to have both
the pubilshed branch and the trunk on one machine. Note that this isn't necessarily
trivial, and its feasibility entirely depends on your build. Having a location agnostic
build is a very important thing, and this is one of the reasons.
</p>
        <p>
Now we have both trunk and published branches. We <strong>almost</strong> always do
all of our work in trunk and then merge over to published. Jacob actually wrote a
PowerShell script to make the merges easier (from merge.ps1):
</p>
        <pre>param([int]$rev, [string]$reverse)

if (!$rev)
{
  echo "Merge.ps1 REV [REVERSE]"
  return;
}
if (!$reverse)
{
  $branch = "Published"
  $url = "https://yoursvnserver/svn/project/trunk"
}
else
{
  $branch = "Trunk"
  $url = "https://yoursvnserver/svn/project/published"
}

$previous = $rev - 1
$range = $previous.ToString() + ":" + $rev

pushd $branch
svn merge -r $range $url
popd</pre>
        <p>
The reason for doing most of the work in trunk is that often times any issues we run
into on the published site will still be issues in the trunk. It makes sense to apply
the work there first and then merge it over. The only time we patch published is when
we need to apply a hack to make something work so that we can fix it the right way
on the trunk later, or maybe that feature is completely different on the trunk and
the fix would not apply. Of course this is dangerous and you have to be sure to remember
to fix the underlying issue in the trunk before publishing from trunk again. Bug trackers
help with that.
</p>
        <p>
That brings us to publishing from trunk again. Merging everything from trunk into
the published branch is a giant pain, and just won't work if you've applied many hacks
to the published branch. I strongly advise against this. Instead, just start over:
</p>
        <pre>svn rm https://yoursvnserver/svn/project/branches/published \
-m "Deleteing published branch"
svn cp https://yoursvnserver/svn/project/trunk \
https://yoursvnserver/svn/project/branches/published \
-m "Branching published"</pre>
        <p>
By nuking it and recopying it you can just svn up on your published branch and you'll
have everything from trunk. For whatever reason, not removing it before copying it
again caused us issues. I'd recommend this two phase approach.
</p>
        <p>
Other things we learned in the course of this are the pros and cons of shared "stuff".
We have at least 10 gigs of course content and a few other resources that don't need
to be in the separate branches like our main trunk and published. We pulled those
into their own repositories and keep them in shared directories so both installs can
reference it. We also have a set of common build scripts between the two. This is
both a good and a bad thing. It's good because it removes some duplication and it
allows us to use a separate repo for these scripts (which is handy for some TC build
configurations that only need the scripts, though I guess we could just checkout the
scripts directory from trunk...) but it's bad because sometimes things will get out
of sync. We'll make changes to the shared scripts, and fix the trunk, but it doesn't
make sense or it's prohibitive to fix the published branch. You can see in the screen
shot I posted our published branch is currently failing. This is likely why. I'd probably
recommend keeping all build scripts branched so that you don't run into these sort
of issues.
</p>
        <p>
The next thing to worry about is the database. I mentioned that we use multiple databases
on the build server. What I didn't mention is that we also use multiple databases
on our dev machines. We usually have three. One for trunk, one for trunk tests and
one for published. The trunk tests db is imported "light" so our tests run
in 6 minutes instead of 14. The trunk db is a nearly full copy of the production db
so we have data when we poke around the site on our dev machines. We have a ConnectionStrings.config
that is generated from the database info you pass the build script. You can do something
like: msbuild Default.msbuild /p:DatabaseName=published and it'd build with the appropriate
connection string.
</p>
        <p>
For web applications you also have to worry about IIS. We have two web applications
configured in IIS. One for trunk and one for published. This allows us to easily switch
between the two by just changing our port or vdir in our url. They have multiple virtual
directories underneath them that point to our various shared directories.
</p>
        <p>
I think that's most of the tips I can think of right now. Let me know if you have
any questions about anything.
</p>
        <img width="0" height="0" src="http://blog.eleutian.com/aggbug.ashx?id=3d1771d8-9a20-4080-a03e-e8525c00e1d1" />
      </body>
      <title>Working with a published branch</title>
      <guid isPermaLink="false">http://blog.eleutian.com/PermaLink,guid,3d1771d8-9a20-4080-a03e-e8525c00e1d1.aspx</guid>
      <link>http://blog.eleutian.com/2008/03/08/WorkingWithAPublishedBranch.aspx</link>
      <pubDate>Sat, 08 Mar 2008 00:21:29 GMT</pubDate>
      <description>&lt;p&gt;
&lt;a href="http://schambers.lostechies.com/"&gt;Sean Chambers&lt;/a&gt; asked how we work with
a published branch so I figured I'd post on the topic since it's a somewhat interesting
one. It's not trivial and it took us a few tries to get it where it is now, and it's
still not quite right.
&lt;/p&gt;
&lt;p&gt;
The first step to using a published branch is to create the branch. You can do that
like this:
&lt;/p&gt;
&lt;pre&gt;svn cp https://yoursvnserver/svn/project/trunk \
https://yoursvnserver/svn/project/branches/published \
-m &amp;quot;Branching published&amp;quot;&lt;/pre&gt;
&lt;p&gt;
After that, we actually check out the whole branch. It's very useful to have both
the pubilshed branch and the trunk on one machine. Note that this isn't necessarily
trivial, and its feasibility entirely depends on your build. Having a location agnostic
build is a very important thing, and this is one of the reasons.
&lt;/p&gt;
&lt;p&gt;
Now we have both trunk and published branches. We &lt;strong&gt;almost&lt;/strong&gt; always do
all of our work in trunk and then merge over to published. Jacob actually wrote a
PowerShell script to make the merges easier (from merge.ps1):
&lt;/p&gt;
&lt;pre&gt;param([int]$rev, [string]$reverse)

if (!$rev)
{
  echo &amp;quot;Merge.ps1 REV [REVERSE]&amp;quot;
  return;
}
if (!$reverse)
{
  $branch = &amp;quot;Published&amp;quot;
  $url = &amp;quot;https://yoursvnserver/svn/project/trunk&amp;quot;
}
else
{
  $branch = &amp;quot;Trunk&amp;quot;
  $url = &amp;quot;https://yoursvnserver/svn/project/published&amp;quot;
}

$previous = $rev - 1
$range = $previous.ToString() + &amp;quot;:&amp;quot; + $rev

pushd $branch
svn merge -r $range $url
popd&lt;/pre&gt;
&lt;p&gt;
The reason for doing most of the work in trunk is that often times any issues we run
into on the published site will still be issues in the trunk. It makes sense to apply
the work there first and then merge it over. The only time we patch published is when
we need to apply a hack to make something work so that we can fix it the right way
on the trunk later, or maybe that feature is completely different on the trunk and
the fix would not apply. Of course this is dangerous and you have to be sure to remember
to fix the underlying issue in the trunk before publishing from trunk again. Bug trackers
help with that.
&lt;/p&gt;
&lt;p&gt;
That brings us to publishing from trunk again. Merging everything from trunk into
the published branch is a giant pain, and just won't work if you've applied many hacks
to the published branch. I strongly advise against this. Instead, just start over:
&lt;/p&gt;
&lt;pre&gt;svn rm https://yoursvnserver/svn/project/branches/published \
-m &amp;quot;Deleteing published branch&amp;quot;
svn cp https://yoursvnserver/svn/project/trunk \
https://yoursvnserver/svn/project/branches/published \
-m &amp;quot;Branching published&amp;quot;&lt;/pre&gt;
&lt;p&gt;
By nuking it and recopying it you can just svn up on your published branch and you'll
have everything from trunk. For whatever reason, not removing it before copying it
again caused us issues. I'd recommend this two phase approach.
&lt;/p&gt;
&lt;p&gt;
Other things we learned in the course of this are the pros and cons of shared &amp;quot;stuff&amp;quot;.
We have at least 10 gigs of course content and a few other resources that don't need
to be in the separate branches like our main trunk and published. We pulled those
into their own repositories and keep them in shared directories so both installs can
reference it. We also have a set of common build scripts between the two. This is
both a good and a bad thing. It's good because it removes some duplication and it
allows us to use a separate repo for these scripts (which is handy for some TC build
configurations that only need the scripts, though I guess we could just checkout the
scripts directory from trunk...) but it's bad because sometimes things will get out
of sync. We'll make changes to the shared scripts, and fix the trunk, but it doesn't
make sense or it's prohibitive to fix the published branch. You can see in the screen
shot I posted our published branch is currently failing. This is likely why. I'd probably
recommend keeping all build scripts branched so that you don't run into these sort
of issues.
&lt;/p&gt;
&lt;p&gt;
The next thing to worry about is the database. I mentioned that we use multiple databases
on the build server. What I didn't mention is that we also use multiple databases
on our dev machines. We usually have three. One for trunk, one for trunk tests and
one for published. The trunk tests db is imported &amp;quot;light&amp;quot; so our tests run
in 6 minutes instead of 14. The trunk db is a nearly full copy of the production db
so we have data when we poke around the site on our dev machines. We have a ConnectionStrings.config
that is generated from the database info you pass the build script. You can do something
like: msbuild Default.msbuild /p:DatabaseName=published and it'd build with the appropriate
connection string.
&lt;/p&gt;
&lt;p&gt;
For web applications you also have to worry about IIS. We have two web applications
configured in IIS. One for trunk and one for published. This allows us to easily switch
between the two by just changing our port or vdir in our url. They have multiple virtual
directories underneath them that point to our various shared directories.
&lt;/p&gt;
&lt;p&gt;
I think that's most of the tips I can think of right now. Let me know if you have
any questions about anything.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://blog.eleutian.com/aggbug.ashx?id=3d1771d8-9a20-4080-a03e-e8525c00e1d1" /&gt;</description>
      <comments>http://blog.eleutian.com/CommentView,guid,3d1771d8-9a20-4080-a03e-e8525c00e1d1.aspx</comments>
      <category>continuous integration</category>
      <category>development</category>
      <category>teamcity</category>
    </item>
    <item>
      <trackback:ping>http://blog.eleutian.com/Trackback.aspx?guid=4e3d3cae-f228-4bab-9a2b-261d6993b1e2</trackback:ping>
      <pingback:server>http://blog.eleutian.com/pingback.aspx</pingback:server>
      <pingback:target>http://blog.eleutian.com/PermaLink,guid,4e3d3cae-f228-4bab-9a2b-261d6993b1e2.aspx</pingback:target>
      <dc:creator>Aaron</dc:creator>
      <wfw:comment>http://blog.eleutian.com/CommentView,guid,4e3d3cae-f228-4bab-9a2b-261d6993b1e2.aspx</wfw:comment>
      <wfw:commentRss>http://blog.eleutian.com/SyndicationService.asmx/GetEntryCommentsRss?guid=4e3d3cae-f228-4bab-9a2b-261d6993b1e2</wfw:commentRss>
      <slash:comments>1</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
          <a href="http://blog.eleutian.com/content/binary/WindowsLiveWriter/TeamCityrocks_AB04/projects_2.png">
            <img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="244" alt="projects" src="http://blog.eleutian.com/content/binary/WindowsLiveWriter/TeamCityrocks_AB04/projects_thumb.png" width="155" align="right" border="0" />
          </a>I've <a href="http://blog.eleutian.com/Default.aspx#a59f6c9cd-c0de-4bc4-8235-e5838734b1cb">mentioned
before</a> how much I like <a href="http://www.jetbrains.com/teamcity/">TeamCity</a>,
but I didn't really talk about how we use it. Just recently Jacob completed some work
on multiple build configurations that make our life much easier. I thought I'd go
over them here to give you an idea of how we handle continuous integration here.
</p>
        <h4>Continuous Integration configurations
</h4>
        <ul>
          <li>
            <strong>CI - Trunk</strong> - This is the CI configuration everyone has. This watches
the trunk of our SVN repository and builds whenever it changes it will also run our
database migrations against the CI trunk database. It has all our built assemblies
as artifacts. The artifacts take up a lot of space so we don't keep them around that
long. 
</li>
          <li>
            <strong>CI - Published</strong> - This is just like CI - Trunk, but it watches our
published branch, which is where we put everything that we're about to publish to
the live site. We keep two branches so that we can make quick fixes to the published
site without having to publish new features we're working on. This has its own database
that is migrated. 
</li>
          <li>
            <strong>Nightly - Trunk </strong>- This runs daily rather than watching our source
control. It migrates a database on our production database server that is a copy of
our live database. It also builds and deploys the trunk to a test address on our production
servers. This allows our team in Korea and our stakeholders to see changes every day
in a safe environment. The Nightly is also a big part of our localization story, which
I'll save for another post.</li>
        </ul>
        <h4>Database build configurations
</h4>
        <ul>
          <li>
            <strong>Snapshot </strong>- This and the other db build configurations probably deserve
their own post with more details, but I'll do my best to explain these briefly. The
Snapshot build configuration takes a point in time snapshot of the database and packages
it into a zip file. The zip file becomes a TeamCity artifact that other projects can
depend on. 
</li>
          <li>
            <strong>Nightly/CI Trunk/CI Published Baseline</strong> - 
<p>
These configurations import database snapshots into the database they refer to. The
only time we need to do this is if a bad migration runs, or we want to "refresh" the
data inside the database.
</p><p>
It is important to note that we do not run CI - Trunk on a complete snapshot of the
live database. When we do, it greatly increases the build time because our integration
tests run significantly slower in a real database. Instead, we import a "light" database
which contains all of the tables, but only the data from our static tables. The users,
records, and anything else that grows as we get more and more users are just left
empty. This means that we have zero sample data for these things during our integration
tests, so we rely heavily on <a href="http://blog.eleutian.com/2007/09/29/FluentFixtures.aspx">Fluent
Fixtures</a> to set up sample data. 
</p><p>
The other two databases do run nearly complete copies of the live database (we exclude
log tables basically), so we still get to test our migrations and our site on real
data.
</p></li>
        </ul>
        <h4>Utility build configurations
</h4>
        <ul>
          <li>
            <strong>StatSVN</strong> - Runs <a href="http://www.statsvn.org/">StatSVN</a> on our
codebase. Somewhat useful source statistics. Mostly use it to see growth and churn. 
</li>
          <li>
            <strong>Duplicate Finder </strong>- Haven't really done this much to be honest, but
TeamCity has <a href="http://blogs.jetbrains.com/teamcity/2007/09/14/code-duplicates-search/">build
configurations</a> whose sole purpose is finding duplicate code.</li>
        </ul>
        <p>
I'm completely enamored with this set of configurations. It makes so many things so
painless. We still have room for improvement, managing all of the configuration differences
in the sites is difficult. We also lack a one click live publish ability. We still
follow a manual script for that, which is error prone and dangerous. 
</p>
        <img width="0" height="0" src="http://blog.eleutian.com/aggbug.ashx?id=4e3d3cae-f228-4bab-9a2b-261d6993b1e2" />
      </body>
      <title>Our build configurations</title>
      <guid isPermaLink="false">http://blog.eleutian.com/PermaLink,guid,4e3d3cae-f228-4bab-9a2b-261d6993b1e2.aspx</guid>
      <link>http://blog.eleutian.com/2008/03/07/OurBuildConfigurations.aspx</link>
      <pubDate>Fri, 07 Mar 2008 14:40:24 GMT</pubDate>
      <description>&lt;p&gt;
&lt;a href="http://blog.eleutian.com/content/binary/WindowsLiveWriter/TeamCityrocks_AB04/projects_2.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="244" alt="projects" src="http://blog.eleutian.com/content/binary/WindowsLiveWriter/TeamCityrocks_AB04/projects_thumb.png" width="155" align="right" border="0"&gt;&lt;/a&gt;I've &lt;a href="http://blog.eleutian.com/Default.aspx#a59f6c9cd-c0de-4bc4-8235-e5838734b1cb"&gt;mentioned
before&lt;/a&gt; how much I like &lt;a href="http://www.jetbrains.com/teamcity/"&gt;TeamCity&lt;/a&gt;,
but I didn't really talk about how we use it. Just recently Jacob completed some work
on multiple build configurations that make our life much easier. I thought I'd go
over them here to give you an idea of how we handle continuous integration here.
&lt;/p&gt;
&lt;h4&gt;Continuous Integration configurations
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CI - Trunk&lt;/strong&gt; - This is the CI configuration everyone has. This watches
the trunk of our SVN repository and builds whenever it changes it will also run our
database migrations against the CI trunk database. It has all our built assemblies
as artifacts. The artifacts take up a lot of space so we don't keep them around that
long. 
&lt;li&gt;
&lt;strong&gt;CI - Published&lt;/strong&gt; - This is just like CI - Trunk, but it watches our
published branch, which is where we put everything that we're about to publish to
the live site. We keep two branches so that we can make quick fixes to the published
site without having to publish new features we're working on. This has its own database
that is migrated. 
&lt;li&gt;
&lt;strong&gt;Nightly - Trunk &lt;/strong&gt;- This runs daily rather than watching our source
control. It migrates a database on our production database server that is a copy of
our live database. It also builds and deploys the trunk to a test address on our production
servers. This allows our team in Korea and our stakeholders to see changes every day
in a safe environment. The Nightly is also a big part of our localization story, which
I'll save for another post.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Database build configurations
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Snapshot &lt;/strong&gt;- This and the other db build configurations probably deserve
their own post with more details, but I'll do my best to explain these briefly. The
Snapshot build configuration takes a point in time snapshot of the database and packages
it into a zip file. The zip file becomes a TeamCity artifact that other projects can
depend on. 
&lt;li&gt;
&lt;strong&gt;Nightly/CI Trunk/CI Published Baseline&lt;/strong&gt; - 
&lt;p&gt;
These configurations import database snapshots into the database they refer to. The
only time we need to do this is if a bad migration runs, or we want to "refresh" the
data inside the database.
&lt;/p&gt;
&lt;p&gt;
It is important to note that we do not run CI - Trunk on a complete snapshot of the
live database. When we do, it greatly increases the build time because our integration
tests run significantly slower in a real database. Instead, we import a "light" database
which contains all of the tables, but only the data from our static tables. The users,
records, and anything else that grows as we get more and more users are just left
empty. This means that we have zero sample data for these things during our integration
tests, so we rely heavily on &lt;a href="http://blog.eleutian.com/2007/09/29/FluentFixtures.aspx"&gt;Fluent
Fixtures&lt;/a&gt; to set up sample data. 
&lt;/p&gt;
&lt;p&gt;
The other two databases do run nearly complete copies of the live database (we exclude
log tables basically), so we still get to test our migrations and our site on real
data.
&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Utility build configurations
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;StatSVN&lt;/strong&gt; - Runs &lt;a href="http://www.statsvn.org/"&gt;StatSVN&lt;/a&gt; on our
codebase. Somewhat useful source statistics. Mostly use it to see growth and churn. 
&lt;li&gt;
&lt;strong&gt;Duplicate Finder &lt;/strong&gt;- Haven't really done this much to be honest, but
TeamCity has &lt;a href="http://blogs.jetbrains.com/teamcity/2007/09/14/code-duplicates-search/"&gt;build
configurations&lt;/a&gt; whose sole purpose is finding duplicate code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
I'm completely enamored with this set of configurations. It makes so many things so
painless. We still have room for improvement, managing all of the configuration differences
in the sites is difficult. We also lack a one click live publish ability. We still
follow a manual script for that, which is error prone and dangerous. 
&lt;/p&gt;
&lt;img width="0" height="0" src="http://blog.eleutian.com/aggbug.ashx?id=4e3d3cae-f228-4bab-9a2b-261d6993b1e2" /&gt;</description>
      <comments>http://blog.eleutian.com/CommentView,guid,4e3d3cae-f228-4bab-9a2b-261d6993b1e2.aspx</comments>
      <category>continuous integration</category>
      <category>development</category>
      <category>teamcity</category>
    </item>
    <item>
      <trackback:ping>http://blog.eleutian.com/Trackback.aspx?guid=59f6c9cd-c0de-4bc4-8235-e5838734b1cb</trackback:ping>
      <pingback:server>http://blog.eleutian.com/pingback.aspx</pingback:server>
      <pingback:target>http://blog.eleutian.com/PermaLink,guid,59f6c9cd-c0de-4bc4-8235-e5838734b1cb.aspx</pingback:target>
      <dc:creator>Aaron</dc:creator>
      <wfw:comment>http://blog.eleutian.com/CommentView,guid,59f6c9cd-c0de-4bc4-8235-e5838734b1cb.aspx</wfw:comment>
      <wfw:commentRss>http://blog.eleutian.com/SyndicationService.asmx/GetEntryCommentsRss?guid=59f6c9cd-c0de-4bc4-8235-e5838734b1cb</wfw:commentRss>
      <slash:comments>5</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
One of the first things we did when we got a dedicated development server was set
up a <a href="http://en.wikipedia.org/wiki/Continuous_Integration">continuous integration</a> server.
The natural choice at the time was <a href="http://confluence.public.thoughtworks.org/display/CCNET/Welcome+to+CruiseControl.NET">CruiseControl.NET</a>,
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.
</p>
        <p>
Enter the free Professional Edition of <a href="http://www.jetbrains.com/teamcity/">TeamCity
3.0</a>. 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. 
</p>
        <p>
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, <a href="http://studios.thoughtworks.com/mingle-project-intelligence">Mingle</a>, <a href="http://www.fogcreek.com/FogBugz/">FogBugz</a> 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. 
</p>
        <p>
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.
</p>
        <p>
          <a href="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/buildconfiguration_2.jpg">
            <img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="244" alt="buildconfiguration" src="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/buildconfiguration_thumb.jpg" width="137" align="right" border="0" />
          </a> Setting
up a build configuration in Team City is an absolute breeze. You basically have a
7 step Q&amp;A about your configuration:
</p>
        <ol>
          <li>
            <strong>General Settings </strong>- Name, Description, a few options and the build
number format (we use our SVN revision #)</li>
          <li>
            <strong>Version Control Settings</strong> - 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.</li>
          <li>
            <strong>Build Runner</strong> - 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.</li>
          <li>
            <strong>Build Triggering</strong> - You can trigger based on VCS changes, schedule,
other build configurations completing, or automatic retry.</li>
          <li>
            <strong>Artifact dependencies</strong> - 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.</li>
          <li>
            <strong>Properties and environment variables </strong>- Self explanatory.</li>
          <li>
            <strong>Agent requirements</strong> - 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.</li>
        </ol>
        <p>
          <a href="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/configs_2.jpg">
            <img style="border-right: 0px; border-top: 0px; margin: 0px 0px 0px 25px; border-left: 0px; border-bottom: 0px" height="187" alt="configs" src="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/configs_thumb.jpg" width="604" border="0" />
          </a>
        </p>
        <p>
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. 
</p>
        <p>
          <a href="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/testduration_2.jpg">
            <img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="80" alt="testduration" src="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/testduration_thumb.jpg" width="244" align="right" border="0" />
          </a> 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 <a href="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/failedtext_2.jpg"><img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="82" alt="failedtext" src="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/failedtext_thumb.jpg" width="187" align="right" border="0" /></a>installed.
The Tests tab even orders your tests by Duration so you have an idea which tests you
may need to optimize.
</p>
        <p>
          <a href="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/statistics_2.jpg">
            <img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="244" alt="statistics" src="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/statistics_thumb.jpg" width="211" align="left" border="0" />
          </a>Then
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. 
</p>
        <p>
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. 
</p>
        <p>
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.
</p>
        <img width="0" height="0" src="http://blog.eleutian.com/aggbug.ashx?id=59f6c9cd-c0de-4bc4-8235-e5838734b1cb" />
      </body>
      <title>CruiseControl.NET is dead. Long live Team City!</title>
      <guid isPermaLink="false">http://blog.eleutian.com/PermaLink,guid,59f6c9cd-c0de-4bc4-8235-e5838734b1cb.aspx</guid>
      <link>http://blog.eleutian.com/2007/12/22/CruiseControlNETIsDeadLongLiveTeamCity.aspx</link>
      <pubDate>Sat, 22 Dec 2007 22:13:34 GMT</pubDate>
      <description>&lt;p&gt;
One of the first things we did when we got a dedicated development server was set
up a &lt;a href="http://en.wikipedia.org/wiki/Continuous_Integration"&gt;continuous integration&lt;/a&gt; server.
The natural choice at the time was &lt;a href="http://confluence.public.thoughtworks.org/display/CCNET/Welcome+to+CruiseControl.NET"&gt;CruiseControl.NET&lt;/a&gt;,
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.
&lt;/p&gt;
&lt;p&gt;
Enter the free Professional Edition of &lt;a href="http://www.jetbrains.com/teamcity/"&gt;TeamCity
3.0&lt;/a&gt;. 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. 
&lt;/p&gt;
&lt;p&gt;
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, &lt;a href="http://studios.thoughtworks.com/mingle-project-intelligence"&gt;Mingle&lt;/a&gt;, &lt;a href="http://www.fogcreek.com/FogBugz/"&gt;FogBugz&lt;/a&gt; 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. 
&lt;/p&gt;
&lt;p&gt;
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.
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/buildconfiguration_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="244" alt="buildconfiguration" src="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/buildconfiguration_thumb.jpg" width="137" align="right" border="0"&gt;&lt;/a&gt; Setting
up a build configuration in Team City is an absolute breeze. You basically have a
7 step Q&amp;amp;A about your configuration:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;General Settings &lt;/strong&gt;- Name, Description, a few options and the build
number format (we use our SVN revision #)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version Control Settings&lt;/strong&gt; - 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build Runner&lt;/strong&gt; - 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build Triggering&lt;/strong&gt; - You can trigger based on VCS changes, schedule,
other build configurations completing, or automatic retry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Artifact dependencies&lt;/strong&gt; - 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Properties and environment variables &lt;/strong&gt;- Self explanatory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent requirements&lt;/strong&gt; - 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.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
&lt;a href="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/configs_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; margin: 0px 0px 0px 25px; border-left: 0px; border-bottom: 0px" height="187" alt="configs" src="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/configs_thumb.jpg" width="604" border="0"&gt;&lt;/a&gt; 
&lt;/p&gt;
&lt;p&gt;
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. 
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/testduration_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="80" alt="testduration" src="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/testduration_thumb.jpg" width="244" align="right" border="0"&gt;&lt;/a&gt; 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 &lt;a href="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/failedtext_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="82" alt="failedtext" src="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/failedtext_thumb.jpg" width="187" align="right" border="0"&gt;&lt;/a&gt;installed.
The Tests tab even orders your tests by Duration so you have an idea which tests you
may need to optimize.
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/statistics_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="244" alt="statistics" src="http://blog.eleutian.com/content/binary/WindowsLiveWriter/CruiseControl.NETisdead.LongliveTeamCity_C953/statistics_thumb.jpg" width="211" align="left" border="0"&gt;&lt;/a&gt;Then
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. 
&lt;/p&gt;
&lt;p&gt;
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. 
&lt;/p&gt;
&lt;p&gt;
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.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://blog.eleutian.com/aggbug.ashx?id=59f6c9cd-c0de-4bc4-8235-e5838734b1cb" /&gt;</description>
      <comments>http://blog.eleutian.com/CommentView,guid,59f6c9cd-c0de-4bc4-8235-e5838734b1cb.aspx</comments>
      <category>development</category>
      <category>reviews</category>
      <category>continuous integration</category>
      <category>teamcity</category>
    </item>
  </channel>
</rss>