Issue Tracker part IV: The Build Enviroment using MSBuild (or NAnt)

Since that in the last post we created the solutions for this application, it's now time to setup our build enviroment. This automated build will help us in the proccess of not performing those tedious tasks of copying files from one side to another, zipping, etc, tasks that some times leads us to errors.

For this job we will use MSBuild. Since both MSBuild and NAnt use, most of the times, the same syntax, converting will be easy for the NAnt lovers.

Our build proccess will:

  • Clean up the Release folder;
  • Run Code Coverega Reports and Unit tests;
  • Build the Marvin.Core project:
    • Generate the build version;
    • using the previous generated build version we will update the AssemblyInfo.cs file;
    • Deploy the generated assembly to the Dependecies folder;
  • Build the Marvin.Website project:
    • First we need to copy the Marvin.Core generated assemply to the Bin folder, this because the aspcompiler dosen't update the references (for what I know. If you know a way, please tell me :) ).
    • The build will deploy the website into the Release\Application folder;
  • Generate Documentation;
  • Zip the Application into the Maintenance folder:
    • the zip will contain both deploy and source code;

In both applications, the build proccess is defined using a xml structure file. Each step of the build is defined using the Task element in both MSBuild and NAnt. Tasks are grouped by Targets in MSBuild and NAnt.

A Project (NAnt and MSBuild) is a set of Targets.

When executing a build proccess, in both NAnt and MSBuild, we can tell wich of the target to execute. If none is suplied, the default target, defined in the Project element, is called. Targets can depend on other targets, so "You might have a target for compiling, for example, and a target for creating a distributable.  You can only build a distributable when you have compiled first, so the distribute target depends on the compile target." (NAnt definition).

This said, the build proccess will be structured like this:

  • Project (the Release is the default target)
    • Clean target
      • Delete task
    • Code Coverage and Unit Tests
      • NCover task
    • Core target
      • Version task
      • Assembly task
      • Build task
      • Copy task
    • Website Target (depends on Clean and Core targets)
      • Build Task
    • Documentation
      • NDoc task;
    • Zip Target
      • Create Version Folder task
      • Zip task
    • Release Target (Depends on the website and zip targets)

The Release target dosen't have any task, but we will use it only when we need to release a new version of our application, so it will have addictional dependencies such as code coverage, unit tests and documentation.

NOTE: We will not cover in this topic the proccess of Continuous Integration with such tools as the CruiseControl.Net. For more on this please visit the CruiseControl.Net website, and check out both the  Using CruiseControl.NET with MSBuild and Part 1: Continuous Integration using MSBuild, CruiseControl.NET, FxCop, NUnit, NCover + Subversion articles. 

Building with MSBuild

MSBuild is very similar to NAnt, so if you already have worked with NAnt, changing to MSBuild will be a easy task.

Has said before, the build proccess is defined using a xml document. So in our Scripts folder we create a xml file named marvin.msbuild:

<?xml version="1.0" encoding="utf-8"?>

A build proccess need a Project element, where we set the default target if none is supplied to the MSBuild executable, so:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Release" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

If we have values that can be duplicated along the script, we can create define them on a PropertyGroup element, and them reference them later on.

We call a property be using the $(PropertyName) syntax.

<PropertyGroup>
    <Property1>false</Property1>
    <Property2>c:</Property2>
    <Property3>$(Property2)\debug</Property3>
</PropertyGroup>

For our project we need values for:

  • the tools directory;
  • Project name;
  • Release directory;
  • Source directory;
  • Dependencies directory;
  • Core directory;
  • Core Project file;
  • Website directory;
  • Website deploy directory;
<PropertyGroup>
	<ToolsDirectory>..\..\..\Tools</ToolsDirectory>
	<ProjectName>Marvin</ProjectName> 
	<ReleaseDirectory>..\Release</ReleaseDirectory>
	<SourceDirectory>..\Source</SourceDirectory>
	<DependenciesDirectory>..\Dependencies</DependenciesDirectory>
	<CoreDirectory>$(SourceDirectory)\Marvin.Core\Marvin.Core</CoreDirectory>
	<CoreProjectFile>$(CoreDirectory)\Marvin.Core.csproj</CoreProjectFile>
	<WebSiteDirectory>$(SourceDirectory)\Marvin.Website</WebSiteDirectory>
	<WebSiteReleaseDirectory>$(ReleaseDirectory)\Application</WebSiteReleaseDirectory>
</PropertyGroup>

Although the properties are defined in our application build file, generic cross-application properties (and all other elements allowed) can be set in a external file, and referenced in.

Properties like company name and tasks like sending emails, that don't depend on the project, can be placed in this external file. All we have to do is Import the file:

<Import  Project="$(ToolsDirectory)\default.targets"/>

