Dec 18 2009

Clean up unwanted svn folders with NAnt

Category: Agileryancmartin1976 @ 20:43

First Strategy:

Second Strategy:

If you ever had a corrupted Subversion repository on your local and the cleanup method wasn't working as it should. Your best bet most of the time depending on the size of the project is to delete all of the svn directories in it and start over fresh. This is a quick and dirty way to do that.

Delete all of svn directories in your project with 2 bat files and 1 NAnt build script.

Your folder structure should resemble something like this.

\ProjectName
    \Trunk
        \Binaries\NAnt
        \Build
        \Source

First you need to download NAnt and place it somewhere on the server where you are planning on doing the cleanup. I like to add a binaries directory under my trunk directory and place all of my executables there.

Next you need to create a new NAnt build script with one task and save it in the build directory as clean.build.

clean.build

<?xml version="1.0" encoding="utf-8"?>
<project name="CleanUp" default="build" xmlns="http://nant.sf.net/release/0.85/nant.xsd">
  <property name="trunk.dir" value="..\" />
 
  <target name="clean" depends="clean.svn.folders" />
 
  <target name="clean.svn.folders">
    <echo message="Deleting all svn folders in directory"/>
    <delete>
      <fileset basedir="${trunk.dir}" defaultexcludes="false">
      <include name="**/.svn" />
      <include name="**/.svn/**/*" />
      </fileset>
    </delete>
  </target>
</project>

Now create two bat files with notepad

build.bat

..\Binaries\NAnt\bin\nant.exe -buildfile:clean.build %*

clickToBuild.bat

@echo off
:input
cls
set INPUT=
set TARGET=
echo Please choose from the following build options:
echo .
echo 1 = Clean
echo .
set /P INPUT=Please specify your option from above: %=%
if "%INPUT%"=="" goto input
if "%INPUT%"=="1" set TARGET="clean"
if "%TARGET%"=="" goto input
echo .
echo .
echo .
build.bat %TARGET% & pause

Now you should have these files

  • Trunk\Build\clean.build
  • Trunk\Build\build.bat
  • Trunk\Build\clickToBuild.bat
  • Trunk\Binaries\Nant\Nant.exe

Now click on the bat file called clickToBuild.bat and select the number 1 to run and that’s it! All of the directories under your trunk named .svn will be deleted.

Tags: , ,

Dec 15 2009

Integrating CruiseControl.NET in your build process

Category: Continuous Integrationryancmartin1976 @ 21:05

First Strategy:

Second Strategy:

Let CruiseControl.NET take care of the heavy lifting!

Automating your daily and nightly builds is the most effective process one can take from the agile methodology processes and procedures. It is also very easy to setup and configure. Once you have it in place, it is on rare occasions when you have to make edits or upgrades to the build process. You can almost say it is as easy as, set it and forget it.

CruiseControl.NET automates your automation of the build process. Download the executable from Source Forge and run the executable on the server where the build process will be run from.

Ok things to note:

The configuration file for CruiseControl.NET lives here C:\Program Files\CruiseControl.NET\server\ccnet.config and this is where you will tell CruiseControl.NET what tasks in the build process to monitor and at what intervals to run at and who to notify when the task is either successful or broke.

There are 2 types of tasks that can be run from the ccnet.config file. First is run directly which special commands that are built right into CruiseControl.NET and the other is calling NAnt build script to run external tasks. You have a lot more flexibility with writing build tasks in NAnt.

ccnet.config – cruise control internal tasks

This task checks subversion for any edits or updates to the server every 120 seconds. If one is found then CruiseControl.NET compiles the solution making sure that the code introduced did not break the build.

