Semantic Versioning (of kortweg SemVer) is een versiebeheerschema voor software dat driedelige versienummers vastlegt in de vorm <major>.<minor>.<patch>, zoals 1.0.2, met een optioneel prerelease achtervoegsel in de vorm -<prerelease>, zoals in 1.0.2-beta.

SemVer is vandaag de dag misschien wel het meest gebruikte versiebeheerschema. Bijvoorbeeld, zowel Nuget en npm bevelen het aan en ondersteunen het, en VS Code gebruikt het ook.

In de meeste GitHub repo’s die de GitHub Releases functie gebruiken om releases te publiceren, zou je een SemVer versienummer zien in de laatste release badge op de startpagina, zoals te zien is in de schermafbeelding hieronder:

Ik moet vaak een SemVer versienummer instellen bij het bouwen van ASP.NET Core API projecten, en dit dan lezen of rapporteren tijdens runtime.

Bijvoorbeeld, als ik een minimaal API bouw met een versie ingesteld op 1.0.2-beta, zou dit gemeld worden door een /version endpoint dat door de API wordt blootgegeven, zoals te zien is in de onderstaande screenshot van Hoppscotch (dit is een Postman-achtig hulpmiddel met het gemak dat het in de browser draait):

Het controleren dat de versie die gemeld wordt door geïmplementeerde services, zoals webapps en APIs, correct is, is een cruciaal onderdeel van mijn CD-pijplijn en is een van de smoke tests die ik gebruik om te bepalen of een implementatie geslaagd is.

Een kleine complicatie bij het instellen van een SemVer-versienummer op .NET-assembly’s is dat .NET oorspronkelijk vierdelige versienummers zoals 1.0.3.212 gebruikte en assembly’s hebben deze nog steeds (assembly is de .NET-term voor eenheden van code gecompileerd tot .NET bytecode, de meest typische hiervan zijn dll’s en exe’s).

Daarnaast heeft .NET niet één, maar veel verschillende, iets verschillende versienummers die aanwezig zijn in dezelfde assembly.

In dit artikel laat ik je zien hoe je deze eigenaardigheden omzeilt en een SemVer-versienummer op een .NET-assembly tijdens de bouw stempelt. Dat wil zeggen, op een gecompileerde .exe of .dll, en hoe je het tijdens de uitvoering leest.

Inhoudsopgave

Structuur van een SemVer Versienummer

Overweeg een SemVer versienummer zoals 1.0.2 of 1.0.2-beta. Het heeft de vorm <major>.<minor>.<patch><prerelease>

Dit is wat de verschillende componenten betekenen:

Het <major>-component van het versienummer zou alleen worden verhoogd als de nieuwe release een bestaande (meest recente) release zou breken.

In geval van een UI-app kunnen klanten bedoeld worden als menselijke klanten. Dus als de nieuwe release de bestaande middelen van gebruikers zoals workflowdefinities zou breken, zou dit een verhoging van het hoofdversienummer rechtvaardigen. In dit geval, als de vorige release 1.0.2 was, zou de nieuwe release 2.0.0 moeten zijn (alle lagere componenten van het versienummer worden gereset).

In geval van een bibliotheek, zoals een bibliotheekpakket op Nuget of NPM, zijn de klanten ander code. Dus als de nieuwe release bestaande klantcode zou breken, dat wil zeggen dat het niet achterwaarts compatibel is met zijn eigen vorige versie, dan zou opnieuw het <major>-component worden verhoogd.

<minor> wordt verhoogd als er nieuwe functies zijn toegevoegd, maar de nieuwe versie nog steeds achterwaarts compatibel is. Dus van 1.0.2 zou je gaan naar 1.1.0.

<patch> wordt verhoogd wanneer een nieuwe release moet worden uitgebracht, zelfs als er geen brekende wijziging is en geen nieuwe functionaliteit is toegevoegd. Dit kan bijvoorbeeld gebeuren als er een bugfix moest worden uitgebracht.

-<prerelease> achtervoegsel is optioneel. Het wordt meestal toegevoegd aan een driedelig versienummer wanneer software beschikbaar moet worden gesteld tijdens prerelease-testfases zoals alpha en beta. Bijvoorbeeld, voordat je versie 1.0.2 van je software algemeen beschikbaar maakt, kun je het beschikbaar stellen aan je bètatesters als 1.0.2-beta.

Het <prerelease>-component kan eigenlijk elke string zijn die u kiest en de enige vereiste is dat het een alphanumeric identifier is zoals beta of 12 of alpha2 (geen tekens andere dan cijfers of letters van het alfabet) of meerdere alphanumeric identifiers gescheiden door een punt (.), bijvoorbeeld development.version.

De Veel Version Numbers van een .NET Assembly

Zoals Andrew Lock’s artikel over .NET versies uitlegt, heeft een .NET assembly niet één, maar verschillende versienummers:

  • AssemblyVersion: Dit is een vierdelig versienummer, bijvoorbeeld 1.0.2.0. Het wordt door de runtime gebruikt bij het laden van gekoppelde assemblies.

  • FileVersion: Dit is het versienummer dat wordt gerapporteerd voor een .dll-bestand in Windows Verkenner wanneer u met de rechtermuisknop op de assembly klikt en Eigenschappen selecteert.

  • InformatieveVersie: Nog een versienummer en, net als FileVersion, zichtbaar in het dialoogvenster Eigenschappen als je met de rechtermuisknop op de assembly in Windows klikt en Eigenschappen selecteert. Dit kan strings bevatten en niet alleen integers en punten waar AssemblyVersion en FileVersion aan zijn gebonden.

  • PakketVersie: Als het project een NuGet-pakket is, zou dit het versienummer van het pakket zijn waar de assembly deel van uitmaakt.

