Showing posts with label CI. Show all posts
Showing posts with label CI. Show all posts

2010-03-17

Embedding a TFS Build label in all assemblies

The default TFS build label does not have a xx.xx.xx.xx pattern, but looks like “MyBuild_20070418.1”. So we cant assign the value to the AssemblyVersion or AssemblyFileVersion attribute, however the AssemblyInformationalVersion attribute can be used as it is a string.

   1: <Target Name="AfterGet">
   2:   <!-- Insert build label into assemblies -->
   3:   <Message Text="Modifying AssemblyInfo files under &quot;$(SolutionRoot)&quot;." />
   4:  
   5:   <Message Text="Undo checkout assemblyinfo files, make sure no files are checked out." />
   6:   <Exec Command="&quot;C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\tf.exe&quot; undo *.* /recursive /noprompt"
   7:         WorkingDirectory="$(MSBuildProjectDirectory)\..\sources" ContinueOnError="true"/>
   8:  
   9:   <Message Text="Checkout assemblyinfo files" />
  10:   <Exec Command="&quot;C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\tf.exe&quot; checkout &quot;AssemblyInfo.*&quot; -r"
  11:         WorkingDirectory="$(MSBuildProjectDirectory)\..\sources" ContinueOnError="true"/>
  12:  
  13:   <ItemGroup>
  14:     <AssemblyInfoFilesCs Include="$(SolutionRoot)\**\assemblyinfo.cs" />
  15:   </ItemGroup>
  16:  
  17:   <Message Text="Adding $(LabelName) to %(AssemblyInfoFilesCs.fullpath)" />
  18:   
  19:   <WriteLinesToFile
  20:           File="%(AssemblyInfoFilesCs.fullpath)"
  21:           Lines="[assembly: AssemblyInformationalVersion(&quot;$(LabelName)&quot;)]"
  22:           Overwrite="false"/>
  23: </Target>
  24:  
  25:  
  26:  
  27: <Target Name="AfterCompileSolution">
  28:   <Message Text="Undo checkout assemblyinfo files" />
  29:  
  30:   <Exec Command="&quot;C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\tf.exe&quot; undo *.* /recursive /noprompt"
  31:         WorkingDirectory="$(MSBuildProjectDirectory)\..\sources" ContinueOnError="false"/>
  32: </Target>

With this in place all assemblies (dll, exe), will have a AssemblyInformationalVersion attribute with the label of the TFS Build. So it should be a no-brainer to trace a dll to a point in time in TFS Source Control. Just open the dll in reflector and disassemble the dll.

reflector

An alternative to AssemblyInformationalVersion  would be AssemblyConfiguration, but then we have to use a task that can do a text replace, as it is usually already defined in AssemblyInfo.cs files.

2008-06-18

How to make a self extracting exe for xcopy deployment

Often I think MSI is overkill when all i need to do, is copy some files to a production server.