<project name="Build">
  <labeller type="defaultlabeller" />
  <webURL>http://localhost/ccnet/ViewFarmReport.aspx</webURL>
 
  <sourcecontrol type="filtered">
    <sourceControlProvider type="svn" autoGetSource="true">
      <executable>C:\Program Files\VisualSVN Server\bin\svn.exe</executable>
      <trunkUrl>http://domain.com/svn/ProjectName/trunk</trunkUrl>
      <workingDirectory>D:\Projects\compile\ProjectName\trunk</workingDirectory>
      <tagOnSuccess>false</tagOnSuccess>
      <tagBaseUrl>http://domain.com/svn/ProjectName/tags</tagBaseUrl>
      <username>rmartin</username>
      <password>********</password>
    </sourceControlProvider>
    <inclusionFilters>
      <pathFilter>
        <pattern>**/*.*</pattern>
      </pathFilter>
    </inclusionFilters>
  </sourcecontrol>
 
  <triggers>
    <intervalTrigger name="continuous" seconds="120" buildCondition="IfModificationExists" />
  </triggers>
 
  <publishers>
    <merge>
      <files>
        <file>C:\Program Files\CruiseControl.NET\server\Build\Artifacts\results.xml</file>
      </files>
    </merge>
    <xmllogger />
    <statistics />
    <email
      from="ProjectNameBuild@domain.com"
      mailhost="mail.domain.com" includeDetails="TRUE">
      <users>
        <user name="Ryan Martin" group="buildmaster" address="rmartin@domain.com"/>
        <user name="Some Guy" group="buildmaster" address="sguy@domain.com"/>
      </users>
      <groups>
        <group name="buildmaster" notification="always"/>
      </groups>
    </email>
  </publishers>
</project>

ccnet.config – nant external tasks

This task is an external task which is run by a nant build script that lives potentially on the build server. You can have your build script wherever you choose but I like to have it with the project and all of its dependencies (documentation, binaries, database scripts and build scripts and reports).

<project name="Deploy-To-Development">
  <labeller type="defaultlabeller" />
  <webURL>http://localhost/ccnet/ViewFarmReport.aspx</webURL>
 
  <sourcecontrol type="filtered">
    <sourceControlProvider type="svn" autoGetSource="true">
      <executable>C:\Program Files\VisualSVN Server\bin\svn.exe</executable>
      <trunkUrl>http://domain.com/svn/ProjectName/trunk</trunkUrl>
      <workingDirectory>D:\Projects\compile\ProjectName\trunk</workingDirectory>
      <tagOnSuccess>false</tagOnSuccess>
      <tagBaseUrl>http://domain.com/svn/ProjectName/tags</tagBaseUrl>
      <username>rmartin</username>
      <password>Madison1</password>
    </sourceControlProvider>
    <inclusionFilters>
      <pathFilter>
        <pattern>**/*.*</pattern>
      </pathFilter>
    </inclusionFilters>
  </sourcecontrol>
 
  <tasks>
    <nant>
      <executable>D:\Projects\compile\ProjectName\trunk\binaries\nant\bin\nant.exe</executable>
      <baseDirectory>D:\Projects\compile\ProjectName\trunk\source\</baseDirectory>
      <buildArgs>-D:svn.executable="D:\Projects\compile\ProjectName\trunk\binaries\subversion\svn.exe"</buildArgs>
      <nologo>false</nologo>
      <buildFile>D:\Projects\compile\ProjectName\trunk\Build\ProjectName.build</buildFile>
      <targetList>
        <target>deploy-to-development</target>
      </targetList>
      <buildTimeoutSeconds>1200</buildTimeoutSeconds>
    </nant>
  </tasks>
 
  <publishers>
    <merge>
      <files>
        <file>C:\Program Files\CruiseControl.NET\server\Deploy-To-Development\Artifacts\results.xml</file>
      </files>
    </merge>
    <xmllogger />
    <statistics />
    <email
      from="ProjectNameBuild@domain.com"
      mailhost="mail.domain.com" includeDetails="TRUE">
      <users>
        <user name="Ryan Martin" group="buildmaster" address="rmartin@domain.com"/>
      </users>
      <groups>
        <group name="buildmaster" notification="always"/>
      </groups>
    </email>
  </publishers>
</project>

ProjectNameWeb.build – nant build script

This script has endless capabilities. There is an extensive set of commands you can call within nant scripts that are specific to nant build process. The command such as exec, echo, delete and copy are easy to learn and understand and most of all extremely powerful in helping cut down on development time by automating everyday remedial tasks.

<?xml version="1.0" encoding="utf-8"?>
<project name="IpswitchftWeb" default="build" xmlns="http://nant.sf.net/release/0.85/nant.xsd">
  <!-- Properties -->
  <property name="devserver.website.dir" value="D:\Projects\www\ProjectName"/>
  <property name="trunk.dir" value="..\" />
  <property name="source.dir" value="${trunk.dir}\Source" />
  <property name="msbuild.exe" value="C:\WINDOWS\Microsoft.NET\Framework\v3.5\msbuild.exe" />
  <property name="solution.sln" value="${source.dir}\ProjectNameWeb.sln" />
 
  <!-- Called Externally -->
  <target name="build" depends="compile" />
  <target name="deploy-to-development" depends="compile, deploy.to.development" />
 
  <!-- Coding Tasks -->
  <target name="compile">
    <exec program="${msbuild.exe}" commandline="${solution.sln} /t:Rebuild /v:q" />
  </target>
 
  <target name="deploy.to.development">
    <echo message="Deleting all files in development web directory"/>
    <delete verbose="true">
      <fileset basedir="${devserver.website.dir}">
        <include name="*.*" />
      </fileset>
    </delete>
 
    <echo message="XCopy files to development web directory"/>
    <copy todir="${devserver.website.dir}" includeemptydirs="false" overwrite="true">
      <fileset basedir="${ProjectNameWeb.dir}">
        <exclude name="**/obj/**"/>
        <exclude name="**/*.csproj"/>
        <exclude name="**/*.csproj*"/>
        <exclude name="**/App_Data*"/>
        <exclude name="**/App_Data*/**"/>
        <exclude name="**/.svn*"/>
        <exclude name="**/.svn*/**"/>
        <include name="**"/>
      </fileset>
    </copy>
  </target>
</project>

CruiseControl.NET Web Dashboard

This is a web interface to view and run CruiseControl.NET configuration file. From here you can run any task configured in your ccnet.config file and view the results.

This web page url defaults to the localhost setting but you will be viewing it from your local machine like other developers so you must change localhost with the server name: http://servername.domain.com/ccnet/ViewFarmReport.aspx

Tags: , , ,

Dec 15 2009

Integrate NUnit tests and results into CruiseControl.NET