Allen deze versienummers worden tijdens de编译 als metadata in de assembly geplaatst. Je kunt ze zien als je de assembly inspecteert met JetBrains dotPeek (gratis) of Red gate Reflector (niet gratis) of soortgelijke tools.

FileVersion en InformatieveVersie kunnen ook worden gezien in het tabblad Details van het dialoogvenster Eigenschappen dat verschijnt wanneer je met de rechtermuisknop op het assemblybestand in Windows Verkenner klikt en Eigenschappen selecteert:

In de screenshot hierboven is “Productversie” de bijschrift voor InformationalVersion, terwijl “Bestandsversie” de bijschrift is voor FileVersion.

Van de vier soorten versienummers die hierboven beschreven worden, zijn alleen de eerste drie van toepassing op elke assembly (dus of de assembly wel of geen onderdeel is van een Nuget-pakket).

Van die drie, voegt AssemblyVersion altijd een 0 toe in de vierde plaats als je probeert een SemVer-versie in te stellen die slechts drie getallen heeft (plus een optionele prerelease-achtervoegsel). Bijvoorbeeld, als je probeert een SemVer-versie van 1.0.2-beta in te stellen tijdens de bouw en vervolgens de AssemblyVersion-waarde leest tijdens het draaien in de assembly, zou het 1.0.2.0 zijn.

FileVersion doet hetzelfde, zoals te zien is in de screenshot hierboven.

InformationalVersion is de enige versienummer die exact wordt ingesteld op de serverversie die je tijdens de bouw hebt ingesteld, zoals de screenshot hierboven laat zien.

Daarom is InformationalVersion de versie die tijdens het draaien gelezen moet worden om de SemVer-versie van de assembly te verkrijgen.

Hoe een SemVer Versienummer Instellen

Er zijn twee dingen die je moet doen om een SemVer-versienummer in te stellen op een assembly tijdens de bouw.

Eerst, in een <PropertyGroup> element in het project’s csproj bestand, voeg het element <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion> toe:

<PropertyGroup>
 ...
 <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion> 
</PropertyGroup>

Zoals beschreven in dit probleem, zorgt dit ervoor dat InformationalVersion exact wordt ingesteld op het SemVer-versienummer dat we hebben opgegeven en er wordt geen +<hash code> achteraan toegevoegd.

Tweede, geef het versienummer door als waarde van de Version-eigenschap die wordt doorgegeven aan de dotnet build-opdracht bijvoorbeeld:

dotnet build --configuration Release -p Version=1.0.2-beta

Dit zou InformationalVersion in de gecompileerde assembly (.exe of .dll-bestand) instellen op 1.0.2-beta.

Overigens zou het AssemblyVersion en FileVersion (een extra 0 zou worden toegevoegd aan het einde van 1.0.2) ook instellen, maar daar zijn we niet geïnteresseerd.

Merk op dat in plaats van het Version-argument op de opdrachtregel door te geven, u de MS Build-eigenschap <Version>1.0.2-beta</Version> kunt instellen in een <PropertyGroup>-element in het csproj-bestand. Het doorgeven van een waarde van het Version-parameter aan de dotnet build-opdracht is echter eenvoudiger omdat het csproj-bestand niet hoeft te worden aangepast elke keer dat het versienummer wordt verhoogd. Dit is nuttig in CD-pijplijnen. Ook hebben csproj-bestanden standaard geen eigenschap die gerelateerd is aan versiebeheer.

Hoe een Assembly’s SemVer Versie tijdens Uitvoering te Lezen

De code die InformationalVersion tijdens uitvoering leest, is als volgt:

string? version = Assembly.GetEntryAssembly()?.
  GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.
  InformationalVersion;

In mijn minimal APIs, om een /version endpoint toe te voegen zoals getoond in het inleidinggedeelte hierboven, plaats ik het bovenstaande fragment in Program.cs, en voeg ik het volgende fragment onmiddellijk erna toe. Merk op dat het hele ding voor builder.Build() wordt aangeroepen:

//dit object van een anonieme type zal 
//ge序列化 als JSON in het response body
//wanneer geretourneerd door een handler
var objVersion = new { Version = version ?? "" };

//ANDER CODE
//var app = builder.Build()

Na builder.Build() wordt aangeroepen, maak ik de handler voor het /version endpoint:

app.MapGet("/version", () => objVersion);

Nu, wanneer ik het API-project uitvoer en het /version endpoint aanroep, krijg ik het versienummer terug in een JSON-object in de HTTP response body:

{
  "version": "1.0.2-beta"
}

Dit is wat de Hoppscotch-screenshot in de inleiding liet zien.

Conclusie

Dit artikel heeft je laten zien hoe je een SemVer-versienummer instelt in je .NET-assembly’s, bibliotheken of apps.

Het heeft je ook laten zien hoe je het versienummer tijdens de uitvoering kunt lezen.