Ciągła integracja projektu z użyciem Travis CI i AppVeyor

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.

Ten wpis został opublikowany w kategorii Avocado, Daj się poznać 2017 - Avocado i oznaczony tagami , . Dodaj zakładkę do bezpośredniego odnośnika.