Category: Continuous Integrationryancmartin1976 @ 21:04

First Strategy:

Second Strategy:

Integrating useful Unit Tests into CruiseControl.NET in ASP.NET Web Application

Unit tests can be a waste of time, especially if your writing them for every method looking for every possible way it could break. I found a very useful way to use unit tests in a project.

The entire project is data driven. All of the content lives in a database, even the page title, keywords and description.

This leaves a lot of room for possible failure points and unacceptable mistakes. Every page should have content on it, which goes without saying, but sometimes we forget to add the Meta data for pages thinking we will get back to it later and just forget. This is not a good scenario when the site depends heavily on SEO.

I have 4 projects:

  1. Web (UI)
  2. Core (Data Access)
  3. WebTests (UI Tests)
  4. CoreTests (Data Access Tests)

We are going to focus on the Web project and WebTests project.

Web project: Example web page that grabs content and Meta data from the database

About.aspx.cs

public partial class About : System.Web.UI.Page, IAbout
{
    private AboutPresenter _presenter;
    protected override void OnInit(EventArgs e)
    {
        _presenter = new AboutPresenter();
        _presenter.Init(this);
        _presenter.PerformLoad(MethodType.Page.Company_About.GetStringValue(),
                               null,
                               MethodType.Section.Main.GetStringValue());
    }
 
    public void LoadHtml(PageFT pageFt, PageContentFT pageContentFt)
    {
        if (pageFt == null)
        {
            Log.Error(this, "There is not any page details for this page.");
        }
        else if (pageFt.IsArchived)
        {
            Log.Error(this, "This page is archived and should not be allowed to be to viewed by users.");
        }
        else
        {
            BuildHeader(pageFt.Title, pageFt.Keywords, pageFt.Description);
            if (pageContentFt == null)
                Log.Error(this, "There is not any content for this page for the " + pageFt.Language + " language");
            else
                BuildPageContent(pageContentFt.PageContent);
        }
    }
 
    private void BuildHeader(string title, string keywords, string description)
    {
        litTitle.Text = "<title>" + title + " </title>";
        litKeywords.Text = "<meta name=\"keywords\" content=\"" + keywords + "\">";
        litDescription.Text = "<meta name=\"description\" content=\"" + description + "\">";
    }
 
    private void BuildPageContent(string pageContent)
    {
        litPageContent.Text = pageContent;
    }
}

AboutPresenter.cs

public class AboutPresenter
{
    private IAbout _view;
    private IUserSession _userSession;
    private IPageService _pageService;
    private IPageContentService _pageContentService;
 
    public void Init(IAbout view)
    {
        _view = view;
        _userSession = ObjectFactory.GetInstance<IUserSession>();
        _pageService = ObjectFactory.GetInstance<IPageService>();
        _pageContentService = ObjectFactory.GetInstance<IPageContentService>();
    }
 
    public void PerformLoad(string page, string lang, string section)
    {
        lang = string.IsNullOrEmpty(lang) ? _userSession.Culture : lang;
 
        if (string.IsNullOrEmpty(lang))
        {
            Log.Error(this, "Users browser culture is not getting passed in by method parameter or session variable.");
            lang = MethodType.Language.English.GetStringValue();
        }
 
        if (string.IsNullOrEmpty(page))
        {
            Log.Error(this, "Page name was not passed into the PerformLoad method which is needed.");
            return;
        }
 
        if (string.IsNullOrEmpty(section))
        {
            Log.Error(this, "Section name was not passed into the PerformLoad method which is needed.");
            return;
        }
 
        PageFT pageFt = _pageService.GetPageInfoByName(page, lang);
        PageContentFT pageContentFt = _pageContentService.GetPageContentBySection(page, lang, section);
        _view.LoadHtml(pageFt, pageContentFt);
    }
}

WebTests project: this unit test checks for a specific pages content and Meta data in all possible translated languages

AboutPresenterTester.cs

[TestFixture]
public class Company_About_Tester
{
    MockAboutPageView pageView = new MockAboutPageView();
    MockAboutPageTitleView pageTitleView = new MockAboutPageTitleView();
    MockAboutPageKeywordsView pageKeywordsView = new MockAboutPageKeywordsView();
 
    MockAboutPageContentView pageContentView = new MockAboutPageContentView();
    MockAboutPageContentHtmlView pageContentHtmlView = new MockAboutPageContentHtmlView();
 
    private MockRepository _mocks;
    private AboutPresenter _presenter;
 
    private string pageName;
    private string languageEnUs;
    private string languageDeDe;
    private string sectionMain;
 
    [SetUp]
    public void SetUp()
    {
        TestUtil.SetUpHttpContext();
        _mocks = new MockRepository();
        _presenter = _mocks.Stub<AboutPresenter>();
 
        pageName = MethodType.Page.Company_About.GetStringValue();
        languageEnUs = MethodType.Language.English.GetStringValue();
        languageDeDe = MethodType.Language.German.GetStringValue();
        sectionMain = MethodType.Section.Main.GetStringValue();
    }
 
    [Test]
    public void Company_About_Page_Record_Exists_In_Database_English()
    {
        _presenter.Init(pageView);
        _presenter.PerformLoad(pageName, languageEnUs, sectionMain);
    }
 
