Automate ClickOnce with Nant

05.14.08

Problem: You need to deploy a clickonce enabled application using NAnt script.

 

Solution: Use the MSBuild, version, exec and copy tasks from the NAnt, and NAntContrib Projects.

 

            When using the clickonce publish wizard, the wizard does some neat things for you. First it increments the version, builds the project then it generates some manifests, signs the manifests, generates a default html page, creates a setup.exe and finally copies all the needed files to your deployment location. This great up until you want to automate this build process and you find out not all of these tasks are being handled in msbuild.

 

Incrementing Version with NAntContrib task version. I also like to automatically update and commit this version number (the file build.number) so that everyone on the team is on the same page.

 

 

Use the MSBuild task from NAntCotrib on the project that is setup for clickonce deployment and use Publish as the target. This will build the project, generate the manifest, sign the manifest, and create the setup.exe. This will not generate a default.htm page; you must create this by hand. I just create a page using the generated page as a template and the application manifest as a datasource to get current build information.

 

 <!--Get the latest buld number from repositiory-->
<target name="versioning">
<exec program="svn.exe" basedir="tools\svn" 
commandline="up build.number 
--username username 
--password password 
--no-auth-cache 
--non-interactive"/>
 
<!--Increment buld number-->
<version buildtype="NoIncrement" revisiontype="Increment" />
 
<!--Commit buld number to repositiory-->
<exec program="svn.exe" basedir="tools\svn" 
commandline="commit build.number 
-m AutomatedCommit 
--username username 
--password password 
--no-auth-cache 
--non-interactive"/>
</target>

 

Finally I just copy the files to the production server.

 

<!--Copy production files to the server-->
<target name ="publish.clickonce"> 
  <copy todir="${Production.dir}">
    <fileset basedir="${ClickOnce.dir}">
      <include name="**/*" />
    </fileset>
  </copy>
</target> 

Complete Source: This contains everything I used for the example the click once hello world project, my project tree, build tools, and build file.

ClickOnceNant.zip (3.93 MB)

Reference:

How to create a SelfCert .pfx file to sign manifest.

ClickOnce on Multiple Environments

Versioning Using NAnt

Nant

NantContrib

Mission Complete: Continuous Integration .Net

06.13.07

I have completed or at least gotten a good start with Continuous Integration. What I have done may not be doctrine but it is what I could find to be the simplest and cleanest way. There are in my opinion 4 main parts you must have in order to achieve CI. First you must have a good repository, second you need to have a development tree setup for your project, then each project needs a build script, and finally you need to setup an automated build server. For more on continuous integration check out Martin Fowlers article and Jay Flowers article.

1. Repository

The repository/version control/source code management, what ever you like to call it is the first thing that needs to be established. There needs to be a common place where your entire source is kept so that your build server can know where to get the latest version (head). I choose Subversion (svn) with TortoiseSVN. I was using visual source safe and I was happy about this, but like my grandpa is happy with dial-up, I had no idea what I was missing. If you are using source safe switch now to Subversion its open source and its free you will not regret it.If you would like to know just about everything a user would need to know about modern repositories go to Eric Sink’s online book: Source Control HOWTO.

2. Creating Development Tree

Basically the Development tree is the folder structure/organization of your project. A good development tree is defined by Mike Roberts as easily inheritable on new environments, requires little maintenance, easily maintained, supports productivity, and is consistent. I tried to follow his guide lines in his article: How to Setup a .NET Development Tree. I am using a refactored patterns project I did earlier for this example. I define the trunk/root folder name to be the same as my visual studio solution. In this folder I have 3 other folders src, lib, and tools.

The src contains all the developer created content need to build the solution. The lib contains all references dlls that are needed for the project and are not built with the solution. The tools folder will house things like NAnt, NUnit, FXCop, and whatever else you need to help build quality software. You can also include a docs folder to contain the documentation. Another good source on development trees is an article on

3. Build Script

To automate my builds I choose NAnt  + NAntContrib. I save the build script in the root of the project with the file name of [projectName].Build and a go.bat that contains one line that calls NAnt with the build script.

@tools\nant\Nant.exe -buildfile:[projectName].build %* > buildOut.txt

 

 The > BuildOut.txt saves the out put of the build result to a text file I found this convenient to view how the build went. My build script should be made in a way that is easily implemented, easy to maintain, and need as little maintenance as possible. I include this as an existing solution item that way it can be edited along with the src.

