Nikt nie lubi nudnych i powtarzalnych czynności. Dlatego staramy się automatyzować je tak bardzo jak to możliwe a zaoszczędzony czas spędzać bardziej produktywnie.
Jednym z takich zadań jest zarządzanie numerami wersji oprogramowania. Kiedy robimy to „ręcznie”, jest to nudne zadanie i łatwo o pomyłkę. Kiedy jest robione „z automatu”, przestajemy się tym przejmować.
W każdym projekcie przychodzi chwile kiedy trzeba zdecydować się na to jak będzie on wersjonowany, jaki format numeracji wybrać itp. Czasami rozwiązaniem jest ręczne nadawanie numerów wersji, czasami używany jest numer rewizji repozytorium Git. Dobrze zaprojektowana numeracja powinna być czytelna i ułatwiająca pracę (np. nie powodować zamieszania która wersja aplikacji jest aktualnie testowana)
Dzisiaj zobaczymy jak zlecić zadanie automatycznego generowania numerów wersji naszemu serwerowi CI/CD. Wykorzystamy do tego celu TeamCity oraz tagi repozytorium Git.
Naszym celem jest opracowanie systemu automatycznego generowania wersji spełniających kryteria:
- Numery kolejnych build-ów się nie powtarzają
- Każdy numer jednoznacznie identyfikuje build oraz pozwala określić rewizje Git
- Proces jest niezależny od języka programowania ani innych narzędzi wykorzystywanych w projekcie
- Wymaga minimalnych akcji ze strony zespołu deweloperskiego
Git
Dla potrzeb automatycznego generowania numerów wersji wykorzystamy strategicznie umieszczone tag-i.
Gdzie:
- gałąź
develop
zawiera kod na następy planowany release. - gałęzie
release
są tworzone zdevelop
(tworzone są przed release-m jako baza dla QA, łatania błędów itp…) - gałęzie
hotfix
są tworzone z gałęzirelease
w ramach potrzeb - zmiany na gałęziach
release
ihotfix
są merge-owane dodevelop
Zaznaczone tag-i oznaczają aktualnie wytwarzaną wersję. Wszystkie commit-y które nastąpiły po nadaniu tag-u należą do tej wersji.
Tag nadawany jest w commit-cie następującym zaraz po stworzeniu gałęzi release
. Dzięki temu commit-y na branch-u release
będą należały do poprzedniej wersji a commit-y na branch-u develop
do następnej wersji.
Branch hotfix
są wersjonowane niezależnie od innych, żeby nie wprowadzać zamieszania.
Tworzenie tag-u w Git:
git tag 1.0.0 git push && git push --tags
Podsumowując, w celu określenia do której wersji należy dany commit, musimy cofać się po strzałkach, pierwszy commit na który natrafimy określa nam numer wersji.
Żeby określić numer wersji użyjemy następującego polecenia:
git describe --tags --abbrev=0 HEAD
które zwróci nam nazwę najnowszego tag-a do którego możemy dotrzeć z aktualnego commit-u.
Konfiguracja TeamCity
Mając repozytorium Git z nadanymi tag-ami (wymagany jest przynajmniej jeden), możemy przejść do konfiguracji kroków w build-planie TeamCity.
Pierwszą rzeczą do ustawienia jest Checkout policy. Musimy zmienić ustawienie na Do not use mirrors.
w przeciwnym przypadku TeamCity będzie używało kopii naszego repozytorium (ang. mirror) gdzie nie będziemy mieli dostępu do tag-ów.
Następnie możemy przejść do konfiguracji build-planu. Stworzymy nowy krok typu Command Line z następującym skryptem:
LAST_TAG=$(git describe --tags --abbrev=0 %build.vcs.number%) echo "Tag: ${LAST_TAG:-0.0.0}" echo "##teamcity[buildNumber '${LAST_TAG:-0.0.0}.%build.counter%']"
Gdzie:
LAST_TAG=$(git describe --tags --abbrev=0 %build.vcs.number%)
Tworzy zmienną LAST_TAG
która przechowuje numer wersji (wynik działania polecenia git describe
). Zmienna %build.vsc.number%
określa hash aktualnej rewizji Git.
W następnej linii wypisujemy zawartość zmiennej na konsole (jeżeli będzie pusta, użyjemy domyślnej wartości 0.0.0
).
echo "Tag: ${LAST_TAG:-0.0.0}"
I na końcu
echo "##teamcity[buildNumber '${LAST_TAG:-0.0.0}.%build.counter%']"
używamy wbudowanej funkcji TeamCity do podmiany zmiennych i parametrów w trakcie trwania build-u. TeamCity czyta logi i jeżeli zauważy ##teamcity[
, wykona polecenie – w tym przypadku zmianę numeru aktualnego build-u. Więcej informacji można znaleźć w oficjalnej dokumentacji TeamCity: https://www.jetbrains.com/help/teamcity/service-messages.html
W naszym przypadku wartość buildNumber
zostanie ustawiona na ${LAST_TAG:-0.0.0}.%build.counter%'
gdzie LAST_TAG
zawiera numer wersji wziętą z repozytorium a %build.counter%
jest wbudowanym parametrem TeamCity określającym aktualny build (wartość zwiększa się o 1 za każdym nowym build-em).
Jeżeli nasze repozytorium zawiera inne tag-i oprócz tych określających wersje, możemy użyć parametru --match
filtrującego tag-i:
LAST_TAG=$(git describe --abbrev=0 --match [0-9].[0-9].[0-9] %build.vcs.number% --tags)