    [Test]
    public void Company_About_Page_Title_Exists_In_Database_English()
    {
        _presenter.Init(pageTitleView);
        _presenter.PerformLoad(pageName, languageEnUs, sectionMain);
    }
 
    [Test]
    public void Company_About_Page_Keywords_Exists_In_Database_English()
    {
        _presenter.Init(pageKeywordsView);
        _presenter.PerformLoad(pageName, languageEnUs, sectionMain);
    }
 
    [Test]
    public void Company_About_PageContent_Record_Exists_In_Database_English()
    {
        _presenter.Init(pageContentView);
        _presenter.PerformLoad(pageName, languageEnUs, sectionMain);
    }
 
    [Test]
    public void Company_About_PageContent_Html_Exists_In_Database_English()
    {
        _presenter.Init(pageContentHtmlView);
        _presenter.PerformLoad(pageName, languageEnUs, sectionMain);
    }
 
    [Test]
    public void Company_About_PageDetails_German_NotNull()
    {
        _presenter.Init(pageView);
        _presenter.PerformLoad(pageName, languageDeDe, sectionMain);
    }
 
    [Test]
    public void Company_About_PageDetails_German_TitleNotEmpty()
    {
        _presenter.Init(pageTitleView);
        _presenter.PerformLoad(pageName, languageDeDe, sectionMain);
    }
 
    [Test]
    public void Company_About_PageDetails_German_KeywordsNotEmpty()
    {
        _presenter.Init(pageKeywordsView);
        _presenter.PerformLoad(pageName, languageDeDe, sectionMain);
    }
 
    [Test]
    public void Company_About_PageContent_German_NotNull()
    {
        _presenter.Init(pageContentView);
        _presenter.PerformLoad(pageName, languageDeDe, sectionMain);
    }
 
    [Test]
    public void Company_About_PageContent_German_HtmlNotEmpty()
    {
        _presenter.Init(pageContentHtmlView);
        _presenter.PerformLoad(pageName, languageDeDe, sectionMain);
    }
}
 
public class MockAboutPageView : IAbout
{
    public void LoadHtml(PageFT pageFt, PageContentFT pageContentFt)
    {
        Assert.IsNotNull(pageFt);
    }
}
 
public class MockAboutPageTitleView : IAbout
{
    public void LoadHtml(PageFT pageFt, PageContentFT pageContentFt)
    {
        Assert.IsNotNull(pageFt.Title);
        Assert.IsNotEmpty(pageFt.Title);
    }
}
 
public class MockAboutPageKeywordsView : IAbout
{
    public void LoadHtml(PageFT pageFt, PageContentFT pageContentFt)
    {
        Assert.IsNotNull(pageFt.Keywords);
        Assert.IsNotEmpty(pageFt.Keywords);
    }
}
 
public class MockAboutPageContentView : IAbout
{
    public void LoadHtml(PageFT pageFt, PageContentFT pageContentFt)
    {
        Assert.IsNotNull(pageContentFt);
    }
}
 
public class MockAboutPageContentHtmlView : IAbout
{
    public void LoadHtml(PageFT pageFt, PageContentFT pageContentFt)
    {
        Assert.IsNotNull(pageContentFt.PageContent);
        Assert.IsNotEmpty(pageContentFt.PageContent);
    }
}

Now we have to setup the configuration file for CruiseControl.NET and add a task in the NAnt build script.

CruiseControl.NET ccnet.config

