Automatyczne numerowanie buildów i paczek NuGeta
W tym poście kontyuuję temat z poprzedniego, gdyż ostatnio wzbogaciłem swój proces generowania paczek o niezwykle istotną funkcję, a mianowicie wersjonowanie plików binarnych i paczek nugeta. Początkowo numer paczki był nadawany na podstawie release notes, zaś dllki nie były wersjonwane w ogóle. Tak oczywiście być nie może, nie tylko dlatego, że kiedyś mam zamiar wypuścić Pizzę w świat. Nawet testując paczki lokalnie, sam się nieraz gubię w tym, którą wersję testuję, czy prawidłowo się zaktualizowała, i czy zawiera pożądane zmiany. Nie da się tego stwierdzić, jeśli po każdym buildzie paczka ma ten sam numer, a dllki na stałe mają wpisane 1.0.0.0
.
Jak numerować wersje?
Ogólnie przyjętym w świecie .NET wzorcem jest [major].[minor].[build].[revision]
. Czym jest revision
? Założenia są takie, że binarki z róznymi numerami revision
mogą być stosowane zamiennie. Po co więc ten numerek? Moim zdaniem nie ma on za bardzo sensu. Kolejne pytanie, które można sobie zadać - kiedy powinny się zmieniać poszczególne cyfry? Zapewne są jakieś wytyczne Microsoftu w tej kwestii, ale niespecjalnie chcę je nawet poznawać. Od razu wolę wybrać sensowne rozwiazanie, czyli semantic versioning.
Ponieważ obecnie Pizza jest w fazie alfa i API zmienia się jak szalone, to do odwołania major
będzie miał wartość 0
, zaś paczki nugeta muszą informować o tym, że nie są jeszcze niestabilne. A zatem wymagania odnośnie wersjonowania można zdefiniować tak:
- Wszystkie binarki i paczki miały zawsze tę samą wersję.
- Pliki binarne miały numer w formacie:
[major].[minor].[patch].[build]
. - Paczki nugeta miały numer w formacie:
[major].[minor].[patch]-alpha[build]
. [major].[minor].[patch]
były jak dotąd odczytywane z release notes. Nic w tym dziwnego - zmiana numeru wersji musi być świadomą i przede wszystkim trzeźwą decyzją programisty.[build]
- ma być automatycznie zwiększany po każdym buildzie. (Buildzie przez skrypt Fake, nie prez Visual Studio.)
Implementacja przy użyciu FAKE
Co jest potrzebne, aby zrealizować powyższe wymagania?
Po pierwsze odczyt ostatniego numeru builda
W normalnej sytuacji zajmuje się tym serwer CI, ale że ja go nie mam, to mam dwa wyjścia - albo oddzielny plik, albo wykorzystać fakt, że ten numer i tak znajduje się w AssemblyInfo.cs
. We frameworku mam na razie cztery pliki o tej nazwie, więc musiałem wybrać jeden z nich. Wybrałem ten z modułu Pizza.Framework
, ponieważ zakładam, że ma on najmniejsze szanse na zmianę nazwy czy zniknięcie w dającej się przewidzieć przyszłości.
Implementacja w F# jest banalna, wystarczy dodać open Fake.AssemblyInfoFile
i napisać taką funkcję:
Po co tam ten Trim
? Ano dlatego, że GetAttributeValue opakowuje wartość atrybutu typu string
w dodatkowe cudzysłowy. Męczyłem się z tym trochę czasu, gdyż te dodatkowe nawiasy sprawiały, że Version.Parse
rzucało wyjątkiem FormatException
. Straciłem dobry kwadrans zanim załapałem czemu.
Po drugie - utworzenie numeru dla paczki nugetowej
To także banał:
Na wyjściu otrzymamy coś takiego: 0.3.1-alpha0012
. Istotne jest, aby numer buildu (version.Revision
w kodzie powyżej) miał zera wiodące, gdyż NuGet stosuje sortowanie leksykograficzne. Bez zer wiodących uznałby wersję alpha11
za wcześniejszą niż alpha9
.
Po trzecie - wstawienie zaktualizowanego numeru paczki do zależności od innego modułu Pizzy
Jest to zmiana w tej funkcji:
Wcześniej zamiast packageVersion
używana była wartość releaseNotes.NugetVersion
.
Po czwarte - aktualizacja wersji w kodzie generujacym paczkę
Wewnąrz funkcji createPackage
:
Po piąte - dodanie kroku aktualizującego pliki AssemblyInfo
Na szczęście FAKE ma wbudowane funkcje do tego, wystarczy tylko podać odpowiednie wartości.
Dziwne wydaje się to wyznaczanie currentBuildVersion
. Wynika to z faktu, że jeśli version.Revision
nie istnieje w pliku, to jej wartość wynosi -1, a nie 0.
Na końcu wystarczy target UpgradeAssemblyInfos
dać przed BuildAll
i można już cieszyć się z prostego systemu CI dla ubogich. ;)