Versionsnummer mit CruiseControl.NET automatisch in AssemblyInfo.cs eintragen

Written on July 29, 2009

"Welche Version verwenden Sie denn?"

Wenn man einen Fehler in einer .NET Assembly sucht, ist es natürlich wichtig zu wissen, welche Version der DLL man vor sich hat.

Mit gutem Beispiel vorangehen

Umso wichtiger ist es natürlich auch, dass man Assemblies, die man selbst erstellt, mit einer nachvollziehbaren Versionsnummer versieht.

Eine Versionsnummer besteht in der Regel aus einer Hauptversion, einer Nebenversion, einer Build-Nummer und einer Revisionsnummer und sieht z.B. so aus:

1.2.0.317

Die Versionsnummer von .NET Anwendungen befindet sich in der Datei Properties\AssemblyInfo.cs des jeweiligen Projekts.

Relevant für die Versions-Info der DLL ist der Eintrag

[assembly: AssemblyVersion( "1.0.0.0" )]

Dieser Eintrag kann von Hand gepflegt werden, z.B. nach jedem Commit in eine Versionsverwaltung wie Subversion.

"Sollen wir uns darum auch noch kümmern?"

Allerdings kommt man sehr schnell an den Punkt, an dem man die Aktualisierung vergisst oder bewußt unterlässt, da der Aufwand schnell wachsen kann.

Besonders bei der Entwicklung im Team ist die Pflege von Hand praktisch nicht praktikabel.

Außerdem möchte man z.B. die Versionsnummer nur erhöhen, wenn der Build erfolgreich war.

Die Automatisierung der Versionierung stellt allerdings kein Problem dar, wenn man Continuous Integration verwendet (siehe auch grüner Grad bei Clean Code Developer) -- z.B. mit dem kostenlosen CI-Server CruiseControl.NET (CC.NET).

Für CC.NET gibt es sog. Labeller, die einen Build, der mit CC.NET erzeugt wurde, eindeutig kennzeichnen können.

Versionsnummern leicht gemacht mit svnrevisionlabeller

Verwendet man als Versionsveraltung Subversion (SVN) zusammen mit CC.NET, kann man o.g. Versionsnummernschema erzeugen, indem man den svnrevisionlabeller von David Keaveny verwendet.

Dieser generiert nach einem frei definierbaren Schema eine Versionsnummer, die er CC.NET übergibt, welcher den jeweiligen Build dann mit diesem Label versieht.

Wie kommt man nun zu einem solchen Label mit svnrevisionlabeller?

Nach dem Download der entsprechendenVersion(vor CC.NET 1.4.3 oder danach), kopiert man die

ccnet.SvnRevisionLabeller.plugin.dll in das Unterverzeichnis

server

der CC.NET-Installation.

Danach ergänzt man in der ccnet.config (nicht ccnet.exe.config) im gleichen Verzeichnis den -Eintrag um einen Child-Node, der wie folgt aussieht:

<labeller type="svnRevisionLabeller">       <url>svn://svn.meinprojekt.de/meinprojekt/trunk/PfadZumCode</url>       <major>0</major>       <minor>0</minor>
  </labeller>

Nach einem Neustart der ccnet.exe oder des CC.NET-Dienstes, werden die Builds ab sofort mit dem neuen Versionsschema gekennzeichnet.

"Welche Version haben wir denn nun?"

Wirft man allerdings einen Blick auf die generierten Assemblies des Builds, stellt man fest, dass die Versionsinfo hier noch nicht erscheint.

Der Grund hierfür ist, dass der CC.NET-Versionslabel nur innerhalb von CC.NET verwendet wird.

Abhilfe schafft hier ein MSBuild-Task, der vor dem eigentlichen Build die Datei AssemblyInfo.cs mit den Infos aus dem CC.NET-Versionslabel aktualisiert.

Der besagte MSBuild-Task ist in den sog. MSBuild Community Tasks enthalten, die man z.B. in ein Verzeichnis

"Tools"

im Projekt-Baum kopiert.

Im Build-Skript wird die MSBuild Communit Tasks Assembly nun importiert ($(MSBuildCommunityTasksPath) verweist auf das Tools-Verzeichnis):

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

Den AssemblyInfo-Task bindet man dann als Target in das Build-Skript ein und definiert die Parameter entsprechend:

<Target Name="BeforeBuild">   <AssemblyInfo Condition="'$(CCNetLabel)' != ''" CodeLanguage="CS" OutputFile="$(ProjectDir)\Properties\AssemblyInfo.cs" AssemblyTitle="Meine Assembly" AssemblyCompany="Meine Firma" AssemblyProduct="Mein Projekt" AssemblyCopyright="Copyright ©  2009" ComVisible="false" Guid="fb6d275e-96df-4ea5-b046-d77a3557c1a2" AssemblyVersion="$(CCNetLabel)" AssemblyFileVersion="$(CCNetLabel)" />

  </Target>

Wenn man nun das Target "BeforeBuild" noch entsprechend in die Reihenfolge des Builds (per CallTarget) integriert, stehen nach dem nächsten Build die aktuellen Informationen in den erzeugten Assemblies.