Het valt me regelmatig op dat versioning van .NET assemblies in CI/CD pipelines niet altijd volledig wordt geautomatiseerd. Ik vind dat altijd ontzettend jammer. Het hele doel van een pipeline is toch om snel en consistent bruikbare pakketjes op te leveren? Dan zou het ook fijn zijn als het juiste versienummer automatisch bepaald kan worden.
Auteur Arno Peters
Dit artikel is gepubliceerd in SDN Magazine 141
In .NET heb je minimaal de ‘AssemblyVersion’ property nodig, die is beperkt tot (maximaal) 4 cijfers: [Major].[Minor].[Patch].[Revision]. Door subjectieve keuzes over de waardes van die 4 cijfers, lukt het vaak niet om een versienummer automatisch te bepalen. Een voorbeeld: “deze change is best groot, dus laten we dan maar het [Minor] cijfer ophogen in plaats van de [Patch]”, zonder dat “grootte” echt goed te meten valt. Of “Marketing wil graag een 12 op de doos, dus laten we dat als [Major] kiezen”. En soms wordt een deel van het versienummer dan wel weer aangevuld met een gegenereerd nummer.
De reden dat me dit stoort, is dat een (deels) handmatig bepaald versienummer ervoor zorgt dat je altijd blijft knoeien met source control en branches. Je moet op het laatste moment vlak voor de release wat in je source zetten, anders klopt het nummer niet, en als je dit op een branch wil doen om iets te kunnen testen en het later wil mergen, krijg je daar weer extra werk van. Een veel voorkomend gevolg is dan weer dat ervoor gekozen wordt om dan maar alleen vanaf de main branch te releasen – een rigide strategie die helemaal niet nodig is.
De oplossing is eigenlijk eenvoudig: maak gebruik van een tweede versienummer! Een ‘AssemblyVersion’ is noodzakelijk, maar dient puur voor de techniek. .NET faciliteert ook een ‘InformationalVersion’ property: een string waarin er een label gezet kan worden om aan gebruikers te tonen. 95, 98, maar ook XP en Vista; als er een commercieel gewenste versie is, kan deze daarin geplaatst worden.
Hiermee kan de ‘AssemblyVersion’ volledig gebruikt worden voor cijfers bepaald vanuit de techniek. Rest de vraag: wat is dan een handig versieschema?
Ik kom daarbij altijd uit op Semantic Versioning (ook wel afgekort tot SemVer). Deze methode gebruikt de eerste 3 posities [Major].[Minor].[Patch] van het versienummer.
Waarom kies ik hiervoor?
Vooral omdat het exacte versienummer in de praktijk niets uitmaakt. Ik kan een heel schema verzinnen gebaseerd op allerlei niet meetbare concepten, maar er is in de praktijk niemand die daar dan wat mee doet. Gebruikers weten zelden meer van hun software dan de commerciële versie, en als developer kijk ik alleen naar technische versienummers als ik een update wil doorvoeren of er iets niet werkt, dan kan ik de juiste documentatie erbij pakken.
Semantic Versioning lost dit heel elegant op door de nummers van een version eigenlijk los van elkaar te zien. Het kijkt niet naar het formaat van de change, maar enkel naar de betekenis van je commit.
- Als je enkel wat bugs fixt, maar niks nieuws toevoegt: [Patch] +1
- Als je wel wat nieuws toevoegt, maar geen breaking changes hebt: [Minor] +1
- Als je breaking changes hebt: [Major] +1
Er wordt niks vastgelegd over HOEVEEL bugfixes, of (breaking) changes je hebt. SemVer helpt je bij het inschatten hoe ingewikkeld een up- of downgrade tussen twee versies zou kunnen worden, zonder dat je direct de documentatie in moet.
Het geheim om dit automatisch in mijn pipelines te kunnen doen zit in de tool GitVersion. Deze tool is voor een heleboel buildplatforms beschikbaar, en kan als stap in een pipeline opgenomen worden. Je maakt vervolgens de betekenis van je commits meetbaar met behulp van een prefix in je commitmessages, en GitVersion doorloopt je commit history om de actuele versie te berekenen. Er is dus geen magie: als developer ben je volledig in control.
GitVersion kan ook overweg met branches en pre-release versies: er kan naast de ‘AssemblyVersion’ ook een suffix bepaald worden (met daarin bijvoorbeeld de branchname) die gecombineerd een unieke waarde oplevert. En die past weer keurig als tekst in de ‘InformationalVersion’, die op dat moment toch niet gebruikt wordt voor commerciële doeleinden.
Op deze manier kan ik zonder zorgen mijn pipeline een correct genummerde release laten produceren, vanaf iedere commit, en vanaf iedere branch.
Nog een tip rondom de vindbaarheid van het versienummer in source control: maak gebruik van commit tagging in je pipeline, op het moment dat je echt een release doet. Dan vind je de juiste commit een stuk vlugger dan via een contentsearch in de history van je repository, EN het kan uiteraard prima opgenomen worden in de pipeline.
Als je meer informatie wil over Semantic Versioning vind je op https://SemVer.org, en voor GitVersion is https://gitversion.net/docs een goed startpunt.