Cześć, dzisiaj chciałbym poruszyć temat Continuous Integration. Temat ciągłej integracji (bo tak w wolnym tłumaczeniu to brzmi) w świecie IT pojawia się coraz częściej. Warto mieć szczątkową wiedzę w tych tematach. Jest wiele użytecznych narzędzi. Jenkins, Hudson…ja jednak chciałbym się skupić na innym narzędziu – Travis CI
Continuous Integration – a cóż to jest?
Jeżeli jesteś początkującym programistą to bardzo możliwe, iż nie wiesz co to jest. Jak już wspomniałem, w wolnym tłumaczeniu jest to po prostu ciągła integracja. Myślę, żę nazwa jest dość wymowna. Często podczas rozwoju aplikacji dokładamy nowe, mniejsze lub większe funkcjonalności. Czasami jest to parę zmian w głównej aplikacji dziennie. zwykle obecna wersja aplikacji (snapshot) działa na osobnym serwerze do którego zespół i często też klienci mają dostęp. No dobrze, ale to tak za każdym razem jak coś nowego wchodzi należy wpisywać tych tryliard komend po drodze, coś ustawiać, zmieniać, sprawdzać? Otóż nie! W tym celu właśnie powstają narzędzia specjalnie do tego typu zadań.
Najpopularniejszymi są Jenkins, Hudson oraz właśnie Travis. Zdecydowanie najpopularniejszym jest ten pierwszy. Jest to projekt open-source’owy oraz darmowy. Osobiście zainstalowałem go lokalnie i trochę się z nim “bawiłem”. Tak tak, użyłem w tym przypadku również Raspberry. Instalacja przebiega normalnie, tak jak na zwykłym serwerze. Niestety, do moich celów nie sprawdzał się aż tak dobrze jak Travis.
Dlaczego Travis?
Travis, w porównaniu do pozostałych dwóch narzędzi stosuje inne podejście. Hudsona i Jenkinsa należy pobrać, skonfigurować, postawić serwer, napisać w miarę dokładne skrypty i “ogarnąć się” w świecie wtyczek, które udostępniają. Bez nich często nie możemy zrobić tego co chcemy. Travis natomiast jest napisany typowo pod projekty na GitHubie. Korzystanie z Travisa to stworzenie repozytorium, zalogowanie się dzięki konta na GitHubie, stworzenie jednego pliku konfiguracyjnego w repozytorium o nazwie .travis.yml i….to wszystko. W zależności od konfiguracji za każdym razem gdy “wbijemy” jakieś zmiany do naszego repozytorium, Travis automatycznie zrobi to, o co go poprosiliśmy w pliku konfiguracyjnym. Szybko, łatwo, przyjemnie, małym nakładem pracy. Travis również może być zintegrowany z narzędziami stosujące podejście kontenerów (Docker) oraz różnego typu chmurami publicznymi (AWS, Azure). Zamierzam z obu tych rozwiązań korzystać w przyszłości, więc kolejny plus. Dziś natomiast tej integracji nie omówię.
Żeby nie było tak kolorowo, Travis jest darmowy tylko do użytku w przypadku repozytoriów darmowych. Korzystanie z Travisa dla repozytoriów płatnych odbywa się poprzez inną stronę (rozszerzenie .com, nie .org). Druga opcja to korzystanie z Travisa w ramach Student Packa oferowanego przez GitHuba (o którym też coś niebawem napiszę bo warto korzystać!). Pricing dla zwykłego użytkownika nie prezentuje się korzystnie więc pozostaje nam korzystanie z Travisa w ramach publicznych repozytoriów, do czego gorąco zachęcam!
Jak zacząć?
Logowanie do Travisa jak wspomniałem jest bardzo proste. Wystarczy przejść do głównej strony i możemy dzięki konta z GitHuba używać Travis CI. W tym celu wystarczy kliknąć w zielony przycisk z napisem – tak tak – “Sign Up”.
Następnie Travis spyta nas o projekty, które chcemy udostępnić. Oczywiście, możemy dowolnie tym manipulować podczas późniejszego użytkowania. Udostępnianie projektów odbywa się bardzo prosto, dzięki “przełączników”. Możemy to zmieniać na stronie “Accounts”. Możemy wybrać ją z menu, które ukaże nam się po najechaniu na naszą piękną twarzyczkę w prawym górnym rogu.
Tak prezentuje się strona z projektami do udostępnienia. Jak widzisz, używam Travisa do Continuous Integration do aplikacji My Coach, zarówno w przypadku części back-endowej oraz front-endowej.
.travis.yml – jak powiedzieć Travisowi co ma zrobić?
Tak jak wspomniałem, ustawienia dla repozytorium wybieramy tworząc w nim plik o nazwie .travis.yml. Plik jest typu yaml, który jest coraz częściej spotykany. To połączenie JSON’a z plikami typu properties. Osobiście bardzo lubię ten typ plików, konfiguracja jest łatwa i czytelna. Co możemy tam konfigurować? Między innymi:
- jakiego języka programowania używamy w projekcie
- w przypadku danego języka możemy definiować konkretną wersję
- zewnętrzne aplikacje, które możemy ściągnąć (na przykład konkretną przeglądarkę, przydatne szczególnie do testów interfejsu webowego)
- co należy zrobić przed i po wykonaniu odpowiednich skryptów
- skrypty, które składają się na główny proces “buildu”
- branche naszego repozytorium, które powinny przechodzić przez etap CI
- powiadomienia email – domyślnie dostajemy, ale z czasem mogą być natarczywe
Podstawowa konfiguracja projektu z użyciem Javy
Aby zrobić swój pierwszy build dzięki Travisa nie trzeba nam wiele. Zakładając, iż korzystamy z Gradle’a plik konfiguracyjny mógłby prezentować się następująco:
Tak, to wszystko! Travis widząc pliki charakterystyczne dla Anta/Mavena/Gradle’a użyje odpowiedniego systemu do buildowania. Nie musisz się niczym martwić. Co więc dokładnie Travis wie i co zrobi?
- główny język stosowany w projekcie to Java
- stosowane JDK to Oracle JDK 8
- po ściągnięciu i ustawieniu całego projektu wykonają się dwa Gradle’owe taski, assembly oraz build
Postępy prac możemy śledzić na głównej stronie Travisa. Mamy tam coś na wzór terminala, możemy na żywo podglądać co się dzieje z naszą aplikacją. o ile wszystko pójdzie dobrze to na końcu w logach ujrzymy odpowiedni komunikat. Poznamy to też poprzez – iż tak prosto powiem – ilości zielonych elementów na stronie. Charakterystyczny znacznik przy nazwie projektu natomiast nie dotyczy konkretnego builda tylko ostatniego, definiuje to więc aktualny stan projektu. U mnie wygląda to tak:
Wygląda bardzo przyjaźnie, prawda? Na górze bardzo ładnie widać znacznik “build: passing”, charakterystycznego “ptaszka” po lewej stronie również. Mamy też informację o tym ile build trwał, na którym branchu, kto był autorem commita oraz kod konkretnego commita. Możemy też zrestartować build dla konkretnej wersji.
Nie zawsze wszystko idzie po naszej myśli. Dla bardziej zaawansowanych skryptów wiele rzeczy może pójść nie tak, na przykład złe dane do połączenia, nieudane testy jednostkowe, brak paczek potrzebnych do buildu aplikacji…bywa. Łatwo zauważyć w Travisie iż coś poszło nie tak, a błędy są do wyśledzenia tak samo prosto jak lokalnie, czyli poprzez konsolę. Tak wygląda podgląd na nieudany build. Różnica nie jest zbyt wielka, ale jest widoczna:
Szybsze buildy? Użyj cache’a!
Z czasem zobaczysz jak projekt się rozrasta, iż buildy potrafią “kręcić się” kilkadziesiąt minut, godzinę, godziny….naprawdę, zdarzają się takie przypadki! Programiści znając tego typu problemy zawsze znajdą rozwiązanie. Jednym z nich jest użycie cache’a. Możemy bardzo łatwo zdefiniować, które foldery należy cache’ować. Dzięki temu nie będzie potrzeby ściągania wszystkich bibliotek za każdym razem tylko wszystko będzie od razu gotowe. Często potrafimy zyskać na tym minuty. Możemy również zdefiniować co chcemy zrobić przed wrzuceniem plików do cache’a. Dodajemy w tym celu dwa proste parametry do naszego buildu. Przykładowo:
Myślę, iż parametry są dość czytelne i zrozumiałe. Co tu naprawdę robię:
- mówię Travisowi, aby przed wrzuceniem plików do cache’a usunął z katalogu domowego plik ze ścieżki .gradle/caches/modules-2/modules-2.lock
- mówię Travisowi, aby do cache’a wrzucił pliki z folderu domowego, w którym znajduje się katalog /.gradle/caches
Parę linii, buildy szybsze. Świetna sprawa. W obu projektach korzystam z tej funkcjonalności. Możemy również podejrzeć ile danych w cache’u trzymamy. W tym celu należy przejść do strony konkretnego projektu, a następnie z menu “More options” wybrać opcję “Caches”. U mnie wygląda to tak:
Cóż, prawie 700 MB w cache’u. Prawie jedna płyta CD, nie tak źle!
Poufne dane, co z nimi?
Początkowo we mnie wielkie wątpliwości budziły sprawy związane z poufnymi danymi. Załóżmy, iż potrzebuję hasła do bazy danych aby build się powiódł. Co teraz? Mam to tak wpisać w pliki konfiguracyjne aby tekst był dostępny dla wszystkich?
Travis umożliwia nam konfigurację zmiennych środowiskowych w taki sposób, iż nikt, choćby my, po ustawieniu nie wie jakie mają wartości. Czym są zmienne środowiskowe myślę, iż wiesz. Istnieją dwa sposoby konfiguracji zmiennych środowiskowych:
- na stronie projektu w zakładce “settings” możemy je łatwo zdefiniować. wpisujemy po prostu klucz – wartość. Przykładowo skonfigurowana zmienna “SONAR_TOKEN”:
- w pliku .travis.yml możemy wpisać zaszyfrowany klucz oraz wartość. Jest to świetnie opisane w tym miejscu.
Oczywiście, istnieje też możliwość zdefiniowania zmiennych środowiskowych bezpośrednio w pliku konfiguracyjnym, “plain textem”. Może być tak, iż nie potrzebujemy tego ukrywać, a wartość może być często modyfikowana. Standardowo, istnieje do tego odpowiednie pole. Zmienne mogą być globalne lub używane w obrębie aplikacji. Przykład:
Po nazwach możemy się domyśleć, iż ustawiona tu jest GLOBALNIE wersja Chrome’a oraz lokalnie wersja bazy danych oraz nazwa testowego użytkownika. Pamiętaj, iż są to nazwy przykładowe i umowne. To, jak te zmienne obsługuje później developer to inna sprawa.
Travis udostępnia też wiele zmiennych globalnych, które ma zdefiniowane. Cała ich lista znajduje się tutaj. Można ich dowolnie używać w skryptach i konfiguracji.
Konfiguracja dwóch projektów na przykładzie My Coach
Na koniec w ramach cotygodniowych raportów z prac nad moim projektem chciałbym zademonstrować moją konfigurację. Wdrożyłem to parę dni temu. Podejrzewam, iż część rzeczy będzie dalej się zmieniać, ale na obecne potrzeby projektu taka konfiguracja jest wystarczająca. o ile jest coś dramatycznie źle zrobionego i masz większe pojęcie o tym, zapraszam do uwag.
Konfiguracja dla części serwerowej aplikacji:
Użyłem tutaj 9 opcji z wielu, które Travis udostępnia. Są to kolejno:
- język w którym projekt jest pisany. W tym przypadku Java
- używane JDK – oraclejdk8
- dystrybucja systemu na której build ma się odbywać. Trusty oznacza zaufany. Bezpieczna opcja o ile nie potrzebujesz konkretnej wersji
- branche dla których buildy mają się wykonywać, zdefiniowałem dwa
- polecenia, które mają się wykonać przed instalacją. Jest to task Gradle’owy, który tworzy wrappera, oraz polecenie, które daje wrapperowi uprawnienia do wykonania się
- polecenia, które mają się wykonać przed stworzeniem paczki do cache’owania. Ja usuwam plik z lockiem oraz folder “plugin-resolution”
- foldery, które mają być “zcache’owane”. Zdefiniowałem 3
- główny skrypt, który musi się wykonać aby build był poprawny. Wykonuję tu podstawowy build gradle’owy
- powiadomienia e-mail. Wyłączone
Analogicznie, w pliku .travis.yml w repozytorium front-endowym mamy skonfigurowane buildy. Tutaj już jest trochę więcej zabawy:
Konfiguracji jest trochę więcej, ustawiam kolejno:
- uprawnienia – chcę mieć administratora w systemie (w systemach linuxowych w terminalu komunikaty administracyjne poprzedzam właśnie “sudo”)
- dystrybucję zaufaną, jak powyżej
- język, w którym piszę, Angular 2 de facto działa na Node.js
- specyficzną wersję Node’a
- branche dla których buildy mają być tworzone. Tutaj tylko master
- dodatkowe package, które powinny być dla nas widoczne. Potrzebuję tutaj odpowiedniej przeglądarki (uwaga, dla Travisa używającego Chromium instalujemy Chrome, nie jestem w stanie odpowiedzieć dlaczego)
- polecenia, które mają wykonać się przed instalacją. Tutaj ustawiamy aplikację która ma być dla nas Chromem oraz startuję xvfb, który umożliwi mi testowanie graficzne
- polecenia, które mają wykonać się przed skryptem. Potrzebuję zainstalować globalnie dwie paczki dla Node.js
- foldery, które mam cache’ować (dwa foldery z paczkami node’owymi)
- skrypty, które mają się wykonać (build i testy, polecenia “ng” są charakterystyczne dla Angular CLI)
- to co ma się wykonać po udanym wykonaniu skryptu, w tym przypadku deploy z użyciem Angular CLI gh pages, które instalowałem przed odpaleniem skryptów.
- zaszyfrowana zmienna środowiskowa. Terefere, nie powiem Ci co tam siedzi.
Podsumowanie
Cóż, osobiście uważam to za świetną alternatywę dla Jenkinsa czy Hudsona. Konfiguracja naprawdę jest przyjemna, nie jest to takie straszne, a w życiu zawodowym znajomość pojęć oraz systemu związanymi z CI jest w tej chwili bardzo istotna. A Ty czego używasz? Jakie masz doświadczenia z Continuous Integration? Jestem strasznie ciekaw Twojej opinii!