<project name="Test">
  <labeller type="defaultlabeller" />
  <webURL>http://localhost/ccnet/ViewFarmReport.aspx</webURL>
 
  <sourcecontrol type="filtered">
    <sourceControlProvider type="svn" autoGetSource="true">
      <executable>C:\Program Files\VisualSVN Server\bin\svn.exe</executable>
      <trunkUrl>http://domain.com/svn/ProjectName/trunk</trunkUrl>
      <workingDirectory>D:\Projects\compile\ProjectName\trunk</workingDirectory>
      <tagOnSuccess>false</tagOnSuccess>
      <tagBaseUrl>http://domain.com/svn/ProjectName/tags</tagBaseUrl>
      <username>rmartin</username>
      <password>*******</password>
    </sourceControlProvider>
    <inclusionFilters>
      <pathFilter>
        <pattern>**/*.*</pattern>
      </pathFilter>
    </inclusionFilters>
  </sourcecontrol>
 
  <tasks>
    <msbuild>
      <executable>C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe</executable>
      <workingDirectory>D:\Projects\compile\ProjectName\trunk\Source</workingDirectory>
      <projectFile>ProjectNameWeb.sln</projectFile >
      <buildArgs>
        /noconlog
        /noconsolelogger /v:quiet
        /p:Configuration=Debug
        /p:ReferencePath="D:\Projects\compile\ProjectName\trunk\Binaries\NUnit"
      </buildArgs>
      <targets>ReBuild</targets >
      <timeout>600</timeout >
      <logger>c:\Program Files\CruiseControl.NET\server\Rodemeyer.MsBuildToCCNet.dll</logger>
    </msbuild>
 
    <nunit>
      <path>D:\Projects\compile\ProjectName\trunk\Binaries\NUnit\nunit-console.exe</path>
      <assemblies>
        <assembly>D:\Projects\compile\ProjectName\trunk\Source\ProjectNameWebTests\bin\Debug\ProjectName.ProjectNameWebTests.dll</assembly>
      </assemblies>
    </nunit>
  </tasks>
 
  <publishers>
    <merge>
      <files>
        <file>C:\Program Files\CruiseControl.NET\server\Test\Artifacts\results.xml</file>
      </files>
    </merge>
    <xmllogger />
    <statistics />
    <email
      from="ProjectNameBuild@domain.com"
      mailhost="mail.domain.com" includeDetails="TRUE">
      <users>
        <user name="Ryan Martin" group="buildmaster" address="rmartin@domain.com"/>
      </users>
      <groups>
        <group name="buildmaster" notification="always"/>
      </groups>
    </email>
  </publishers>
</project>

NAnt build script

<project name="ProjectNameWeb" default="build" xmlns="http://nant.sf.net/release/0.85/nant.xsd">
  <!-- Properties -->
  <property name="devserver.website.dir" value="D:\Projects\www\ProjectName"/>
  <property name="trunk.dir" value="..\" />
  <property name="source.dir" value="${trunk.dir}\Source" />
  <property name="msbuild.exe" value="C:\WINDOWS\Microsoft.NET\Framework\v3.5\msbuild.exe" />
  <property name="solution.sln" value="${source.dir}\ProjectNameWeb.sln" />
 
  <!-- Called Externally -->
  <target name="build" depends="compile" />
  <target name="test" depends="compile, test.project.ProjectNameWeb" />
 
  <!-- Coding Tasks -->
  <target name="move.assemblies.for.tests" depends="compile">
    <echo message="Moving contents of ${ProjectNameWebTests.bin.dir} to ${assemblies.output.dir}" />
 
    <copy todir="${assemblies.output.dir}" flatten="true">
      <fileset basedir="${ProjectNameWebTests.bin.dir}">
        <include name="*.dll" />
      </fileset>
    </copy>
  </target>
 
  <target name="test.project.ProjectNameWeb" depends="move.assemblies.for.tests">
    <echo message="Starting to run tests for ProjectNameWeb" />
 
    <nunit2 haltonfailure="false" failonerror="false" verbose="true">
      <formatter type="Xml" extension=".xml" outputdir="${nunit.output.dir}" usefile="true" />
      <test assemblyname="${assemblies.output.dir}\ProjectNameWebTests.dll" />
    </nunit2>
  </target>
</project>

Run the task through CruiseControl.NET Web dashboard and viewed the results.

After click on the FORCE button for Test click on the most recent build number

And view the results by clicking the Nunit Details link on the left hand panel

What we make out of this test is the About.aspx page has content for the body, title and keywords in English but that content has either not been translated into German yet or it should be and now we know there is a problem that needs to be addressed

Tags: , , , ,

Dec 15 2009

Integrate FxCop code analysis into CruiseControl.NET

Category: Continuous Integrationryancmartin1976 @ 21:01

First Strategy:

Second Strategy:

Integrating FxCop into CruiseControl.NET

I am not a fan of this technology and I would probably steer away from using this in future projects.

There are three steps in setting up, configuring and viewing the results of FxCop within your Continuous Integration process.

STEP 1

Add a CruiseControl.NET task in your ccnet.config file

<project name="FxCop">
  <labeller type="defaultlabeller" />
  <webURL>http://localhost/ccnet/ViewFarmReport.aspx</webURL>
 
  <sourcecontrol type="filtered">
    <sourceControlProvider type="svn" autoGetSource="true">
      <executable>C:\Program Files\VisualSVN Server\bin\svn.exe</executable>
      <trunkUrl>http://domain.com/svn/ProjectName/trunk</trunkUrl>
      <workingDirectory>D:\Projects\compile\ProjectName\trunk</workingDirectory>
      <tagOnSuccess>false</tagOnSuccess>
      <tagBaseUrl>http://domain.com/svn/ProjectName/tags</tagBaseUrl>
      <username>rmartin</username>
      <password>*******</password>
    </sourceControlProvider>
    <inclusionFilters>
      <pathFilter>
        <pattern>**/*.*</pattern>
      </pathFilter>
    </inclusionFilters>
  </sourcecontrol>
 
  <tasks>
  <exec>
  <executable>C:\Program Files\Microsoft FxCop 1.36\FxCopCmd.exe</executable>
  <baseDirectory>D:\Projects\compile\ProjectName\trunk\source\</baseDirectory>
  <buildArgs>
      /p:"d:\Projects\compile\ProjectName\trunk\Build\rules.FxCop" 
      /out:"c:\Program Files\CruiseControl.NET\server\FxCop\Artifacts\results.xml" /gac
  </buildArgs>
  <buildTimeoutSeconds>300</buildTimeoutSeconds>
  </exec>
  </tasks>
 
  <publishers>
    <merge>
      <files>
        <file>c:\Program Files\CruiseControl.NET\server\FxCop\Artifacts\results.xml</file>
      </files>
    </merge>
    <xmllogger />
    <statistics />
    <email
      from="ProjectNameBuild@domain.com"
      mailhost="mail.domain.com" includeDetails="TRUE">
      <users>
        <user name="Ryan Martin" group="buildmaster" address="rmartin@domain.com"/>
      </users>
      <groups>
        <group name="buildmaster" notification="always"/>
      </groups>
    </email>
  </publishers>
