Continuous Integration jest mocno powiązany ze współczesnym podejściem do tworzenia oprogramowania. Usprawnia proces automatycznego budowania i testowania oprogramowania.
Oto jak ja rozumiem zasadę działania CI:
- programista wprowadza zmiany w kodzie i wysyła je do głównego repozytorium (
git push
) - serwer wykrywa zmiany, pobiera kopię brancha na którym je wprowadzono
- serwer buduje aplikacje
- serwer uruchamia testy
- informacja o sukcesie lub raport z błędami przesyłane są do programisty/repozytorium
Mocno uprościłem ideę działania - bardziej skomplikowane konfiguracje uruchamiają statyczną analizę kodu, testy integracyjne. Są w stanie przygotować artefakt czy nawet go wdrożyć - wszystko bez ingerencji człowieka. Te etapy wychodzą już poza granice CI i poruszają tematy związane z Continuous Delivery i Continuous Deployment - nie stosowałem ich jeszcze w tym projekcie, ale chciałbym te tematy poruszyć w przyszłości.
Jak zrealizować taki schemat? Jedną z opcji jest przygotowanie repozytorium Gita na innej maszynie i umieszczeniu w nim git hooków, które będą reagować na zmiany przesłane do tego repozytorium, a następnie wywoływać skrypty budujące projekt. To bardzo dużo pracy - wynajdujemy koło od nowa i nie każdy przepada za pisaniem skryptów bashowych. Zasadę działania git hook na przykładzie Avocado zaprezentuję w innym razem.
Zamiast tego można wykorzystać gotowe darmowe rozwiązania takie jak Jenkins, Bamboo czy TeamCity. W nich takie scenariusze można skonfigurować z poziomu wygodnego interfejsu webowego z masą opcji. Niestety nie każdy ma możliwość postawienia maszyny do obsługi takiego oprogramowania.
Istnieją na szczęście jeszcze inne rozwiązania pozbawione tej wady. Dla Avocado zastosowałem dwa - Travis CI oraz AppVeyor. Ogromnymi zaletami są:
- darmowa usługa online dla projektów Open Source
- integracja z GitHubem
- prosta konfiguracja z użyciem plików .yml
Dlaczego wybrałem dwa serwisy, których możliwości pokrywają się? Travis dostarcza wirtualne środowisko oparte o Ubuntu i umożliwia budowanie projektu pod Linuksa z użyciem gcc lub clang (obsługuje też OS X, ale na ten moment nie testowałem jego obsługi). Moim głównym systemem jest jednak Windows i to właśnie jego AppVeyor (z pomocą Visual Studio) używa do kompilacji projektu. Dzięki temu rozwiązaniu testowane są automatycznie dwie platformy. Wiąże się to jednak z potrzebą utrzymania dwóch skryptów budujących projekt.
Oprócz budowania na różne platformy oba rozwiązania dostarczają build matrix, co pozwala na testowanie konfiguracji z różnymi wersjami kompilatorów (gcc, clang, VS2015, VS2017)
Travis CI
Integracja jest prosta - na stronie travis-ci.org logujemy się z użyciem GitHuba, co połączy oba serwisy. Travis odczyta listę repozytoriów i pozwoli wybrać, w których z nich ma śledzić zmiany. Następnie do repozytorium należy dodać plik .travis.yml
, gdzie umieszczamy schemat według którego ma być budowany projekt (dokumentacja).
Oczywiście u mnie nie obeszło się bez problemów. Travis używa dosyć starej wersji systemu - Ubuntu 12.04 lub 14.04. Przekłada się to na stare wersje kompilatorów dostępnych w środowisku, a to rzutuje na brak możliwości korzystania z nowych standardów języka.
Poniżej krótkie wyjaśnienie konfiguracji w pliku .travis.yml:
sudo: false language: generic
sudo: false
oznacza, że korzystamy z infrastruktury opartej o kontenery. Ta opcja jest zalecana dla nowych konfiguracji, gdyż przyśpiesza czasy budowania. Ograniczenia: brak dostępu do komendy sudo
, co wyklucza użycie apt-get
. Dodatkowe pakiety można dostarczać w inny sposób.
language
określa dla jakiego języka ma zostać przygotowane środowisko. Ja używam generic
, ponieważ ręcznie dodaję kompilatory i chcę uniknąć konfliktu z tymi dostarczanymi przez Travisa.
matrix: include: - os: linux env: COMPILER_NAME=clang CXX=clang++-3.8 CC=clang-3.8 addons: apt: packages: - clang-3.8 sources: - ubuntu-toolchain-r-test - llvm-toolchain-precise-3.8 - os: linux env: COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5 addons: apt: packages: - g++-5 sources: - ubuntu-toolchain-r-test
Dla powyższej konfiguracji będą przygotowane dwa środowiska do buildów - Linux z kompilatorem clang 3.8 oraz Linux z kompilatorem gcc 5. Aby mieć możliwość pobrania tych pakietów wymagane jest dodanie odpowiedniego źródła apt. Te z kolei muszą zostać dodane na whitelistę przez twórców Travisa.
install: - export BASE=$PWD # Build SDL2 from externals - mkdir buildsdl - export PREFIX=$PWD/buildsdl - export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig - export PATH=$PATH:$PWD/buildsdl/bin - cd externals/SDL2 - ./configure --prefix=$PREFIX - make -j - make install - cd $BASE - export PREMAKE_VERSION=5.0.0-alpha5 # Download and build premake5 from source # new versions have problems with build on travis-ci enviroment - wget https://github.com/premake/premake-core/releases/download/v`echo $PREMAKE_VERSION`/premake-`echo $PREMAKE_VERSION`-src.zip -O premake.zip - unzip premake.zip - cd premake-`echo $PREMAKE_VERSION`/build/gmake.unix - make config=release - cd ../../.. - mv premake-`echo $PREMAKE_VERSION`/bin/release/premake5 premake5
Ten skrypt pobiera i buduje brakujące zależności (premake5) oraz kompiluje SDL2 ze źródeł.
before_script: - cd $BASE - ./premake5 gmake --headless
Przed właściwą kompilacją generowany jest Makefile.
script: - make config=release -j
I ostatecznie uruchamiany jest make, który buduje projekt.
Cały plik .travis.yml można podejrzeć tutaj: https://github.com/JaCzekanski/Avocado/blob/develop/.travis.yml. Na ten moment
zakomentowana została obsługa gcc - niektóre z flag używanych do kompilacji nie są kompatybilne pomiędzy kompilatorami i build kończy się porażką.
Buildy są wyzwalane dla każdego commitu w repozytorium. Można to ograniczyć w konfiguracji projektu na stronie Travisa, aby budować przy pushach lub przy okazji pull requestów. W pliku .yml można odfiltrować które branche mają być obserwowane - domyślne obserwowane będą wszystkie.
Od teraz commity na Githubie będą oznaczane statusem buildu:
AppVeyor
Konfiguracja jest w zasadzie identyczna do tej z Travisa - integrujemy serwis appveyor.com z kontem Github, oznaczamy repozytorium i dodajemy plik appveyor.yml. Konfiguracja w tym przypadku była dużo prostsza, ponieważ aktualne wersje Visual Studio są dostępne out-of-the-box.
os: - Visual Studio 2015 environment: matrix: - TOOLSET: vs2015 before_build: - git submodule update --init --recursive # premake - ps: Start-FileDownload 'https://github.com/premake/premake-core/releases/download/v5.0.0-alpha5/premake-5.0.0-alpha5-windows.zip' 'premake.zip' - 7z x premake.zip # SDL - cd externals - rmdir /Q /S SDL2 - ps: Start-FileDownload 'https://www.libsdl.org/release/SDL2-devel-2.0.4-VC.zip' 'SDL.zip' - 7z x SDL.zip - ren SDL2-2.0.4 SDL2 - cd .. # generate solution - premake5.exe %TOOLSET% --headless configuration: - Release build: project: Avocado.sln
Plik appveyor.yml dostępny tutaj: https://github.com/JaCzekanski/Avocado/blob/develop/appveyor.yml.
Przed kompilacją aktualizowane są submoduły, pobierany jest Premake5 i SDL2 (tutaj nie jest kompilowany ze źródeł) i generowana jest solucja kompatybilna z Visualem. AppVeyor już wie jak zbudować taki plik - wystarczy podać jego nazwę.
W celu przyśpieszenia buildów uruchamiana jest tylko konfiguracja Release dla VS2015.
Badges
Obydwa serwisy pozwalają na wygenerowanie obrazka mówiącego o obecnym statusie (build passing/failed). Można je dodać do pliku README.md i w łatwy sposób pokazać status CI. Dla Travisa opis znajduje się tutaj. AppVeyor w ustawieniach projektu posiada zakładkę Badges, gdzie można skopiować gotowy kod markdown.
Na koniec
Pliki .yml to świetny sposób, żeby dowiedzieć się w jaki sposób zbudować dany projekt - są one aktualizowane na bieżąco przez programistów (aby CI działało poprawnie), natomiast sekcje How to build w dokumentacji nie zawsze poruszają wszystkie problemy lub posiadają braki. Ja sam posiłkowałem się plikami z innych projektów przy konfiguracji Travisa i trafiałem na działające rozwiązania w przeciwieństwie do sprzecznych lub nieaktualnych informacji zawartych na forach i blogach.