GitLab jako Continuous Integration (nejen) pro PHP

V článku si ukážeme, jak kromě hostování a správy git repositářů můžeme GitLab používat pro CI (Continuous Integration). Zajímavé na něm je, že na rozdíl od konkurence to nabízí i v hostované verzi zadarmo.

Znáte GitLab? Dlouho jsem si myslel, že to je jen nástroj pro pohodlnější správu git repositářů na vlastním serveru. Už to dávno není pravda, teď je to celá platforma pro řízení vývoje. Kromě hostování git repositáře umí issues a merge requesty pro code review. Jako nejdůležitější součást bych vyzdvihl integrované CI (Continuous Integration), které je podobné TravisCI. Ale na rozdíl od něj je zadarmo i pro private repositáře. Tím je odbouraný další důvod, proč na menších projektech nepoužívat CI.

GitLab nabízí tři varianty:

  1. hostovanou na Gitlab.com – zdarma neomezeně private repositářů, uživatelů a CI buildů (+možnost objednání lepšího supportu)
  2. self-hosted community edition (open-source)
  3. placenou Enterprise variantu (self-hosted), která navíc obsahuje další integrace a vychytávky pro větší týmy

Nám bude stačit ta první, tedy hostovaná varianta – funguje podobně jako GitHub, ale má navíc právě CI.

Continuous Integration

Jen stručně, co Continuous Integration (CI) je:

Kontinuální integrace je technika používaná při vývoji software, kdy členové týmu integrují svůj kód často, obvykle aspoň jednou za den, což vede k mnoha integracím každý den. Každá integrace je ověřena automatickým sestavením (včetně testů), aby byly případné problémy objeveny co nejdříve. Mnoho týmů zjistilo, že tento přístup vede k výraznému snížení problémů při integraci a umožňuje jim rychleji dodávat
kvalitnější software.

Přeloženo z článku Continuous Integration od Martina Fowlera

Zjednodušeně řečeno, pokud používáte branche a rebase před merge (což je vlastně integrace změn z masteru do vaší branche), tak se to budete snažit dělat co nejčastěji a průběžně budete spouštět testy a automatizované kontroly.

Jak vše nastavit?

Výhodou CI serveru je, že spouští build automaticky pro každou změnu (v tomto případě pro každý merge request nebo push do repositáře), takže se nestane, že by na to někdo mohl zapomenout. Aby bylo co spouštět, potřebujeme nejdříve build skript, který spustí různé nástroje – php lint, PHPUnit, kontrolu coding standards apod.

V článku vás provedu nastavením CI buildu na GitLabu pro PHP aplikaci. V první polovině článku se zaměřím na samotný build skript, který je na CI serveru nezávislý. V druhé polovině článku vám předvedu nastavení GitLab CI, které použije předtím vytvořený build skript.

Vytvoření build scriptu

Pro vytvoření build scriptu použijeme scripts přímo z Composeru. Pro komplexnější skripty můžete zvolit Phing, ale na začátku bych doporučoval dát šanci skriptům v Composeru. (Koneckonců v JS světě se teď také vracejí od buildovacích nástrojů Gulp/Grunt zpátky k npm skriptům.)

Nejjednodušší skript může vypadat nějak takto (předpokládá, že máme vytvořený phpunit.xml.dist s konfigurací PHPUnitu, aby ho šlo spouštět jen příkazem vendor/bin/phpunit):

{
    "require-dev": {
        "phpunit/phpunit": "5.6.1"
    },
    "scripts": {
        "test": "phpunit"
    }
}

A pokud zavoláme composer test (případně composer run-script test, pokud by se název skriptu překrýval s nativním příkazem Composeru), spustí se dle očekávání PHPUnit.

Pozn.: Composer automaticky přidá do cesty adresář vendor/bin/ takže ho není potřeba do příkazů vypisovat.

V dalším kroku si přidáme kontrolu coding standards:

{
    "require-dev": {
        ...
        "squizlabs/php_codesniffer": "2.7.1",
    },
    "scripts": {
        ...
        "phpcs": "phpcs --standard=PSR2 src tests"
    }
}

Jak jste se už jistě dovtípili, tak kontrolu můžeme zavolat pomocí composer phpcs.

Síla skriptů v Composeru je v možnosti volat je navzájem pomocí @. Vytvoříme tedy meta příkaz, který bude volat ostatní dva:

{
    "scripts": {
        "build": [
            "@test",
            "@phpcs"
        ],
        ...
    }
}

A composer build spustí jak testy, tak kontrolu coding standards.

Ještě se mi osvědčilo přidat si do buildu i volání composer install, aby build vždy proběhl se správnými verzemi balíčků. Výsledný build skript může vypadat následovně:

{
    "scripts": {
        "build": [
            "@composer install --no-progress --no-interaction --no-suggest",
            "@test",
            "@phpcs"
        ],
        "test": "phpunit",
        "phpcs": "phpcs --standard=PSR2 src tests"
    }
}

Při každém spuštění composer build se sice dozvíme, jestli testy a kontrola coding standards procházejí, ale nemáme jistotu, že na to některý vývojář nezapomene. A tady přichází ke slovu CI server, v našem případě GitlabCI.

Za domácí úkol doporučuji na začátek buildu přidat ještě PHP Parallel Lint, který kontroluje zda .php soubory jsou vůbec validní.

Nastavení buildu na GitlabCI

Registrací na GitLabu, vytvořením projektu a souvisejícími úkoly se tu zabývat nebudu, neměl by v tom být žádný problém. Pustíme se rovnou do konfigurace CI.

Pro konfiguraci se používá soubor .gitlab-ci.yml v rootu projektu, který v minimalistické podobě může vypadat takto:

image: geertw/docker-php-ci:7.0

my_app:
  script:
    - composer build

První řádek říká, na jakém image se build spustí (můžeme použít jakýkoliv image z Docker Hubu). Dále máme zapsaný úkol my_app, pro který se spustí příkaz composer build (proč je to takhle krkolomněji na dalším řádku, uvidíme později).

V GitLabu není potřeba nic nastavovat, pokud pushnete branch se souborem .gitlab-ci.yml, spustí se build automaticky. Detaily najdete v projektu na GitLabu v záložce Pipelines. Pokud se vám povede spustit tento skript, tak už máte vyhráno – dále už tu konfiguraci budeme jen vylepšovat.

Tip: Pro kontrolu validity konfigurace můžete použít jejich linter (je potřeba být přihlášen). Výhodou je, že nemusíte špinit git historii a čekat na failnutí buildu.

Výběr image a konfigurace

