Od dłuższego czasu interesowało mnie Continuous Integration w wykonaniu Attlasiana i jego serwera Bamboo.  Do tego doszedł popularny ostatnio Docker, który wydawał mi się bardzo dobrym rozwiązaniem do szybkiego stawiania środowiska potrzebnego do zbudowania aplikacji.

Postanowiłem zatem zrobić prosty proof of concept – żeby zobaczyć jak to wygląda w praktyce.

Nie będę tutaj opisywał jak zainstalować Bamboo czy Docker i docker-compose  czyjak dodać projekt w Bamboo – odsyłam do znakomitych dokumentacji. Skupię się tylko na problemie z tytułu posta.

Scenariusz

Serwery – Bamboo i Docker

Mamy dwa serwery – „bamboo” oraz „build1”.

Przeznaczeniem serwera „bamboo” jest hostowanie Bamboo i tylko tyle. „build1” jest zdalnym agentem dla „bamboo”, na nim także zainstalowany jest Docker. Na „build1” będzie budowana nasza aplikacja testowa – w dedykowanym dla niej kontenerze Dockera.

Bamboo umożliwia także odpalenie swojego agenta na tej samej maszynie na której się znajduje (tzw. „lokalny agent”), ale poza bardzo prostymi przypadkami, moim zdaniem, to proszenie się o kłopoty – środowiska powinny być odseparowane – Bamboo i tak ma nad nimi kontrolę właśnie dzięki zdalnym agentom.

Ja swoje środowisko postawiłem na dwóch maszynach wirtualnych przy użyciu Vagranta.

Aplikacja testowa

Aplikacją testową jest prosty projekt z PHP (framework Silex) – tylko, żeby pokazać użycie Composera i testów jednostkowych.

Projekt testowy hostuję na repozytorium Git na BitBucket.com. Jest ono spięty z Bamboo – odsyłam do dokumentacji jak to zrobić.

Struktura plików w projekcie jest następująca:

Bamboo test - strucure

W katalogu „docker” znajdują się skrypty potrzebne do zbudowania środowiska na Dockerze (oraz skrypt do uruchomienia PHPUnit – ale o tym za chwilę), a w katalogu „src” właściwy projekt.

W src/web/index.php mamy instancję klasy libs/Message/Message.php:

którą przetestujemy w src/tests/libs/MessageTest.php:

Tak jak pisałem – to jest maksymalnie prosty projekcik. 😉

Akcja właściwa

Zakładam, że mamy w Bamboo projekt „bamboo_test”.

Tworzymy w projekcie nowy plan:

Bamboo - tworzenie planu - krok1

Drugi krok pozostawiamy na razie bez zmian:

bamboo_screen_plan2

Następnie wchodzimy w edycję naszego nowego planu (Build -> All Build Plans i klikamy w edycję). Będzie nas interesować zakładka „Stages”:

Bamboo - Stages

Wybieramy „Default job”. W zakładce Tasks ustawimy poszczególne zadania planu budowania.

Poniższy screen przedstawia już cały mój plan, omówię konfigurację poszczególnych zadań:

bamboo_screen_tasks1

Pierwsze zadanie, czyli „Source Code Checkout” – już powinniśmy mieć (było zasugerowane podczas tworzenia planu). Pierwszy etap to pobranie kodu projektu z repozytorium.

Ja tylko dodatkowo włączyłem „Force Clean Build”, żeby podczas testowania niniejszego scenariusza, być pewnym, że wszystko od podstaw się buduje dobrze.

Kolejnym etapem jest postawienie naszego kontenera Dockera w którym będzie uruchomiona testowana aplikacja. Tak jak na poniższym obrazku:

bamboo_screen_tasks2

Dodajemy, więc poprzez „Add task”, komendę („command”). Będzie brakowało pliku wykonywalnego („executable”) docker-compose, więc dodajemy poprzez „Add exectuable”.

docker-compose będzie pracował na serwerze „build1” w podkatalogu „docker”, zatem tak ustawiamy w tym zadaniu. Plik „docker/docker-compose.yml” wygląda u mnie następująco:

Mam, jak widać jeden kontener („web1”), który jest zdefiniowany w pliku „docker/Dockerfile-web1”. Jest to kontener z PHP 7 i Composerem:

Kolejne zadanie to instalacja zależności projektu za pomocą Composera:

bamboo_screen_tasks3

Kolejna dodana komenda nam to załatwi. Za pomocą „docker-compose exec” uruchami ona Composera na kontenerze. Cały czas pamiętamy, żeby wskazać podkatalog roboczy „docker”.

Kolejne dwa zadania odnoszą się do uruchomienia testów jednostkowych. Tutaj parę dodatkowych szczegółów. Jako, że Bamboo cały czas „myśli”, że PHPUnit jest na serwerze „build1”, a nie kontenerze Docker, napisałem skrypt bashowy („docker/phpunit-docker.sh), który opakowuje wywołanie PHPUnit przez docker-compose:

Ułatwiło mi to przekazywanie argumentów do PHPUnit przez zadanie Bamboo „PHPUnit”.

Dlaczego tam jest „echo 'ok'”? Bamboo nie uruchamiał interpertacji wyników testów w przypadku, gdy któryś z testów jednostkowych nie przechodził – w logach miałem, że brakuje „OK”, więc dopisałem. Zapewne da się to sensowniej rozwiązać, ale wystarczyło na potrzeby proof of concept.

Zatem wracjąc do naszych zadań.

Kolejne zadanie to ustawienie praw do uruchomienia skryptowi „docker/phpunit-docker.sh”. Można użyć do tego zadania typu „script”:

bamboo_screen_tasks4

Następnie dodajemy zadanie typu „PHPUnit” w celu odpalenia testów jednostkowych:

bamboo_screen_tasks5

Generowanie wyników testów obsłużyłem w polu „arguments”, gdyż miałem problemy ze ścieżkami, jeśli próbowałem korzystać z „natywnych” funkcji zadania „PHPUnit”.

Jak widać na zrzucie ekranu – generuję wynik testów w postaci pliku xml oraz raportu z pokrycia testami. Wygenerowane pliki będą potrzebne w kolejnych etapach budowania.

Kolejny etap to interpretacja wyników testów. Dodajemy, więc zadanie typu „JUnit Parser” (nie udało mi się obsłużyć parsowania wyników bezpośrednio w zadaniu „PHPUnit”):

bamboo_screen_tasks6

Ostatnie dwa zadania w sekcji „final” to sprzątanie:

bamboo_screen_tasks7 bamboo_screen_tasks8

Pozostało jeszcze ustawienie interpertacji raportu pokrycia kodu testami, robimy to w zakładce „Miscellaneous”:

bamboo_screen_misc

W ten sposób mamy konfigurację budowania gotową.  Bamboo ładnie prezentuje wyniki testów czy pokrycie kodu testami:

bamboo_screen_result1

bamboo_screen_result2

Podsumowanie

Przedstawiłem prosty przykład użycia Dockera w procesie budowania przy użyciu serwera Bamboo.

Ogromną zaletą tego rozwiązania jest pewność, że aplikacja będzie budowana w identycznym środowisku w jakim była tworzona.