So how to set up TFS Build Server to Create a exe from a zip file. (Note I use the http://msbuildtasks.tigris.org/ to create the initial zip file).

I found the free http://www.disoriented.com/FreeExtractor/ , you need to download the optional command line version.

(Note I have a Tools folder at the root of my solution)

At the top of the TFSBuild.proj add the following:

<UsingTask TaskName="MSBuild.Community.Tasks.Zip" AssemblyFile="$(SolutionRoot)\Tools\MSBuildCommunityTasks\MSBuild.Community.Tasks.dll" /> 

Afterwards the folder can be zipped:

<CreateItem Include="$(DropLocation)\$(BuildNumber)\Release\**" >     <Output ItemName="ZipFiles" TaskParameter="Include"/>

  </CreateItem>

  <Zip ZipFileName="$(DropLocation)\$(BuildNumber)\deployment.zip" WorkingDirectory="$(DropLocation)\$(BuildNumber)" Files="@(ZipFiles)" /> 

And then use exec to call the MakeSFX:

<Exec Command="$(SolutionRoot)\Tools\FreeExtractor\MakeSFX.exe /nogui /zip=%22$(DropLocation)\$(BuildNumber)\deployment.zip%22 /sfx=%22$(DropLocation)\$(BuildNumber)\deployment.exe%22 /defaultpath=%22%24curdir%24%22 /exec=%22Deployment\AutoDeploy.bat%22 /autoextract /delete" IgnoreExitCode="true"  /> 

All you need to do is make make a bat file called AutoDeploy.bat, and do the xcopy operations from there.

2008-05-21

Using NUnit and NCover with TFS Build

Just in case you are using TFS as a Source Control and Build Server, does not exclude you from using NUnit and NCover.

First we need a MSBuild task for running NCover: NCoverExplorer.Extras-1.4.0.5.zip

Extract the "NCoverExplorer.MSBuildTasks.dll" file to the folder where the "TFSBuild.proj" is located. Add the file to TFS Source Control, and do a check-in.

Since the build server needs NUnit, NCover and NCoverExplorer they should be placed in a folder under source control like "Tools".

All that is left is to modify the modyfy the TFSBuild.proj file.

At the start.

  <!-- Do not edit this -->   <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets" />
<!-- Start add the NCover MS Build Tasks -->     <UsingTask TaskName="NCoverExplorer.MSBuildTasks.NCoverExplorer" AssemblyFile="NCoverExplorer.MSBuildTasks.dll"/>     <UsingTask TaskName="NCoverExplorer.MSBuildTasks.NCover" AssemblyFile="NCoverExplorer.MSBuildTasks.dll"/>     <UsingTask TaskName="NCoverExplorer.MSBuildTasks.NUnitProject" AssemblyFile="NCoverExplorer.MSBuildTasks.dll"/>

<!-- End add the NCover MS Build Tasks -->     <ProjectExtensions>

And in the end

<Target Name="NCover">        <CreateProperty Value="%(ConfigurationToBuild.FlavorToBuild)">
            <Output PropertyName="FlavorToBuildValue" TaskParameter="Value" />        </CreateProperty>        <CreateItem Include="$(BinariesRoot)\**\*Tests.dll">            <Output TaskParameter="Include" ItemName="TestAssemblies" />        </CreateItem>        <NCover          ToolPath="$(BuildDirectory)\Sources\Tools\NCover\"          WorkingDirectory="$(BinariesRoot)\$(FlavorToBuildValue)"          CommandLineExe="$(BuildDirectory)\Sources\Tools\NUnit\nunit-console.exe"          CommandLineArgs="@(TestAssemblies) /xml=$(DropLocation)\$(BuildNumber)\NUnit.xml /labels /nologo"          CoverageFile="$(DropLocation)\$(BuildNumber)\release_test_results.xml"          LogFile="$(DropLocation)\$(BuildNumber)\coverage.log"      />    </Target>     <Target Name="CreateNCoverReport">         <NCoverExplorer           ToolPath="$(BuildDirectory)\Sources\Tools\NCoverExplorer\"           ProjectName="$(TeamProject)"           ReportType="ModuleClassFunctionSummary"           OutputDir="$(DropLocation)\$(BuildNumber)\"           XmlReportName="ncoverreport.xml"           HtmlReportName="ncoverreport.html"           ShowExcluded="False"           SatisfactoryCoverage="80"
          CoverageFiles="$(DropLocation)\$(BuildNumber)\release_test_results.xml"           Exclusions="Assembly=*Tests"   />     </Target>     <Target Name="AfterCompile" DependsOnTargets="NCover;CreateNCoverReport">         <Message Text="AfterCompile target executed."> </Message>     </Target>

In every build there should be a file called ncoverreport.html

2008-03-21

TFS 2008 Build with WiX 3 - Keeping it clean

Its a good practise to keep the Build Box as clean as posible, that is dont install 3rd party dependencies.

To begin with you need to add WiX 3 to your Source Control, most peaple seen to have a "tools" folder or the like.

When Wix has been added, all you need to do is override the MSBuild properties WiX uses.

Check out the TFSBuild.rsp file and add the following.

# This is a response file for MSBuild
# Add custom MSBuild command line options in this file
/property:WixTargetsPath=..\..\..\tools\WiX\bin\Wix.targets
/property:WixTasksPath=WixTasks.dll
/property:WixToolPath=C:\projects\MyProject\Sources\tools\WiX\bin\
/v:diag

The paths might need some tweaking to fit your needs.

2007-10-31

How to insert an assembly version number into a WiX v3 script using TFS

The following blog post shows how to do it, but I though it was a bit clumsy.

First set the product version to a var in the .wxs file:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Product Id="*" Name="PUT-PRODUCT-NAME-HERE" Language="1033" Version="$(var.PRODUCTVERSION)" Manufacturer="PUT-COMPANY-NAME-HERE" UpgradeCode="PUT-GUID-HERE">
    <Package InstallerVersion="200" Compressed="yes" Id="*"/>
    <Media Id="1" Cabinet="WixProject.cab" EmbedCab="yes" />
    <!--The rest of the WiX declaration.-->
  </Product>
</Wix>

Next define the constant on the project.

WiX Project -> Properties -> Compiler Tab -> Define constants -> "PRODUCTVERSION=1.0.0.0;".

Do this for both debug and release, so a local build is always 1.0.0.0.

Is recommended that the WiXProject is not build locally (just remove it from the solution configuration manager).

Now we want TFS to make us a build where all project assemblies are given an incremental version and the MSI is versioned with the same version number.

Install AssemblyInfoTask on the Build Server. (See this blog for a fix for y7k issue.)

In TFSBuild.proj add the following:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="DesktopBuild" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- TO EDIT BUILD TYPE DEFINITION
To edit the build type, you will need to edit this file which was generated
by the Create New Build Type wizard. This file is under source control
needs to be checked out before making any changes.
The file is available at -
$/{TeamProjectName}/TeamBuildTypes/{BuildTypeName}
where you will need to replace TeamProjectName and BuildTypeName with your
Team Project and Build Type name that you created
Checkout the file
1. Open Source Control Explorer by selecting View -> Other Windows -> Source Control Explorer
2. Ensure that your current workspace has a mapping for the $/{TeamProjectName}/TeamBuildTypes folder and 
that you have done a "Get Latest Version" on that folder
3. Browse through the folders to {TeamProjectName}->TeamBuildTypes->{BuildTypeName} folder

4. From the list of files available in this folder, right click on TfsBuild.Proj. Select 'Check Out For Edit...'
Make the required changes to the file and save
Checkin the file1. Right click on the TfsBuild.Proj file selected in Step 3 above and select 'Checkin Pending Changes'
2. Use the pending checkin dialog to save your changes to the source control
Once the file is checked in with the modifications, all future builds using
this build type will use the modified settings
-->
<!-- Do not edit this -->
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v8.0\TeamBuild\Microsoft.TeamFoundation.Build.targets" />

<Import Project="$(MSBuildExtensionsPath)\Microsoft\AssemblyInfoTask\Microsoft.VersionNumber.Targets"/>
<ProjectExtensions>
<!--  DESCRIPTION
The description is associated with a build type. Edit the value for making changes.
-->
<Description>
</Description>
<!-- BUILD MACHINE
Name of the machine which will be used to build the solutions selected.
-->
<BuildMachine>****</BuildMachine>
</ProjectExtensions>
<PropertyGroup>
<!--  TEAM PROJECT
The team project which will be built using this build type.
-->
<TeamProject>****</TeamProject>
<!-- BUILD DIRECTORY
The directory on the build machine that will be used to build the
selected solutions. The directory must be a local path on the build
machine (e.g. c:\build).
-->
<BuildDirectoryPath>****</BuildDirectoryPath>
<!-- DROP LOCATION
The location to drop (copy) the built binaries and the log files after
the build is complete. This location has to be a valid UNC path of the
form \\Server\Share. The build machine service account and application
tier account need to have read write permission on this share.
-->
<DropLocation>\\appfrontend02\TFSBuildDrops\EVServiceBil</DropLocation>
<!-- TESTING
Set this flag to enable/disable running tests as a post build step.
-->
<RunTest>****</RunTest>
<!-- WorkItemFieldValues
Add/edit key value pairs to set values for fields in the work item created
during the build process. Please make sure the field names are valid 
for the work item type being used.
-->
<WorkItemFieldValues>Symptom=build break;Steps To Reproduce=Start the build using Team Build</WorkItemFieldValues>
<!-- CODE ANALYSIS
To change CodeAnalysis behavior edit this value. Valid values for this
can be Default,Always or Never.
Default - To perform code analysis as per the individual project settings
Always - To always perform code analysis irrespective of project settings
Never - To never perform code analysis irrespective of project settings
-->
<RunCodeAnalysis>****</RunCodeAnalysis>
<!-- UPDATE ASSOCIATED WORK ITEMS
Set this flag to enable/disable updating associated workitems on a successful build
-->
<UpdateAssociatedWorkItems>true</UpdateAssociatedWorkItems>
<!-- Title for the work item created on build failure -->
<WorkItemTitle>Build failure in build:</WorkItemTitle>
<!-- Description for the work item created on build failure -->
<DescriptionText>This work item was created by Team Build on a build failure.</DescriptionText>
<!-- Text pointing to log file location on build failure -->
<BuildlogText>The build log file is at:</BuildlogText>
<!-- Text pointing to error/warnings file location on build failure -->
<ErrorWarningLogText>The errors/warnings log file is at:</ErrorWarningLogText>
</PropertyGroup>
<PropertyGroup>
<!-- Assembly version properties. Add others here -->
<AssemblyMajorVersion>1</AssemblyMajorVersion>
<AssemblyFileMajorVersion>1</AssemblyFileMajorVersion>
<!-- TF.exe -->
<TF>&quot;$(TeamBuildRefPath)\..\tf.exe&quot;</TF>
<!-- AssemblyInfo file spec -->
<AssemblyInfoSpec>AssemblyInfo.cs</AssemblyInfoSpec>
</PropertyGroup>
<!-- Set this to non-existent file to force rebuild. -->
<ItemGroup>
<IntermediateAssembly Include="$(SolutionRoot)\foobar.dll"/>
</ItemGroup> 

<ItemGroup> <!-- SOLUTIONS The path of the solutions to build. To add/delete solutions, edit this value. For example, to add a solution MySolution.sln, add following line - <SolutionToBuild Include="$(SolutionRoot)\path\MySolution.sln" />

To change the order in which the solutions are build, modify the order in which the solutions appear below. --> <SolutionToBuild Include="****" /> </ItemGroup> <ItemGroup> <!-- CONFIGURATIONS The list of configurations to build. To add/delete configurations, edit this value. For example, to add a new configuration, add following lines - <ConfigurationToBuild Include="Debug|x86"> <FlavorToBuild>Debug</FlavorToBuild> <PlatformToBuild>x86</PlatformToBuild> </ConfigurationToBuild> The Include attribute value should be unique for each ConfigurationToBuild node. --> <ConfigurationToBuild Include="Release|Any CPU"> <FlavorToBuild>****</FlavorToBuild> <PlatformToBuild>****</PlatformToBuild> </ConfigurationToBuild> </ItemGroup> <ItemGroup> <!--  TEST ARGUMENTS If the RunTest is set to true then the following test arguments will be used to run tests. To add/delete new testlist or to choose a metadata file (.vsmdi) file, edit this value. For e.g. to run BVT1 and BVT2 type tests mentioned in the Helloworld.vsmdi file, add the following - <MetaDataFile Include="$(SolutionRoot)\HelloWorld\HelloWorld.vsmdi"> <TestList>BVT1;BVT2</TestList> </MetaDataFile>

Where BVT1 and BVT2 are valid test types defined in the HelloWorld.vsmdi file. MetaDataFile - Full path to test metadata file. TestList - The test list in the selected metadata file to run.

Please note that you need to specify the vsmdi file relative to $(SolutionRoot) --> <!-- <MetaDataFile Include=" "> <TestList> </TestList> </MetaDataFile> --> </ItemGroup> <ItemGroup> <!--  ADDITIONAL REFERENCE PATH The list of additional reference paths to use while resolving references. For example, <AdditionalReferencePath Include="C:\MyFolder\" /> <AdditionalReferencePath Include="C:\MyFolder2\" /> --> </ItemGroup> <Target Name="AfterGet" Condition="'$(IsDesktopBuild)'!='true'"> <!-- Set the AssemblyInfoFiles items dynamically --> <CreateItem Include="$(SolutionRoot)\**\$(AssemblyInfoSpec)"> <Output ItemName="AssemblyInfoFiles" TaskParameter="Include" /> </CreateItem> <Exec WorkingDirectory="$(SolutionRoot)" Command="$(TF) checkout /recursive $(AssemblyInfoSpec)"/> </Target>

<Target Name="AfterCompile" Condition="'$(IsDesktopBuild)'!='true'"> <!-- Create MSI--> <Exec WorkingDirectory="$(SolutionRoot)" Command="C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe $(SolutionRoot)\SolutionFolder\WixProjectFolder\WixProject.wixproj /target:rebuild /p:Configuration=Release /p:DefineSolutionProperties=false /p:OutputPath=$(OutDir) /p:DefineConstants=%22PRODUCTVERSION=$(MaxAssemblyVersion);%22" Timeout="60000" /> <Copy SourceFiles="$(OutDir)\msiname.msi" DestinationFolder="$(OutDir)" /> <Exec WorkingDirectory="$(SolutionRoot)" Command="$(TF) checkin /comment:&quot;Auto-Build: Version Update $(MaxAssemblyVersion) ***NO_CI***&quot; /noprompt /override:&quot;Auto-Build: Version Update&quot; /recursive $(AssemblyInfoSpec)"/> </Target> <!-- In case of Build failure, the AfterCompile target is not executed. Undo the changes --> <Target Name="BeforeOnBuildBreak" Condition="'$(IsDesktopBuild)'!='true'"> <Exec WorkingDirectory="$(SolutionRoot)" Command="$(TF) undo /noprompt /recursive $(AssemblyInfoSpec)"/> </Target> </Project>

Basically it runs the WixProject in a separate MSBuild process, this also we can create any number of msi files we want with different configurations.

Remember if you use CI, you have to stop an infinet loop because the Build Script does a check-in on the assemblyinfo.cs files.
I use Automaton, that fails if a running build gets a build request. So that work just fine.