In our case the default.targets will not only define the company name and email, but also import custom tasks, this because while MSBuild delivers a large number of common tasks, not always they do what we want, so  when the existing ones don't do what we need, we can always write one, or check the community, and particullary, the MSBuild Community Tasks Project, that we will use in our build proccess.

The default.targets inside the Tools directory looks (for now) like this:

<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<PropertyGroup>
		<MSBuildCommunityTasksPath>.\</MSBuildCommunityTasksPath>
		<CompanyName>BrunoFigueiredo.com</CompanyName>
		<CompanyEmail>notreallymyemail@someboguscompany.com</CompanyEmail>
		<Copyright>Copyright (c) $(CompanyName) 2007</Copyright>
	</PropertyGroup>
	<Import Project=".\MSBuild.Community.Tasks\Build\MSBuild.Community.Tasks.Targets"/>
</Project>

For more details on the MSBuild Community Tasks Project, please visit their site.

Now that we have setup the properties to use, we will write our first target: Clean the release folder.

Every time we want to release a new version of our application, the Release folder must first be clean up. Since the Delete task dosen't accepts wildcards it its declaration, we can:

  • Create a ItemGroup containing the list of files/folders to delete, or...
  • ... execute a command-line delete.

We will use a ItemGroup with all the folders we want to remove . This allows us to move this target to the default.targets while mantaining the ItemGroup on the project script file.

So in the default.targets we will have:

<Target Name="CleanUp">
	<RemoveDir Directories="@(RemoveFolders)" />
</Target>

While the marvin.msbuild inside the Scripts folder of our application has, for now:

<ItemGroup>
	<RemoveFolders Include="$(WebSiteReleaseDirectory)" />
</ItemGroup>

Our first target is done. Easy right? Let's move on then. Next we need to build the Marvin.Core project. We have now several tasks to perform:

The first one is to update the build version on the AssemblyInfo.cs file. For this we will use two MSBuild Community Tasks Project tasks: the Version and the AssemblyInfo taks.

The Version task updates a textfile containing the version, while the AssemblyInfo task recreates the assemblyinfo.cs file in the Marvin.Core project with the new generated version:

<Target Name="Version">
	<Version VersionFile="version.txt" BuildType="Increment" RevisionType="None">
		<Output TaskParameter="Major" PropertyName="Major" />
		<Output TaskParameter="Minor" PropertyName="Minor" />
		<Output TaskParameter="Build" PropertyName="Build" />
		<Output TaskParameter="Revision" PropertyName="Revision" />
	</Version>
	<Message Text="Version: $(Major).$(Minor).$(Build).$(Revision)"/>
	<AssemblyInfo CodeLanguage="CS"  
			OutputFile="$(CoreDirectory)\Properties\AssemblyInfo.cs" 
			AssemblyTitle="$(ProjectName)" 
			AssemblyDescription="$(ProjectDescription)"
			AssemblyConfiguration=""
			AssemblyCompany="$(CompanyName)"
			AssemblyProduct="$(ProjectName)"
			AssemblyCopyright="$(Copyright)"
			AssemblyTrademark=""
			ComVisible="false"
			CLSCompliant="true"
			Guid="$(ProjectGuid)"
			AssemblyVersion="$(Major).$(Minor).$(Build).$(Revision)" 
			AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)" />
</Target>

Now that we have updated the version and the assemblyinfo file, it now time to compile.  

<Target Name="Core" DependsOnTargets="Version">
	<MSBuild Projects="$(CoreProjectFile)" Targets="ReBuild" Properties="Configuration=Release">
		<Output ItemName="Outputs" TaskParameter="TargetOutputs"/>
	</MSBuild>
	<Copy
		SourceFiles="@(Outputs)"
		DestinationFolder="$(DependenciesDirectory)"
	/>
</Target>

 The Core target depends on the Version target, so it runs this first. After that the MSBuild task compiles our assembly. We use the Output element to create a new property named Outputs containing all the files generated by the MSBuild task. With this property we can next Copy them to the Dependencies folder.

Alright, we have cleaned up the Release folder, generated a new version, updated the AssemblyInfo file, compiled and copied the Marvin.Core assembly.

That just leaves us with the Application target (for now).

For compiling the Marvin.Website we will use the AspNetCompiler task. Since we have a reference to the Marvin.Core in the Marvin.WebSite project, we first need to copy the core assembly to the Bin directory and then compile:

<Target Name="Application" DependsOnTargets="Core">
	<Copy
		SourceFiles="@(Outputs)"
		DestinationFolder="$(WebSiteBinDirectory)"
	/>
	<AspNetCompiler 
		VirtualPath="/$(ProjectName)"
		PhysicalPath="$(WebSiteDirectory)"
		TargetPath="$(WebSiteReleaseDirectory)"
		Force="true"
		Debug="false"
		Updateable="true"
	/>