</project>

STEP 2

Add a task in your NAnt build script

<?xml version="1.0" encoding="utf-8"?>
<project name="ProjectNameWeb" default="build" xmlns="http://nant.sf.net/release/0.85/nant.xsd">
  <!-- Properties -->
  <property name="fxcop.output.dir" value="${build.output.dir}\FxCopOutput" />
  <property name="fxcopcmd.exe" value="C:\Program Files\Microsoft FxCop 1.36\fxcopcmd.exe" />
  <property name="rules.fxcop" value="rules.fxcop" />
  <property name="fxcop.xml" value="${fxcop.output.dir}\fxcop.xml" />
 
  <!-- Called Externally -->
  <target name="build" depends="compile" />
  <target name="fxcop" depends="compile, reporting" />
 
  <!-- Coding Tasks -->
  <target name="reporting" depends="compile">
    <exec program="${fxcopcmd.exe}" commandline="/p:${rules.fxcop} /o:${fxcop.xml}" failonerror="false"/>
  </target>
</project>

STEP 3

Go to your CruiseControl.NET Web Dashboard to run and view the results from FxCop

Click the FORCE button for the FxCop task and review the results it generates

NOT WORTH IT IN MY OPINION, RESHARPER DOES A LOT OF THE SAME FUNCTIONALITY IN REAL TIME

Tags: , , , , ,

Dec 15 2009

Deploy Subversion code base into your development environment via CruiseControl.NET

Category: Continuous Integrationryancmartin1976 @ 21:00

First Strategy:

Second Strategy:

Deploy your local code to development for further testing with a click of a button

There are 3 techniques used here to make code deployment seamless for testing in your development environment.

  1. Setup a server to be the development server
  2. Add your project to Subversion
  3. Configure your cruisecontrol.net configuration file and nant build script accordingly

STEP 1

Pick any server to be your development server, potentially one that you use for all projects and sites. This server should mirror your production server, same frameworks, executables and services.

STEP 2

Add your local project to your Subversion server.

STEP 3

Configure the CruiseControl.NET ccnet.config file to call Nant build task without an interval trigger. This assures that the versioned codebase will never get copied over by accident.

<project name="Deploy-To-Development">
  <labeller type="defaultlabeller" />
  <webURL>http://localhost/ccnet/ViewFarmReport.aspx</webURL>
 
  <sourcecontrol type="filtered">
    <sourceControlProvider type="svn" autoGetSource="true">
      <executable>C:\Program Files\VisualSVN Server\bin\svn.exe</executable>
      <trunkUrl>http://domain.com/svn/ProjectName/trunk</trunkUrl>
      <workingDirectory>D:\Projects\compile\ProjectName\trunk</workingDirectory>
      <tagOnSuccess>false</tagOnSuccess>
      <tagBaseUrl>http://domain.com/svn/ProjectName/tags</tagBaseUrl>
      <username>rmartin</username>
      <password>Madison1</password>
    </sourceControlProvider>
    <inclusionFilters>
      <pathFilter>
        <pattern>**/*.*</pattern>
      </pathFilter>
    </inclusionFilters>
  </sourcecontrol>
 
  <tasks>
    <nant>
      <executable>D:\Projects\compile\ProjectName\trunk\binaries\nant\bin\nant.exe</executable>
      <baseDirectory>D:\Projects\compile\ProjectName\trunk\source\</baseDirectory>
      <buildArgs>-D:svn.executable="D:\Projects\compile\ProjectName\trunk\binaries\subversion\svn.exe"</buildArgs>
      <nologo>false</nologo>
      <buildFile>D:\Projects\compile\ProjectName\trunk\Build\ProjectName.build</buildFile>
      <targetList>
        <target>deploy-to-development</target>
      </targetList>
      <buildTimeoutSeconds>1200</buildTimeoutSeconds>
    </nant>
  </tasks>
 
  <publishers>
    <merge>
      <files>
        <file>C:\Program Files\CruiseControl.NET\server\Deploy-To-Development\Artifacts\results.xml</file>
      </files>
    </merge>
    <xmllogger />
    <statistics />
    <email
      from="ProjectNameBuild@domain.com"
      mailhost="mail.domain.com" includeDetails="TRUE">
      <users>
        <user name="Ryan Martin" group="buildmaster" address="rmartin@domain.com"/>
      </users>
      <groups>
        <group name="buildmaster" notification="always"/>
      </groups>
    </email>
  </publishers>
</project>

Next setup the NAnt build script to run a task that does the deployment

<?xml version="1.0" encoding="utf-8"?>
<project name="IpswitchftWeb" default="build" xmlns="http://nant.sf.net/release/0.85/nant.xsd">
  <!-- Properties -->
  <property name="devserver.website.dir" value="D:\Projects\www\ProjectName"/>
  <property name="trunk.dir" value="..\" />
  <property name="source.dir" value="${trunk.dir}\Source" />
  <property name="msbuild.exe" value="C:\WINDOWS\Microsoft.NET\Framework\v3.5\msbuild.exe" />
  <property name="solution.sln" value="${source.dir}\ProjectNameWeb.sln" />
 
  <!-- Called Externally -->
  <target name="build" depends="compile" />
  <target name="deploy-to-development" depends="compile, deploy.to.development" />
 
  <target name="deploy.to.development">
    <echo message="Deleting all files in development web directory"/>
    <delete verbose="true">
      <fileset basedir="${devserver.website.dir}">
        <include name="*.*" />
      </fileset>
    </delete>
 
    <echo message="XCopy files to development web directory"/>
    <copy todir="${devserver.website.dir}" includeemptydirs="false" overwrite="true">
      <fileset basedir="${ProjectNameWeb.dir}">
        <exclude name="**/obj/**"/>
        <exclude name="**/*.csproj"/>
        <exclude name="**/*.csproj*"/>
        <exclude name="**/App_Data*"/>
        <exclude name="**/App_Data*/**"/>
        <exclude name="**/.svn*"/>
        <exclude name="**/.svn*/**"/>
        <include name="**"/>
      </fileset>
    </copy>
  </target>