<?xml version="1.0" encoding="utf-8"?>
<project name ="nant" default ="all">
  <!-- The Build Directory -->    
  <property name="build.dir" value ="build"/>
  <!-- Define Default targets and the order they run -->
  <target name="all" depends="clean, compile, run-unit-tests" description="Compile and Run Tests"/>
  <!-- Start fresh, delete build folder -->
  <target name="clean" description="Delete automated build artifacts">
      <delete dir="${build.dir}" if="${directory::exists(property::get-value('build.dir'))}"/>
  </target>
  <!-- Compile solution with msbuild task(Only Available with NAntCOntrib) 
       if it is 2003 use solution task all build properties should be 
       set up in a new VS Automated Build configuration. -->
  <target name="compile">
  <!-- point msbuild to the solution file with should be at the root of your src folder-->    
    <msbuild project="src\Patterns.sln">
      <property name="Configuration" value="Automated"/>
    </msbuild>
  </target>
  <!-- RunUnit tests -->
  <target name ="run-unit-tests">
  <!-- Creates Nunit Reprot Directory-->
    <mkdir dir="${build.dir}\test-reports"/>
    <exec program="nunit-console.exe" basedir="tools\nunit"
      workingdir="${build.dir}">
      <!-- The dll that contains your test that 
           should now be in the build folder -->
      <arg value="Strategy.Test.dll"/>
      <!-- Location and name of the NUnit test report -->
      <arg value="/xml:test-reports\UnitTests.xml"/>
    </exec>
  </target>
</project>

In side VS I set up a new visual studio build configuration called Automated. Automated is basically a copy of the default configuration Release but I changed the folder it builds to be ..\..\build\.

4. Build Server

The final step is implementing your build server. I have heard of people just using scheduled tasks for this but that doesn’t sound very useful. I wanted the build done when some one commits a change to the repository. For this I used CruiseControl.NET. CruiseControl.NET is an Automated Continuous Integration server, implemented using the Microsoft .NET Framework. It works with majority of the source code management (SCM) tools out there and more importantly it works with subversion. The installation was simple just download and run the MSI but the configuration can be a little difficult because of the flexibility available. To get it running you need to edit the ccnet.config.

<cruisecontrol> 
  <!-- Each Project needs the following setup --> 
  <project name="Patterns"> 
    <!-- The Working folder/checked out 
         folder for the project on the server -->
    <workingDirectory>H:\MyApps\Patterns</workingDirectory> 
    <!-- sourcecontrol setup --> 
    <sourcecontrol type="svn"> 
      <!-- URL to the project in the repository --> 
      <trunkUrl>http://www.myrepos.com/myapps/Patterns/</trunkUrl> 
      <executable>D:\Program Files\Subversion\bin\svn.exe</executable> 
      <!-- The Working folder/checked out 
           folder for the project on the server -->
      <workingDirectory>H:\MyApps\Patterns</workingDirectory> 
    </sourcecontrol> 
    <!--Build task--> 
    <tasks> 
      <nant> 
        <!-- Nant EXE for project --> 
        <executable>H:\MyApps\Patterns\tools\Nant\nant.exe</executable> 
        <!-- where the build file lives --> 
        <baseDirectory>H:\MyApps\Patterns</baseDirectory> 
        <buildArgs></buildArgs> 
        <nologo>false</nologo> 
        <buildFile>Patterns.build</buildFile> 
        <buildTimeoutSeconds>1200</buildTimeoutSeconds> 
      </nant> 
    </tasks> 
    <!-- reporting information --> 
    <publishers>             
      <merge> 
        <files>         
          <!-- Location of Unit test reports --> 
          <file>H:\MyApps\Patterns\Build\test-reports\UnitTests*.xml</file> 
        </files> 
      </merge> 
      <!-- create the log files used by the CruiseControl.NET Web Dashboard --> 
      <xmllogger logDir="D:\Program Files\CruiseControl.NET\server\Patterns\BuildLogs" /> 
      <!--default statistics for capturing during the build process.--> 
      <statistics /> 
    </publishers> 
  </project> 
</cruisecontrol>

After that just edit the project, commit the changes, and watch the dashboard build report.

Mission: Continuous Integration

05.25.07

When I first accepted this mission I was so excited I imagined my self doing a happy dance, it was a Latin dance because maracas are cool. I thought this was going to be great! I am going to have an automated build process that hooks into source control so it will build the latest check in, run unit test, and then give you feed back on how every thing worked out. I was not in the least bit intimidated by the complexity of how all that may be because well all shops should have this, it can’t be any harder than installing Visual Studio, right? After about an hour of research my head exploded.

I have a lot of learning to do but I have found some references that will hopefully lead me in the right direction.

Reference: