Vítejte na blog.vyvojar.cz Přihlásit | Registrovat | Pomoc

Team Build a automatické verzování

Simple description of one way how to setup automatic versioning and Team Build (Team Foundation Server 2005), including WiX

Krátký popis jak se dá nastavit automatické verzování na Team Build Serveru (Team Foundation Server 2002), včetně změny verze pro WiX.

Popis je rozdělen do několika částí popisující jednotlivé kroky. Není je však potřeba provádět přesně v popsaném pořadí, některé lze i vynechat. V popisu je zdůvodněno proč se co dělá, ale není to úplný návod.

Všechny projekty v solution mají vložen odkaz na soubor pojmenovaný SolutionVersion.cs, který je součástí solution a je při automatickém sestavení vždy vytvořen s novým číslem verze. Obsahuje tedy jen řádek s AssemblyVersion atributem.

Při automatickém sestavení se mění číslo sestavení (Major.Minor.Build.Revision), přičemž číslo revize je nastaveno na 0. Pro vývojáře je v TFS repozitory je číslo revize nahrazeno znakem '*', takže vývojářské verze se liší od produkční. Informace o verzi jsou ukládány do souboru version.txt, který je součástí solution. V něm je možné měnit čísla verzí Major, Minor ručně a při dalším automatickém sestavení budou použity.

Jednotlivá automatická sestavení jsou pojmenována ve tvaru <jméno>_<verze>, místo výchozího pojmenování pomocí datumů.

Protože používám WiX pro vytváření instalátorů, tak je popsána i možnost verzování WiX projektů. A výsledkem celého snažení je pouze již hotový MSI balíček.

Co je potřeba

Celé je to postaveno tak, aby vývojářské PC nepotřebovalo žádné speciální nastavení. Takže všechno musí být na stroji, který provádí samotný build. Vlastně stačí jenom MSBuild Community Tasks.

A samozřejmě vytvořený Team Build. Na jeho jméně nezáleží, protože skoro všechno je vytvořeno variabilně. Co není, na to zvlášť upozorním.

Předpokládá se, že v adresáři solution jsou sobory version.txt a SolutionVersion.cs. Dále, že solution obsahuje WiX projekt s názvem "Setup", který obsahuje soubor version.template pro vytvoření souboru Version.wxi s verzí pro MSI balíček.

Jak to funguje

V před vlastním sestavením se stáhne z TFS repozitory soubor s poslední verzí. Tato verze se zvedne o 1. Následně se vytvoří soubory s verzemi pro jednotlivé projekty (cs soubor pro projekty v C# a wxi soubor, který je vložen do souboru pro WiX, ze kterého je vytvořen MSI balíček.

Po dokočení kompilací je v souboru cs provedena náhrada čísla revize a všechny vytvořené a změněné verzovací soubory jsou vráceny do TFS repozitory. Tím je zajištěno že všichni vývojáři budou mít nová čísla verzí.

Krok 1: Editace TFSBuild.proj

Celé nastavení automatického buildu je v souboru TFSBuild.proj, uloženém na serveru v adresáři TeamBuildTypes\<jméno buildu>. Tento soubor je vlastně msbuild projektový soubor, který build server vykoná.

Tento soubor je potřeba nejprve pomocí příkazu "Get latest version" stáhnout na lokální disk a pomocí "Check-out" uvolnit pro editaci a otevřít, klidně rovnou ve Visual Studiu, ale stačí i notepad.

Krok 2: Import MSBuild  Community Tasks

Aby fungovali přidané msbuild tasky, je nutné je do projektového souboru naimportovat. Najdeme tedy řádek. kde je proveden import Team Build tasků (najít řádek na kterém je tag "Import") a za něj přidáme nový:

  <Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />

Krok 3: Cesta k tf.exe

Protože budeme potřebovat tahat soubory z Team Serveru a žádná rozumná cesta v msbuildu není, tak si pomůžeme spouštěním řádkového příkazu tf.exe. Cestu k tomuto příkazu zadáme někam mezi PropertyGroup:

<!-- TF.exe -->
<TF>&quot;c:\Program Files\Microsoft Visual Studio 8\Common7\IDE\TF.exe&quot;</TF>

Krok 4: Pojmenování sestavení

Na začátku cyklu sestavení se vytváří jméno sestavení. Toho je využito a nejen, že je jméno změněno na více popisné, než je výchozí, tak jsou také naplněny proměnné verzí. MSBuild target se jmenuje BuildNumberOverrideTarget.

<Target Name="BuildNumberOverrideTarget" Condition="'$(IsDesktopBuild)'!='true'">
 
    <MakeDir
        Directories="$(SolutionRoot)"
        Condition="!Exists('$(SolutionRoot)')" />
 
    <!-- Delete the workspace left by previous run -->
    <DeleteWorkspaceTask
        TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
        Name="$(WorkspaceName)" />
 
    <!-- Create the workspace enlistment -->
    <CreateWorkspaceTask
        TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
        MappingFile="$(WorkspaceMappingFile)"
        LocalPath="$(SolutionRoot)"
        Name="$(WorkspaceName)"
        TeamProject="$(TeamProject)" />
 
    <!-- Get the latest version source from the workspace -->
    <Get
        Condition=" '$(SkipGet)'!='true' "
        Workspace="$(WorkspaceName)"
        Recursive="$(RecursiveGet)"
        FileSpec="$(SolutionRoot)\Source\version.txt"
        Force="$(ForceGet)" />
 
    <!-- Workaround: Version task must have read/write access -->
    <Attrib Files="$(SolutionRoot)\Source\version.txt" ReadOnly="false" />
 
    <!-- Get setup build number -->
    <Version VersionFile="$(SolutionRoot)\Source\version.txt" 
        BuildType="Increment" RevisionType="BuildIncrement">
      <Output TaskParameter="Major" PropertyName="BuildVersionMajor" />
      <Output TaskParameter="Minor" PropertyName="BuildVersionMinor" />
      <Output TaskParameter="Build" PropertyName="BuildVersionBuild" />
      <Output TaskParameter="Revision" PropertyName="BuildVersionRevision" />
    </Version>
 
    <!-- Create BuildNumber property value, 
         if you want "better" default naming convetions
         you may delete it -->
    <CreateProperty Value="$(BuildType)_$(BuildVersionMajor).$(BuildVersionMinor).$(BuildVersionBuild).$(BuildVersionRevision)" >
      <Output TaskParameter="Value" PropertyName="BuildNumber" />
    </CreateProperty>
 
</Target>

Krok 5: Vytváření verzovacích souborů

Všechny změny v souborech vytvořených v BuildNumberOverrideTarget jsou přepsány v okamžiku kdy msbuild vytáhne kompletní zdrojové kódy pro překlad. Hned poté vytvoříme soubory s verzemi. SolutionVersion.cs se vytváří 2x. Nejprve s '*' místo revize a hned se všechno uloží zpět do TFS repozitory. Potom se revize nastaví na 0 se kterou je provedena kompilace.

<Target Name="AfterGet" Condition="'$(IsDesktopBuild)'!='true'">
 
    <!-- Check out version files -->
    <Exec Command="$(TF) checkout &quot;$(SolutionRoot)\Source\version.txt&quot; &quot;$(SolutionRoot)\Source\Setup\version.wxi&quot; &quot;$(SolutionRoot)\Source\SolutionVersion.cs&quot;" WorkingDirectory="$(SolutionRoot)" />
 
    <!-- Set file setup build number -->
    <WriteLinesToFile
       File="$(SolutionRoot)\Source\version.txt"
       Lines="$(BuildVersionMajor).$(BuildVersionMinor).$(BuildVersionBuild).$(BuildVersionRevision)"
       Overwrite="true" />
 
    <!-- Create tokens for wxi file replace -->
    <CreateItem Include="Major" AdditionalMetadata="ReplacementValue=$(BuildVersionMajor)">
      <Output TaskParameter="Include" ItemName="TokenMajor"/>
    </CreateItem>
    <CreateItem Include="Minor" AdditionalMetadata="ReplacementValue=$(BuildVersionMinor)">
     <Output TaskParameter="Include" ItemName="TokenMinor"/>
    </CreateItem>
    <CreateItem Include="Build" AdditionalMetadata="ReplacementValue=$(BuildVersionBuild)">
      <Output TaskParameter="Include" ItemName="TokenBuild"/>
    </CreateItem>
    <CreateItem Include="Revision" AdditionalMetadata="ReplacementValue=$(BuildVersionRevision)">
      <Output TaskParameter="Include" ItemName="TokenRevision"/>
    </CreateItem>
 
    <CreateItem Include="@(TokenMajor);@(TokenMinor);@(TokenBuild);@(TokenRevision)">
      <Output TaskParameter="Include" ItemName="Tokens"/>
    </CreateItem>
 
    <!-- Replace tokens and create version.wxi -->
    <TemplateFile Template="$(SolutionRoot)\Source\Setup\Version.template" 
        OutputFilename="$(SolutionRoot)\Source\Setup\Version.wxi" 
        Tokens="@(Tokens)" />
 
    <!-- Solution version file, change CodeLanguage for VB -->
    <AssemblyInfo CodeLanguage="CS"
        OutputFile="$(SolutionRoot)\Source\SolutionVersion.cs"
        AssemblyVersion="$(BuildVersionMajor).$(BuildVersionMinor).$(BuildVersionBuild).*" />
 
    <!-- Check in version files -->
    <Exec Command="$(TF) checkin &quot;$(SolutionRoot)\Source\version.txt&quot;  &quot;$(SolutionRoot)\Source\Setup\version.wxi&quot; /comment:&quot;Setup version $(BuildVersionMajor).$(BuildVersionMinor).$(BuildVersionBuild)&quot; &quot;$(SolutionRoot)\Source\SolutionVersion.cs&quot; /noprompt /override:BUILDSERVER " WorkingDirectory="$(SolutionRoot)" />
 
    <!-- Workaround: Version task must have read/write access -->
    <Attrib Files="$(SolutionRoot)\Source\SolutionVersion.cs" 
        ReadOnly="false" />
    <!-- Now generate version file with Revision = 0 -->
    <AssemblyInfo CodeLanguage="CS"
        OutputFile="$(SolutionRoot)\Source\SolutionVersion.cs"
        AssemblyVersion="$(BuildVersionMajor).$(BuildVersionMinor).$(BuildVersionBuild).$(BuildVersionRevision)"
        AssemblyFileVersion="$(BuildVersionMajor).$(BuildVersionMinor).$(BuildVersionBuild).$(BuildVersionRevision)" />
 
</Target>

Krok 6: Zabalení výsledků kompilace

Nepovinný krok, pouze pro jistotu zabalím všechny *.dll, *.pdb do zipu a smažu je, potřebujeme jenom MSI balíček. Vlastně je ani balit nemusím a rovnou je smazat. Build server potom všechno přesune do cílového adresáře. A to je vlastně vše.

<Target Name="PackageBinaries"
    Condition="'$(IsDesktopBuild)'!='true'">
 
    <!-- Zip and delete assemblies (without MSI) -->
    <CreateItem Include="$(OutDir)\**\*.*" Exclude="$(OutDir)\**\*.msi">
      <Output ItemName="FilesToDelete" TaskParameter="Include" />
    </CreateItem>
 
    <!-- package (you may remove this line and simply delete files) ... -->
    <Zip Files="@(FilesToDelete)" WorkingDirectory="$(OutDir)" 
        ZipFileName="$(OutDir)\$(BuildNumber).zip" />
 
    <!-- ... and delete -->
    <Delete Files="@(FilesToDelete)" />
 
</Target>

Soubory

Protože všechny zmíněné soubory musí existovat, je nutno je vytvořit:

  • version.txt
1.0.0.0
  • SolutionVersion.cs
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
 
[assembly: AssemblyVersion("1.0.0.*")]
  • version.template
<?xml version="1.0" encoding="utf-8"?>
<Include>
    <?define ProductMajor = "${Major}" ?>
    <?define ProductMinor = "${Minor}" ?>
    <?define ProductBuild = "${Build}" ?>
    <?define ProductRevision = "${Revision}" ?>
    <?define ProductVersion = "${Major}.${Minor}.${Build}" ?>
</Include>
  • version.wxi
<?xml version="1.0" encoding="utf-8"?>
<Include>
    <?define ProductMajor = "1" ?>
    <?define ProductMinor = "0" ?>
    <?define ProductBuild = "0" ?>
    <?define ProductRevision = "0" ?>
    <?define ProductVersion = "1.0.0" ?>
</Include>

Trocha WiXu na závěr

Jen malá ukázka jak potom vypadá wxs soubor. Je to jenom začátek ...

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
 
    <?include Version.wxi ?>
 
    <Product Id="*" Name="Program $(var.ProductVersion)" 
             Language="1033"
            Version="$(var.ProductVersion)" Manufacturer="Me"
            UpgradeCode="{AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAA0}">
        <Package InstallerVersion="200" Compressed="yes" />
 
        <Upgrade Id='{AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAA1}'>
            <UpgradeVersion OnlyDetect='yes' Property='PATCHFOUND'
              Minimum='$(var.ProductVersion)' IncludeMinimum='yes' 
              Maximum='$(var.ProductVersion)' IncludeMaximum='yes' />
            <UpgradeVersion OnlyDetect='yes' Property='NEWERFOUND'
              Minimum='$(var.ProductVersion)' IncludeMinimum='yes' />
            <UpgradeVersion OnlyDetect='no' Property='UPGRADE'
              Maximum='$(var.ProductVersion)' IncludeMinimum='no'/>
        </Upgrade>
...atd
Zveřejněno 1. října 2008 15:51 by arci
Vedeno pod: ,

Komentář

1. října 2008 20:18 by mjurek

# re: Team Build a automatické verzování

Pekne cviceni :-)

Kouknete sem http://www.codeplex.com/freetodevtasks, najdete tam tasky pro Checkin/Checkout, ale netroufnu si hodnotit jejich kvalitu.

6. října 2008 22:36 by arci

# re: Team Build a automatické verzování

Díky, už jsem to nastavoval u druhého projektu, tak jsem si udělal poznámky.

Díval jsem se na ty tasky. Vnitřně se volá tf.exe. Vlastně jenom hezky zabalili volání tf.exe co mám já. On buildserver nějaké tasky pro čtení zdrojáků ze serveru má, ale nenašel jsem dokumentaci :-)

Neregistrovaní uživatele nemužou přidávat komentáře.
 
Vyvojar.cz na prodej!