</project>

Next run the task from CruiseControl.NET Web Dashboard

This process takes about 30 seconds to 3 minutes depending on the size of the application. You can add all types of files to exclude from the copy over which saves you tons of time in it from doing it manually. This is especially helpful when your project is in Subversion and you have all of those .svn folders everywhere.

Tags: , , ,

Dec 15 2009

Report on your code base for TODO comments via CruiseControl.NET

Category: Continuous Integrationryancmartin1976 @ 20:59

First Strategy:

  • a
  • b
  • c

Second Strategy:

Everyone has to do's in their code comments, so here is an easy way to to check how many you have left in your project

STEP 1

Add a new project to your solution called BuildTasks. Add a new class called ParseCodeDebt.cs

Here is the class code

using System.Diagnostics;
using System.IO;
using NAnt.Core;
using NAnt.Core.Attributes;
 
[TaskName("ParseCodeDebt")]
public class ParseCodeDebt : Task
{
    private string CurrentFolder { get; set; }
 
    public void PerformTest()
    {
        ExecuteTask();    
    }
 
    [TaskAttribute("PathToParse", Required = true)]
    [StringValidator (AllowEmpty = false)]
    public string PathToParse { get; set; }
    protected override void ExecuteTask()
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        Log(Level.Warning,"---------------------------------------------------------------------------");
        Log(Level.Warning, "CODE DEBT CHECK for " + PathToParse);
 
        //iterate through all the files in a directory recursively
        System.IO.DirectoryInfo root = new DirectoryInfo(PathToParse);
        WalkDirectoryTree(root);
 
        Log(Level.Warning, "");
        Log(Level.Warning, "Completed in " + sw.ElapsedMilliseconds + "ms");
        Log(Level.Warning, "---------------------------------------------------------------------------");
        Log(Level.Warning, "");
    }
 
    private void WalkDirectoryTree(System.IO.DirectoryInfo root)
    {
        System.IO.FileInfo[] files = null;
        System.IO.DirectoryInfo[] subDirs = null;
 
        // First, process all the files directly under this folder
        try
        {
            files = root.GetFiles("*.*");
        }
            // This is thrown if even one of the files requires permissions greater
            // than the application provides.
        catch (UnauthorizedAccessException e)
        {
            // This code just writes out the message and continues to recurse.
            Log(Level.Warning, e.Message);
        }
 
        catch (System.IO.DirectoryNotFoundException e)
        {
            Log(Level.Warning, e.Message);
        }
 
        if (files != null)
        {
            foreach (System.IO.FileInfo fi in files)
            {
                if (fi.Name.ToLower().EndsWith(".cs") && fi.Name.ToLower() != "parsecodedebt.cs")
                {
                    try
                    {
                        //look for code debt in this file
                        ParseFile(fi);
                    }
                    catch (FileNotFoundException e)
                    {
                        Log(Level.Warning, e.Message);
                    }
                }
            }
 
            // Now find all the subdirectories under this directory.
            subDirs = root.GetDirectories();
 
            foreach (System.IO.DirectoryInfo dirInfo in subDirs)
            {
                if (dirInfo.Name != ".svn")
                {
                    // Resursive call for each subdirectory.
                    WalkDirectoryTree(dirInfo);
                }
            }
        }
    }
 
    private void ParseFile(FileInfo file)
    {
        StreamReader sr = file.OpenText();
        int lineNumber = 1;
        string line = "";
        line = sr.ReadLine();
        while(line!=null)
        {
            if(line.ToLower().Contains("//codedebt"))
            {
                if (CurrentFolder != file.DirectoryName)
                {
                    CurrentFolder = file.DirectoryName;
                    Log(Level.Warning, "");
                    Log(Level.Warning, CurrentFolder);
                }
                Log(Level.Warning, "\t" + file.Name);
                string[] attributes = line.ToLower().Replace("//codedebt","").Trim().Split('|');
                Log(Level.Warning, "\tLine " + lineNumber.ToString());
                foreach (string s in attributes)
                {
                    Log(Level.Warning, "\t" + s.Trim());
                }
            }
            lineNumber++;
            line = sr.ReadLine();
        }
        sr.Close();
    }
}

