Introduction
Every solution has its own set of configuration. So operations needed to be done for automated build system depend on the project. Here I have suggested effective workflow and operations which are the same for every project.
Automation Processes
I have used Cruise Control and NAnt for build automation. Cruise control is a fantastic tool with which you can automate your build and implement continuous integration in your team very easily. It is also very easy to integrate other service output like Unit test, NCover result to improve build process and ensure good and deployment product.
Cruise control triggers (<intervalTrigger seconds="60"/>
) after certain time to check anything is checked into source control or with force build and starts build processes. In my project, our source control is maintained by our client which we access through internet. If we try to checkout whole solution after every check in for build, then it takes much time but we only need to merge last checked-in files. So we create a folder say SVNFOLDER where we check out the whole solution only one time and after that every time cruise control only updates with latest files but we will run build processes in that checkout location to ensure our code does not conflict anytime when taking latest changes by cruise control in next time. We create another folder say BUILDFOLDER where we copy fresh code from SVNFOLDER and perform all operations to build solutions. These operations I have done using NAnt. After updating latest code cruise control calls NAnt script as defined in config file.
NAnt operation can be described by the following steps:
- Copy whole source from SNVFolder to BuildFolder.
- Get the SVN Revision Number and CCNet level for creating version number. Version number as
<CCNetLevel>.<SVN Revision>
.
- Change GlobalAssembly.cs with version number and other settings and also change web.config with release configuration.
- Run MSBuild with all configuration to rebuild solution.
- Run Unit Test if your project contains Unit Test.
- Run NCoverage (optional).
- You can also run FxCop and other tools for analyzing your build. This step is also optional.
- Create Setup file or other deployable output.
- Copy the deployable output into SetupFolder with version number.
When ForceBuild is clicked from CCNET or when BuildCondition = “ForceBuild”
, then Nant does all operations as mentioned above from 1 to 9. And for regular checkin by developer, NAnt only performs steps 1 to 7 to ensure developer last checkin is safe to create deployable output.
The workflow of build automation is given here:
Implementation
I have included here ccnet configuration file and Nant build script which have been implemented based on the above described steps. Now I describe here my CCNet and Nant scripts.
In team environment, we need to ensure my code that I checked in source control does not break build and also have to ensure that my code is also deploy-able. To ensure that what we have checked in does not break build, we can build the solution by build automation tools after every check in or can do pre-commited build ( teamcity CI tool) which ensures last checked in code is secured for team development. After successful build, we can create setup file or deploy-able output but it is not needed after every commit which can increase our automated process time . But to ensure deploy-able output/setup file is also safe , we can automatically create setup file by doing a nightly build. We can do easily by cruise control <triggers>
options.
<triggers>
<intervaltrigger seconds="60">
<scheduletrigger time="04:00" buildcondition="ForceBuild">
</scheduletrigger></intervaltrigger></triggers>
Here after 60 seconds CCNet triggers to check if any new code is committed in SVN. If it finds any latest commit by developer, then it runs NAnt script. And with schedule trigger, it runs Nant at 6am with force build which does not depend on any latest code is committed in SVN or not. If you want, you create Setup file/deploy-able output when Force build condition is true
, otherwise only rebuild solution then you can create an target in Nant which makes this decision according to your BuildCondition.
<target name="Run">
<property value="" name="CCNetBuildCondition" overwrite="false"/>
<call target="BuildPublish"
unless="${CCNetBuildCondition=='ForceBuild'}">
<call target="BuildSetup" if="${CCNetBuildCondition=='ForceBuild'}"/>
</target>
Here in this target "Run
" calls other two targets 'BuildPublish
' and 'BuildSetup
' target. When you press ‘Force Build” button of CCNet or with BuildCondition =”ForceBuild”
, it will call target "BuildSetup
" otherwise for regular check in of source code, it will run "BuildPublish
" to ensure your source code is always deployable.
In target "BuildPublish
", it first calls "setversion
" target and runs MSBuild for rebuild solution in release mode which is Step 4 of the above described process. You can also include unit test script and NConverage scripts calls after MSBuild
command to ensure your unit test code is working well after checkin.
<target name="BuildPublish" depends="setversion">
<echo message="Build is published"/>
<exec program="${MSBuildPath}">
<arg value="${BuildDir}\${SolutionFileName}" />
<arg value="/property:Configuration=release" />
<arg value="/t:Rebuild" />
</exec>
</target>
Here before rebuilding the solution, it first calls "setversion
" target (Step 3). "setversion
" target also depends on another target "get
" and after performing "get
" target, it calls "getSubversionRevision
" which is step 2. This is needed for maintaining version to understand with which SVN latest revision our build system is working. After getting revision number, it sets version of assemblies common assembly file. So our assemblies also know CCNet build number and SVN revision number.
<target name="setversion" depends="get">
<call target="getSubversionRevision"/>
<asminfo output="${BuildDir}\GlobalAssemblyInfo.cs" language="CSharp">
<attributes>
<attribute
type="System.Reflection.AssemblyFileVersionAttribute"
value="${CCNetLabel}.${RevisionNumber}"/>
</attributes>
</asminfo>
</target>
Now the first step which basically copies SVN folder to BUILD folder for running the above described targets so that SVN folder remain the same as latest SVN repository and takes less time to get latest checkin. All modifications done by NAnt only on BUILD folder which is a copy of SVN folder. So SVN folder is always safe from conflicting with SVN repository.
<target name="clean" description="Remove all files from target folder.">
<delete dir="${BuildDir}" failonerror="false" />
<mkdir dir="${BuildDir}" />
</target>
<target name="get" depends="clean">
<copy todir="${BuildDir}">
<fileset basedir="${SVNDir}">
<include name="**/**"/>
</fileset>
</copy>
</target>
Here, I have described steps 1 to 4 which work after developer checkin code and when BuildCondition
is not equal to "ForceBuild
".
Now when Build Condition is "ForceBuild
", then calls "BuildSetup
" taget which also depends on "BuildPublish
". This means for "ForceBuild
" steps 1 to 4 is done before and then it creates setup file. This target is:
<target name="BuildSetup" depends="BuildPublish" >
<echo message="Build is setup"/>
<script language="c#">
<code>
<![cdata[
Here have some code which used to change product code so that running setup
allowed on machine with already contained previous version of this product.
]]>
</code>
</script>
<exec program="${VisualStudio9}" failonerror="true"
commandline="/build Release ${BuildDir}\${SolutionFileName}
/project MYPROJECT.Setup">
</exec>
<property name="publish.dir" value="${ResultDir}\${CCNetLabel}.${RevisionNumber}" />
<mkdir dir="${publish.dir}" />
<copy todir="${publish.dir}">
<fileset basedir="${BuildDir}\MYPROJECT.Setup\release">
<include name="*"/>
</fileset>
</copy>
</target>
After performing all steps of "BuildPublish
" target, it first runs Setup project and creates setup files. Then it creates version file in deployment folder and copies the setup file to this location so that it would be easy to understand that this setup is created for which SVN revision and CCNet Label.
Conclusion
I have described here all steps of build automation and its implementation which is common for all projects. I did not include here some NAnt targets like NUnit, NCover, NCoverExplorer, FxCop which are implemented in some of my projects. I did not include this NAnt target because these are not essential for all projects.