Fake it... ale nie tak jak myślisz - Build - Run Unit Tests - Publish
Posty z tej serii:
- Fake it… ale nie tak jak myślisz - NServiceBus Web Host
- Fake it… ale nie tak jak myślisz - ASP.NET Web Host
- Fake it… ale nie tak jak myślisz - NServiceBus Windows Service Host
- Fake it… ale nie tak jak myślisz - Build - Run Unit Tests - Publish
Witam Cię w ostatniej części serii, w której opisuję, w jaki sposób zrealizowałem mechanizmy wdrażania nowych wersji Systemu komentarzy na blogu, wykorzystując do tego narzędzie FAKE. Trzy poprzednie artykuły dotyczyły wgrywania artefaktów ze zmianami. W tym artykule przejdziemy przez funkcjonalności przygotowania kodu do wdrożenia.
Design
Cały proces składa się z kroków:
- Pobranie źródeł z systemu kontroli wersji
- Kompilacja kodu
- Uruchomienie testów jednostkowych
- Przygotowanie do wdrożenia Endpointa BlogComments, za pomocą komendy dotnet publish
- Przygotowanie do wdrożenia komponentu Web, za pomocą tej samej komendy
dotnet publish
W tym momencie natrafiamy na klasyczny element do rozstrzygnięcia - w jaki sposób podzielić kod. Co powinno być razem, a co osobno. Pisałem o tym trochę w drugiej części. Strategia, która w takich sytuacjach pomaga mi podjąć decyzję, zwłaszcza wtedy, kiedy od razu nie widać rozwiązania, polega na rozważaniu dwóch skrajnych podejść:
- Maksymalnie wszystko uwspólnić
- Maksymalnie wszystko rozdzielić
W ten sposób zwiększam swoją szansę na zauważenie plusów oraz minusów. Następnie bazując na tej wiedzy, podejmuję decyzję.
W pierwszym przypadku mógłbym zakodować wszystko w jednym Targecie w jednym pliku .fsx. Plus byłby taki, że miałbym wszystko w jednym miejscu i od razu wiedziałbym, w którym miejscu wprowadzać ewentualne zmiany. Minusem mogłoby być to, że z każdą zmianą musiałbym testować cały proces od początku do końca.
W drugim przypadku mógłbym umieścić każdy krok w osobnym Targecie, a każdy Target w osobnym pliku .fsx. Plusem byłoby to, że mógłbym wprowadzać zmiany niezależnie dla każdego kroku. Minus byłby taki, że miałbym kod w różnych miejscach.
Jeśli udało Ci się zrealizować ćwiczenie opisane w drugiej części, to zauważysz, że mamy tutaj taką samą sytuację, a mianowicie plusy pierwszego podejścia stają się automatycznie minusami podejścia drugiego i odwrotnie.
Jeśli żadne z podejść mi nie pasuję, to przechodzę do drugiej iteracji, patrząc, co mogłoby być razem, a co osobno itd.
Ostatecznie wyszło mi, że najbardziej pasującą opcją jest zakodowanie poniższych kroków w jednym pliku .fsx:
- Target o nazwie Compile Code:
- Pobranie źródeł z systemu kontroli wersji
- Kompilacja kodu
- Target o nazwie Run Unit Tests:
- Uruchomienie testów jednostkowych
Krok przygotowania do wdrożenia za pomocą komendy dotnet publish
pasuje, aby był w osobnym pliku .fsx jako jeden Target o nazwie Publish Artifacts, który w parametrze przyjmuję ścieżkę do projektu, który ma zostać opublikowany.
Develop
Mając zaprojektowane rozwiązanie, możemy przejść do implementacji. Na początek kompilacja kodu oraz testy jednostkowe np. w pliku o nazwie build.fsx:
Standardowo w powyższym kodzie pominąłem elementy opisane w poprzednich częściach tej serii. Całość implementacji możesz zobaczyć na moim GitHubie.
Elementem startowym jest uruchomienie testów jednostkowych, które zależne są od sukcesu kompilacji kodu. Kod pobierany jest z Gita za pomocą funkcji clone
należącej do modułu Repository
, który udostępniany jest przez FAKEa. Następnie uruchamiana jest kompilacja za pomocą funkcji build
należącej do modułu DotNet
, który również dostarcza FAKE.
Kiedy dotarłem do momentu kompilacji kodu, wpadła mi nowa wiedza z języka F#. W poprzedniej implementacji, zakodowanej w PowerShellu, jawnie podawałem konfiguracją Release. W implementacji z wykorzystaniem funkcji DotNet.build
chciałem uzyskać taki sam efekt. Sygnatura funkcji w piątej wersji FAKEa wygląda tak:
Co oznacza, że funkcja przyjmuje dwa parametry:
- Pierwszy parametr jest funkcją, która przyjmuje parametr typu
BuildOptions
i zwraca wartość typuBuildOptions
- Drugi parametr to ścieżka do projektu/solucji do kompilacji
Funkcja nie zwraca żadnej wartości.
Pierwsza myśl, jaka przyszła mi do głowy, to podejście obiektowe. Podstawię pod zmienną BuildOptions.Configuration
nową wartość udostępnianą przez FAKEa - DotNet.BuildConfiguration.Release
. Efekt? Błąd kompilacji z informacją:
This field is not mutable
No tak. W F# domyślnie wszystkie już utworzone wartości są niezmienne. Poza tym BuildOptions
nie jest klasą, tylko typem Record, który nie występuję np. w wersji ósmej języka C#. Research w internecie naprowadził mnie na nową konstrukcję języka F# - Copy and Update Record Expressions. Połączenie tej konstrukcji z możliwością tworzenia funkcji anonimowych okazało się tym, czego potrzebowałem. Fragment kodu:
oznacza utworzenie anonimowej funkcji z parametrem p
, który jest typu BuildOptions
, z ciałem funkcji, w której za pomocą słowa kluczowego with
podmieniany jest Record field o nazwie Configuration
na wartość DotNet.BuildConfiguration.Release
. Drugim parametrem funkcji DotNet.build
jest ścieżka do pliku .sln.
Uruchomienie testów jednostkowych odbywa się poprzez wywołanie funkcji DotNet.test
, gdzie test
jest nazwą funkcji, a DotNet
modułem udostępnianym przez FAKEa. Wywołanie funkcji odbywa się za pomocą tej samej konstrukcji, którą opisałem w powyższym akapicie.
Przejdźmy teraz przez implementację publikacji, umieszczając kod np. w pliku publish.fsx:
Zgodnie z założeniami projektowymi, jeden Target odpowiada za publikację, która odbywa się poprzez wywołaniem funkcji publish
należącej do modułu DotNet
. Funkcja przyjmuje konfigurację pod postacią rekordu BuildOptions
oraz ścieżkę do projektu, który ma zostać opublikowany. Tym razem w opcjach podmieniamy trzy wartości:
Configuration
- tak, jak poprzednio, ustawiamy wersję ReleaseNoBuild
- mamy już zbuildowany kod, więc nie trzeba powtarzać tej operacjiRuntime
- jeśli chcemy opublikować kod pod konkretne środowisko np. Windows
Test & Deploy
Stworzyliśmy dwa skrypty FAKEa. Do przygotowania do wdrożenia całego Systemu komentarzy potrzebne są trzy skrypty PowerShell. Pierwszy np. run_build.ps1 uruchamia kompilację oraz testy jednostkowe:
Drugi np. run_publish_blogcomments.ps1 publikuje kod Endpointa BlogComments:
Trzeci np. run_publish_web.ps1 publikuje kod komponentu Web:
Kod uruchamiany jest tak samo, niezależnie od tego, czy artefakty mają być wdrażane na środowisko testowe, czy produkcyjne.
W ten oto sposób dotarliśmy do końca serii, w której podzieliłem się z Tobą moimi doświadczeniami przy realizacji funkcjonalności CI/CD z wykorzystaniem narzędzia FAKE. Samo narzędzie posiada o wiele więcej możliwości. Jeśli chcesz sprawdzić w praktyce, jak działa mechanizm, wdrożony za pomocą skryptów, z którymi zapoznałeś/zapoznałaś się w tej serii, zostaw komentarz pod tym artykułem. Jestem ciekaw, jakich narzędzi używasz do realizacji procesów CI/CD dla funkcjonalności, którymi się zajmujesz.
=