STEP 2

Next you need to create a new NAnt build task, here is the code

<?xml version="1.0" encoding="utf-8"?>
<project name="ProjectNameWeb" default="build" xmlns="http://nant.sf.net/release/0.85/nant.xsd">
  <!-- Properties -->
  <property name="trunk.dir" value="..\" />
  <property name="dependencies.dir" value="${trunk.dir}\dependencies" />
 
  <!-- Custom Assemblies -->
  <loadtasks assembly="${dependencies.dir}\CustomBuildTasks\CustomBuildTasks.dll"/>
  <loadtasks assembly="${dependencies.dir}\NCover\NCover.NAntTasks.dll"/>
  <loadtasks assembly="${dependencies.dir}\NCover\NCoverExplorer.NAntTasks.dll"/>
 
  <!-- Called Externally -->
  <target name="findToDo" depends="compile, parseCodeDebt" />
 
  <!-- Coding Tasks -->
  <target name="parseCodeDebt">
  <ParseCodeDebt RootPathToParse="${ProjectNameWeb.dir}" 
    DirectoriesToIgnore=".svn,_ReSharper.ProjectNameWeb" 
    FileTypeToProcess=".cs" />
  <ParseCodeDebt RootPathToParse="${ProjectNameWebTests.dir}" 
    DirectoriesToIgnore=".svn,_ReSharper.ProjectNameWeb" 
    FileTypeToProcess=".cs" />
  </target>
</project>

STEP 3

Go to your log4net output directory and open up the file and your Custom NAnt Build task should of dumped all of the data there

some more text here

Tags: , , , ,

Dec 15 2009

Compile your solution with and without MSBuild via CruiseControl.NET

Category: Continuous Integrationryancmartin1976 @ 20:57

First Strategy:

Second Strategy:

Compiling your solution every time subversion has a new commit

There are 2 ways to compile a solution in CruiseControl.NET, with MSBuild and without. I use both, one, without, is very simple process and runs every time a new file is committed to Subversion.

Compiling without MSBuild in ccnet.config

<project name="Build">
  <labeller type="defaultlabeller" />
  <webURL>http://localhost/ccnet/ViewFarmReport.aspx</webURL>
 
  <sourcecontrol type="filtered">
    <sourceControlProvider type="svn" autoGetSource="true">
      <executable>C:\Program Files\VisualSVN Server\bin\svn.exe</executable>
      <trunkUrl>http://domain.com/svn/ProjectName/trunk</trunkUrl>
      <workingDirectory>D:\Projects\compile\ProjectName\trunk</workingDirectory>
      <tagOnSuccess>false</tagOnSuccess>
      <tagBaseUrl>http://domain.com/svn/ProjectName/tags</tagBaseUrl>
      <username>rmartin</username>
      <password>********</password>
    </sourceControlProvider>
    <inclusionFilters>
      <pathFilter>
        <pattern>**/*.*</pattern>
      </pathFilter>
    </inclusionFilters>
  </sourcecontrol>
 
  <triggers>
    <intervalTrigger name="continuous" seconds="120" buildCondition="IfModificationExists" />
  </triggers>
 
  <publishers>
    <merge>
      <files>
        <file>C:\Program Files\CruiseControl.NET\server\Build\Artifacts\results.xml</file>
      </files>
    </merge>
    <xmllogger />
    <statistics />
    <email
      from="ProjectNameBuild@domain.com"
      mailhost="mail.domain.com" includeDetails="TRUE">
      <users>
        <user name="Ryan Martin" group="buildmaster" address="rmartin@domain.com"/>
        <user name="Some Guy" group="buildmaster" address="sguy@domain.com"/>
      </users>
      <groups>
        <group name="buildmaster" notification="always"/>
      </groups>
    </email>
  </publishers>
</project>

Compiling with MSBuild in my nant build script

<?xml version="1.0" encoding="utf-8"?>
<project name="ProjectNameWeb" default="build" xmlns="http://nant.sf.net/release/0.85/nant.xsd">
  <!-- Properties -->
  <property name="devserver.website.dir" value="D:\Projects\www\ProjectName"/>
  <property name="trunk.dir" value="..\" />
  <property name="source.dir" value="${trunk.dir}\Source" />
  <property name="msbuild.exe" value="C:\WINDOWS\Microsoft.NET\Framework\v3.5\msbuild.exe" />
  <property name="solution.sln" value="${source.dir}\ProjectNameWeb.sln" />
 
  <!-- Called Externally -->
  <target name="build" depends="compile" />
 
  <!-- Coding Tasks -->
  <target name="compile">
    <exec program="${msbuild.exe}" commandline="${solution.sln} /t:Rebuild /v:q" />
  </target>
</project>

Seeing the results

Launch the CruiseControl.NET from Programs -> CruiseControl.NET -> CruiseControl.NET from the server where you installed CruiseControl.NET. The software will run the tasks in the configuration file you setup in ccnet.config

I recommend using the windows service which runs continuously and does all of the automation for you based off of the interval trigger you setup in the file as well. The interval triggers are task specific.

And lastly you can force builds and view the result in the CruiseControl.NET Web dashboard.

Tags: , , , ,