Um welche Version der Software handelt es sich denn?
Diese Frage wird spätestens dann gestellt, wenn die Software den Entwickler-PC verlässt und von Endanwendern getestet wird. Beim Reproduzieren und fixen von Fehlern ist die Angabe einer Verionsnummer und die Rückverfolgbarkeit zu dem passenden Quellcode-Stand eine zentrale Information. Doch wie werden Versionsnummern richtig vergeben?
Aufbau der Versionsnummern
Die klassische Versionsnummer untergliedert sich in vier Stellen: <Major>.<Minor>.<Build>.<Rev>: Die Haupt- bzw. Nebenversionsnummer des Produktes (Major und Minor Version) werden in der Major und Minor Stelle gespeichert und klassischerweise vom Produkt Management oder der Marketing-Abteilung vorgegeben. Die Buildnummer wird in der Stelle Build gespeichert. In der vierten Stelle (Rev) wird die Revisionsnummer gespeichert.
Mögliche Versionierung
Wichtig bei der Versionierung ist die Generierung einer streng monoton wachsenden und dadurch eindeutigen Versionsnummer. Weiterhin ist zu beachten, dass Installer nur die Major, Minor und Build Stelle einer Datei überprüfen, um zu bestimmen, ob eine Datei bei einem Update ersetzt werden muss.
Die Major und Minor Stelle der Versionsnummer des Produkts sollte von außen vorgegeben werden (Zum Beispiel über Parameter in der Build Definition). Die Build Stelle der Versionsnummer sollte auf das Datum des aktuellen Builds gesetzt werden. Zusammen mit dem in der Revisionsstelle gespeicherten Build des Tages ergibt sich eine monoton wachsende Versionsnummer.
Diese Vergabe der Versionsnummer stellt jedoch nur eine Möglichkeit dar. Eine weitere Variante wäre die semantische Versionierung. Auf die Vor- und Nachteile der verschiedenen Varianten wird an dieser Stelle jedoch nicht weiter eingegangen, da der Fokus auf der Integration in den Buildprozess liegt.
Setzen von Versionsnummern
Die Assembly- und File-Version wird grundsätzlich in der AssemblyInfo.cs festgelegt. Je nach Aufbau der Software kann so für jede Assembly eine separate Version, oder durch die Nutzung einer GlobalAssemblyInfo.cs eine gemeinsame Version, vergeben werden. Das Vergeben der Versionsnummer sollte Grundsätzlich im Buildprozess geschehen, um lokale Builds eindeutig von Server-Builds unterscheiden zu können. Das Grundprinzip besteht nun darin, eine Buildnummer zu erzeugen, welche die Versionsnummer wiederspiegelt. Basierend auf der Buildnummer kann in einem Build-Task die aktuelle Versionsnummer durch die Buildnummer ersetzt werden.
Für die Major- und Minor-Version wird, um sie manuell vergeben zu können, eine Variable im Buildprozess definiert. Die Werte für <Build> und <Rev> werden über bereits vorhandene Variablen im Buildprozess zusammengesetzt. Das Zusammensetzen der <Build>-Stelle kann aus dem Jahr und der Tagesnummer erfolgen. Der Wertebereich von 65535 wird somit erst im Jahr 2066 überschritten. Die resultierende Buildnummer ist nachfolgend dargestellt.
$(MajorVersion).$(MinorVersion).$(Year:yy)$(DayOfYear)$(Rev:.rr)
Um die Versionsnummer in der AssemblyInfo patchen zu können, ist ein zusätzlicher Schritt in der Builddefinition nötig. Auf dem Visual Studio Marketplace existiert hierfür eine Extension mit dem passenden Build-Task. Durch die Extension Colin’s ALM Corner Build & Release Tools steht nach der Installation der Build-Task Version Assemblies zur Verfügung.
Diese Variante ist jedoch erst ab dem TFS 2015 Update 2 verfügbar. Das Anpassen der Inhalte der AssemblyInfo ist jedoch auch einfach über ein PowerShell-Skript möglich.
# Regular expression pattern to find the version in the build number
# and then apply it to the assemblies
$VersionRegex = "\d+\.\d+\.\d+\.\d+" # Get and validate the version data
$VersionData = [regex]::matches($Env:BUILD_BUILDNUMBER,$VersionRegex)
$NewVersion = $VersionData[0] # Apply the version to the assembly property files
$files = gci $Env:BUILD_SOURCESDIRECTORY -recurse |
?{ $_.PSIsContainer } |
foreach { gci -Path $_.FullName -Recurse -include GlobalAssemblyInfo.* } foreach ($file in $files) {
$filecontent = Get-Content($file)
attrib $file -r
$filecontent -replace $VersionRegex, $NewVersion | Out-File $file
Write-Warning "$file.FullName - version applied"
}
Das komplette Skript steht hier zum Download zur Verfügung. Je nach Anwendungsfall ist der Regex-Ausdruck und der Filter für die AssemblyInfo-Datei anzupassen. Das Skript muss anschließend nur noch in der Versionsverwaltung abgelegt, und in der Builddefinition aufgerufen werden.
Somit wird jede Assembly im Buildprozess mit einer eindeutigen Versionsnummer versehen und kann jederzeit zum Build und zum Quellcode zurückverfolgt werden.