Nevybral jsem oficiální image s čistým PHP, ale jiný, který už je připravený pro použití v GitLabCI – obsahuje navíc git, Composer a některá rozšíření. Ještě místo geertw/docker-php-ci:7.0, který je v ukázce, může být vhodnější použít geertw/docker-php-ci:7.0-no-xdebug`, který mu tam před několika dny poslal Tomáš Fejfar. Pokud nepotřebujete sbírat code coverage z testů,  Xdebug nedává v CI smysl a jen bude build zpomalovat.

(V oficiální dokumentaci ukazují postup instalace závislostí do základního image v rámci buildu, což v během mého testování klidně minutu nebo dvě přidalo. Proto používám odlišný postup – už hotový image.)

Pokud pro svůj projekt máte specifické požadavky na rozšíření nebo nainstalované aplikace, tak vám nic nebrání vytvořit vlastní image a používat ten. Nicméně to už přesahuje rozsah článku, třeba se tématu chopí někdo povolanější.

Pokud se vám nechce vytvářet vlastní image a v rámci buildu potřebujete něco nastavit, tak je možné použít hook before_script. Pro ukázku jsem do něj přidal jen příkaz na vypsání verze PHP, což se nám bude hodit později. Jen pozor, že pokud budete něco instalovat v každém buildu, tak to zbytečně prodlouží jeho běh.

before_script:
  - php -v

Kde vlastně buildy běží? GitLab se domluvil s DigitalOcean a ten mu zadarmo poskytujete VPS pro běh buildů. (Případně by mělo být možné přidat vlastní VPS, ale to jsem nezkoušel.)

Spouštění na různých verzích PHP

Pokud potřebujete build spouštět na různých verzích PHP (ať už proto, že musíte podporovat i starší verze, nebo naopak chystáte přechod na novou), tak to v GitLabCI není problém. Stačí upravit konfiguraci takto (a odebrat původní image:):

my_app:5.6:
  image: geertw/docker-php-ci:5.6-no-xdebug
  script:
    - composer build

my_app:7.0:
  image: geertw/docker-php-ci:7.0-no-xdebug
  script:
    - composer build

Build se automaticky spustí na obou image.

Cachování

Zbytečně mnoho času stráví build na stahování závislostí přes Composer. Lze to snadno vylepšit pomocí nastavení cachování vybraných adresářů (v případě Composeru to bude vendor).

cache:
  paths:
    - vendor/

Po úspěšném buildu se vybraný adresář uloží mimo Docker container a při příštím běhu je obnoven. Přináší to docela znatelné zrychlení, tak to určitě nevynechejte.

Services

Pokud budete v rámci buildu potřebovat jakoukoliv další aplikaci (databázi, Redis, cokoliv), tak se to řeší prostřednictvím tzv. services – k hlavnímu kontejneru můžete napojit libovolné další.

Například pro MySQL by to vypadalo takto:

services:
  - mysql:latest

variables:
  # Configure mysql environment variables (https://hub.docker.com/r/_/mysql/)
  MYSQL_DATABASE: zdrojak_demo
  MYSQL_ROOT_PASSWORD: super_secure_password

mysql:latest říká, že chceme použít oficiální image pro MySQL v poslední verzi. Dále pak nastavíme konfigurační proměnné pro konkrétní image. Obdobně si můžeme nastavit jakoukoliv další service (tzn. připojit si jakýkoliv image).

Výsledek

Výsledná konfigurace .gitlab-ci.yml pro buildování aplikace, která pro testy potřebuje MySQL databázi, může vypadat takto:

before_script:
  - php -v

services:
  - mysql:latest

variables:
  MYSQL_DATABASE: "zdrojak_demo"
  MYSQL_ROOT_PASSWORD: "123456"

my_app:5.6:
  image: geertw/docker-php-ci:5.6-no-xdebug
  script:
    - composer build

my_app:7.0:
  image: geertw/docker-php-ci:7.0-no-xdebug
  script:
    - composer build

cache:
  paths:
    - vendor/

Závěr

Pro mě osobně je GitLab a jejich CI překvapení a své miniprojekty, které mám aktuálně na Bitbucketu bez CI, začnu postupně převádět na Gitlab a nastavovat k nim CI. A samozřejmě se nemusíte omezovat jen na projekty v PHP.

Teď už opravdu nevidím žádný důvod, proč CI nepoužívat. Pokud na nějaký přijdete, tak vám ho v komentářích rád zkusím vyvrátit. Případně vám pomohu s nastavením buildu.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 17

Přehled komentářů

Kamil Paměťová náročnost
Martin Hujer Re: Paměťová náročnost
MichalKleiner Bitbucket Pipelines
Jarda Re: Bitbucket Pipelines
Jirka Ďoubek Podoba s Bitbucket Pipelines
Martin Hujer Re: Podoba s Bitbucket Pipelines
Martin Hujer Re: Podoba s Bitbucket Pipelines
maryo
zvedavec
Martin Hujer Re:
frees
Martin Hujer Re:
Vojta Svoboda Re:
Martin Hujer Re:
lenoch dotaz
Martin Hujer Re: dotaz
Martin Re: dotaz
Zdroj: https://www.zdrojak.cz/?p=19033