</Target>

Thats it. We are done, at least for now.

I leave you with the full default.targets and marvin.msbuild:

Default.Targets
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<PropertyGroup>
		<MSBuildCommunityTasksPath>.\</MSBuildCommunityTasksPath>
		<CompanyName>BrunoFigueiredo.com</CompanyName>
		<CompanyEmail>notrealymyemail@someboguscompany.com</CompanyEmail>
		<Copyrights>Copyright (c) $(CompanyName) 2007</Copyrights>
	</PropertyGroup>
	<Import Project=".\MSBuild.Community.Tasks\Build\MSBuild.Community.Tasks.Targets"/>
	
	<Target Name="CleanUp">
		<RemoveDir Directories="@(RemoveFolders)" />
	</Target>
</Project>
Marvin.msbuild
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Release" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<PropertyGroup>
		<ToolsDirectory>..\..\..\Tools</ToolsDirectory>
		<ProjectName>Marvin</ProjectName> 
		<ProjectDescription>$(ProjectName) Issue Tracker</ProjectDescription>
		<ProjectGuid>d038566a-1937-478a-b5c5-b79c4afb253d</ProjectGuid>
		<ReleaseDirectory>..\Release</ReleaseDirectory>
		<SourceDirectory>..\Source</SourceDirectory>
		<DependenciesDirectory>..\Dependencies</DependenciesDirectory>
		<CoreDirectory>$(SourceDirectory)\Marvin.Core\Marvin.Core</CoreDirectory>
		<CoreProjectFile>$(CoreDirectory)\Marvin.Core.csproj</CoreProjectFile>
		<WebSiteDirectory>$(SourceDirectory)\Marvin.Website</WebSiteDirectory>
		<WebSiteBinDirectory>$(SourceDirectory)\Marvin.Website\Bin</WebSiteBinDirectory>
		<WebSiteReleaseDirectory>$(ReleaseDirectory)\Application</WebSiteReleaseDirectory>
	</PropertyGroup>
	
	<ItemGroup>
		<RemoveFolders Include="$(WebSiteReleaseDirectory)" />
	</ItemGroup>
	
	<Import  Project="$(ToolsDirectory)\default.targets"/>
			
	<Target Name="Release" DependsOnTargets="CleanUp;TestsCoverage;Application;Documentation;Maintenance" />
		
	<Target Name="Version">
		<Version VersionFile="version.txt" BuildType="Increment" RevisionType="None">
			<Output TaskParameter="Major" PropertyName="Major" />
			<Output TaskParameter="Minor" PropertyName="Minor" />
			<Output TaskParameter="Build" PropertyName="Build" />
			<Output TaskParameter="Revision" PropertyName="Revision" />
		</Version>
		<Message Text="Version: $(Major).$(Minor).$(Build).$(Revision)"/>
		<AssemblyInfo CodeLanguage="CS"  
				OutputFile="$(CoreDirectory)\Properties\AssemblyInfo.cs" 
				AssemblyTitle="$(ProjectName)" 
				AssemblyDescription="$(ProjectDescription)"
				AssemblyConfiguration=""
				AssemblyCompany="$(CompanyName)"
				AssemblyProduct="$(ProjectName)"
				AssemblyCopyright="$(Copyright)"
				AssemblyTrademark=""
				ComVisible="false"
				CLSCompliant="true"
				Guid="$(ProjectGuid)"
				AssemblyVersion="$(Major).$(Minor).$(Build).$(Revision)" 
				AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)" />
	</Target>
	
	<Target Name="TestsCoverage">
	</Target>
	
	<Target Name="Core" DependsOnTargets="Version">
		<MSBuild Projects="$(CoreProjectFile)" Targets="ReBuild" Properties="Configuration=Release">
			<Output ItemName="Outputs" TaskParameter="TargetOutputs"/>
		</MSBuild>
		<Copy
			SourceFiles="@(Outputs)"
			DestinationFolder="$(DependenciesDirectory)"
		/>
	</Target>
	
	<Target Name="Application" DependsOnTargets="Core">
		<Copy
			SourceFiles="@(Outputs)"
			DestinationFolder="$(WebSiteBinDirectory)"
		/>
		<AspNetCompiler 
			VirtualPath="/$(ProjectName)"
			PhysicalPath="$(WebSiteDirectory)"
			TargetPath="$(WebSiteReleaseDirectory)"
			Force="true"
			Debug="false"
			Updateable="true"
		/>
	</Target>
	
	<Target Name="Documentation">
	</Target>
	
	<Target Name="Maintenance">
	</Target>
</Project>

Writing a Issue Tracker part III: Creating the Solutions

While in the two previous posts we talked about the main ideia and how the project will be strutured, it's now time to create our solution.

The Solution - Marvin the issue tracker

"Marvin" (application code name), will be strucured in three projects:

  • Marvin.Core, that makes the application ticks;
  • Marvin.Website, that is what we will use as UI;
  • Marvin.Core.Tests, for unit and mock testing;

For the developement of this application we will be using the Microsoft Visual Studio Web Developer Express Edition for the website, and the Microsoft Visual Studio C# 2005 Express Edition for both the Core and Tests development. You can get both application freely here.

The Website project

So after starting the VS WebDevelopment, we will create a new Asp.NEt Web Site:

image

 

The Core project

All of the Marvin logical functionality will be handled by the Marvin.Core assembly. So we startup the Msft Visual Studio C# 2005 Express Edition and create a new Class Project:

image

 

The Tests project

 It's always nice to know that when we change on line of code we are not creating other bugs. So unit testing will help us on this matter. It's not 100% accurate, but it will help us finding early bugs. So will add a new Class Project to the previous created solution:

image

 

image

 

For the unit testing we'll be using the NUnit framework. Since this is a dependency for our project we first need to copy the nunit.framework.dll assembly from our Tools\NUnit\Bin folder to the Marvin\Dependencies folder.

After that, we add the this assembly reference to the Tests project:

image

 

All done...

... and in next post I'll cover the build enviroment setup using MSBuild. I will also show how to accomplish the same task using NAnt.


It seems that they are real


Writing a Issue Tracker part II: folder structure

 

image Folowing the first post on this topic, and regarding the folder structure, I'm using the How To: Structure Your Source Control Folders in Team Foundation Server guidance, with some "minor" changes.

Dependencies

All extenal references needed by this project will land here. In this case, assemblies like log4net.dll, RssToolkit.dll will live here.

Maintenance

The maintenance folder keeps all the old versions in case we need to change something on a production enviroment that has a old version.

Release

The lastest version of our application is kept here, for easy deployment. It also has the documentation, unit tests and code coverage reports.

Scripts

The nant scripts reside in here.

Source

Well, this seems obvious.

Tools

This folder shouldn't bellong to the application, but rather to the development enviroment. In where we will keep tools needed for the development of our applications. This should be keept updated with the latest versions.

You may be asking: "Why do you also keep the log4net and the AspnetRssToolkit also on this folder? why not reference to where instead?"

I choosed to copy the references to the project because I could be developing with a different version. In this particular project this will probably not happen, but when working on multiple solutions in a team enviroment, it is best, in my opinion, to keep the project references in a solution folder. This way you know (or at least expect :) )that the project will build with that set of assemblies.

 

Any opinions on this structure?


Writing a Issue tracker part I: The main ideia

For a long time I've been wishing to build a open-source web 2.0 application using all kind of free and open-souce code available on the internet.

So after some thinking, I came to the ideia of building a issue tracking system.

It's a relative simple application to build, and I can use a bunch of open-source code to add cool functionality to the application.

So, I'm thinking of using:

I'll use the Provider Model for the authentication and data access. This will facilitate the later usage, for instance, of the  amazing SubSonic for the data layer.

This will also help us if we later on change the authentication provider, since I don't yet know if I can easily integrate the OpenID or even the Google Account into a Provider Model, this because of the redirects made, as you can see in the diagram bellow:

 

The workflow of the application itself is simple:

The "client" user:

First, the user logins to the application. After that, the application will show all projects assign to the user, and their tasks. The user can review, close, reopen and create new tasks. A issue (or task) is defined by a project, workid, title, description, severity, status, release date and version.

The "programmer" user:

The programmer user does all a "client" user, but we is the only one how fills the release date and version.

The "administrator" user:

The administrator is the only one that can create projects and assign users to projects.

 

So, there you have it.  If you have any ideia, suggestion, or are willing to participate, go on and drop me an email.

[UPDATE]

Decided to add two more tools to the package:

Another thing I forgot to mention is that one of the main objective of this project is to learn more about these tools. So all post will have reference links that helped me understand the usage of the tools used.


Changed to BlogEngine.net

Well, as you can see i've change the look of the blog. With this change I also change the blog engine to the BlogEngine.net.

I will be improving the user interface, so please be pacient if any of the bookmarks don't work. I'm working on it.

Thanks. 


Moodgets: Widgets for mootools

 I've been using mootools for some time, and over this time I've developed some widgets to facilitate the daily work. I'm thinking of building a website to share all of the  widgets developed by me and by the community... what do you think?

 

  • EditInPlace
  • MultiView
  • Template Engine (to be released soon)
  • SelectBinder (to be released soon)
  • fxCache (to be released soon)

Hope you find them useful.

Any sugestions, comments and bug report is much appreciated.


About Me

I'm a Senior Consultant in the work, and a geek outside of it :P

Recent posts

Recent comments