Visual Studio Team Services, PHP a DevOps

ci2

Pokud se budeme v dnešní době bavit o moderním vývoji, můžeme zaslechnout slova jako DevOps nebo Continuous Integration (pokud jste o ničem takovém zatím neslyšeli, můžete začít třeba zde). Dnes vám chci ukázat jak vytvořit build definici pro PHP projekt, který je uložený ve Visual Studio Team Services, a jeho následné nasazení do Azure Web Apps nebo na virtuální server.

Na začátek se pojďme podívat, z čeho se taková build definice skládá: V prvním kroku typicky dochází ke stažení všech potřebných balíčků a závislostí (to je stojné pro většinu projektů v různých jazycích). Pro PHP aplikace se nejčastěji používá Composer. Pokud vaše aplikace obsahuje nějaký frontend, můžete potřebovat třeba JavaScriptové balíčky z NPM nebo spustit nějaké Grunt či Gulp tasky. Poté máte možnost spustit automatické testy (cože, kód se dá testovat i mimo produkci? :)). A pokud se vše povede, můžeme vypublikovat Artefakty z projektu, abychom se k sestavené verzi dostali z VSTS a následně kód nasadit třeba do Azure Web Apps, virtuálního stroje (Windows či Linux), který běží na Azure, Amazonu, Google Cloudu nebo třeba u vás v serverovně!

Zatím to zní hezky, ne? Předpokládám, že již máte nějaké VSTS Workspace vytvořené (pokud ještě ne, přejděte sem a vytvořte si ho), nyní se pojďme podívat, jak na to:

Build Agent

Nejdůležitější součást v samotném build procesu hraje takzvaný build agent. Visual Studio Team Services nabízejí hostovaného agenta, který se dá použít pro Node.js, ASP.NET a mnoho dalších typů projektů, ale bohužel neobsahuje Composer ani PHP prostředí (obsahuje však mnoho jiných nástrojů), takže si musíme vytvořit vlastního (a není to vůbec těžké!).

Build agent je prakticky virtuální stroj (nebo fyzický), kterému přijde daná build definice a zprocesuje ji.

Virtuální stroj, jaký OS vybrat?

Typicky budete vybírat operační systém na základě prostředí, ve kterém vaše aplikace poběží. Pokud si zvolíme Azure Web Apps nebo virtuální stroj s Windows jako naše prostředí, bude jasnou volbou pro build agenta stroj s OS Windows. Pokud bychom chtěli aplikaci nasadit například do App Service na Linuxu, vytvoříme build agenta na Linuxu. Jenom na vysvětlenou – pokud třeba vyvíjíte v Node.js, určitě víte, že existují nativní balíčky, které se kompilují přímo pro systém, na kterém poběží – není možné vzít balíček zkompilovaný na Linuxu a spustit ho ve Windows. Dalším, možná ještě důležitejším důvodem by byly třeba testy – určitě by pro vás mělo být důležité to, aby se testy spouštěli vždy v prostředí, které je velmi podobné (nebo stejné) jako to, kde vaše aplikace poběží v produkci.

Protip: Pokud byste si vybrali Azure Virtual Machines jako prostředí pro běh vašeho build agenta, mohli byste ocenit možnost využití Startup/Shutdown politik v Dev/Test labech a díky tomu tak ušetřit peníze za běh velkého virtuálního stroje (za předpokladu, že buildovat budete periodicky a ne při každém commitu). Případně byste mohli pomocí Azure Functions celý flow nastavit tak, že při commitu se virtuální stroj spustí a zavolá váš build task.

Nastavení agenta

V mém případě budu aplikaci nasazovat na hosting, který běží na Windows (Azure App Service, ale zmíním i další možnosti). Prvním krokem je vytvoření serveru s Windows. Vybral jsem si VM v Azure, ale mohl bych si ji vytvořit kdekoliv, klidně na mém PC v Hyper-V. Pokud nevíte, jak vytvořit virtuální stroj v Azure, návod najdete třeba zde.

Jakmile je můj server připraven, připojím se k němu pomocí vzdálené plochy a můžu začít nastavovat prostředí:

  1. Nejdůležitějším krokem je instalace PHP prostředí. To můžete nainstalovat třeba tak, že použijete Web Platform Installer nebo si třeba stáhnete samostatnou verzi (nebo si ji sami zkompilujete) a přidáte do vašeho PATHu.
    web_platform_installer_php
  2. Další podstatnou komponentou je Composer. Můžete ho nainstalovat pomocí příkazové řádky nebo třeba využít instalační balíček pro Windows, který se o vše postará za vás.
  3. Pokud váš projekt obsahuje testy, můžete si například nainstalovat PHPUnit, který opět nabízí velmi jednoduchou instalaci.
  4. Také byste měli mít nainstalované prostředí Node.js. Bude se vám hodit hlavně z dvou důvodů - pokud ve vašem projektu používáte NPM nebo spouštíte nějaké Tasky (např. pomocí Grunt) ve vašem kódu a také z důvodu toho, že build tasky jsou převážně psané v Node.js.
  5. Pokud budete vaši aplikaci nasazovat do Azure Web Apps, nezapomeňte nainstalovat Web Deploy.
  6. Posledním krokem je instalace samotného Build Agenta. Microsoft nabízí podrobné návody k instalaci jak pro Windows, Linux, tak i pro Mac.

Po úspěšné konfiguraci se vás build agent objeví v sekci Agent pools ve VSTS jako na obrázku:

vsts_agent

Jelikož VSTS agent neumí automaticky detekovat Composer nebo PHP, může se hodit manuálně přidat tzv. Capabilities. Pokud byste build agentů měli několik, s různými konfiguracemi, bude pak VSTS vybírat vždy takového, který splňuje vaše požadavky.

vsts_agent_capabilities

Nyní máme vytvořeného build agenta, pojďme se vrhnout na vytvoření build definice.

Build Definice

Build definice je sada kroků/tasků, které definují celý proces buildu. Budeme začínat z prázdné definice, ve které budeme mimo naše vlastní kroky využívat i kroky nachystané Microsoftem.

VSTS automaticky vytvoří dočasnou složku s projektem, do kterého se vždy stáhne kód projektu, takže první věcí, kterou začneme bude získání balíčků z Composeru.

Composer

Jelikož zatím na Composer neexistuje žádný předem definovaný krok, musíme si ho sami vytvořit pomocí skriptu. Ve Windows rád používám PowerShell, takže vytvořím krok s PowerShellem:

vsts_build_task_ps

Následně do kroku vložím kód, který chci spustit. Můžete využít buď skriptu jako takového, který bude uložený společně se zdrojovým kódem nebo využít možnost definice skriptu přímo v kroku (Inline definition). Obsah skriptu je následující:

 # You can write your powershell scripts inline here. 
# You can also pass predefined and custom variables to this scripts using arguments

composer install

Následně musíte změnit nastavení skriptu, jelikož Composer odesílá debug zprávy na stderr (více info v #4034), musíte odškrtnout Fail on standard error v Advanced sekci, jinak bude build task konstantně failovat. Nemusíte se ničeho bát, jelikož pokud dojde k opravdové chybě, např. nepodaří se stáhnout balíček, task bude považován jako chybový, jelikož Composer vrátí chybový kód.

vsts_build_task_ps_cfg

Po tomto kroku bude projekt obsahovat všechny závislosti z Composeru. Podobně bychom mohli nastavit i stahování NPM balíčků nebo spuštění Grunt tasku s rozdílem, že tyto tasky jsou již nachystané a nemusíte pro ně vytvářet skripty.

Dalším v pořadí jsou testy.

PHPUnit

PHPUnit také nemá nachystaný task, takže si ho budeme muset vytvořit. V průběhu testování jsem narazil na několik problémů s formátem, který vrací PHPUnit, ale to vysvětlím později.

Opět musíme vytvořit nový task s příkazovou řádkou a opět jako inline script. Obsah skriptu najdete níže společně s vysvětlením:

 phpunit

try
{
    $XslPatht = New-Object System.Xml.Xsl.XslCompiledTransform
    $XslPatht.Load("Test/phpunit2junit.xsl")
    $XslPatht.Transform("Test/Logs/junit.xml", "Test/Logs/junit-fixed.xml")
}
catch
{
    Write-Host $_.Exception -ForegroundColor Red
}

V první řadě spustíme phounit. Aby vše proběhlo úspěšně, musíme mít vytvořenou definici phpunit.xml v rootové složce projektu. Příklad definice najdete níže:

 <?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/5.2/phpunit.xsd"
         backupGlobals="false"
         backupStaticAttributes="false"
         beStrictAboutCoversAnnotation="true"
         beStrictAboutOutputDuringTests="true"
         beStrictAboutTestsThatDoNotTestAnything="true"
         beStrictAboutTodoAnnotatedTests="true"
         forceCoversAnnotation="true"
         verbose="true">
    <logging>
        <log type="junit" target="Test/Logs/junit.xml"/>
    </logging>
    <testsuite name="Sample">
        <directory suffix="Test.php">Test</directory>
    </testsuite>
</phpunit>

Definice ve zkratce říká PHPUnitu, že má spustit všchny testy ve složce Test a uložit výsledky v JUnit kompatibilním formátu. Jak se však ukázalo, kompatibilní JUnit formát, který vrátí PHPUnit není doopravdy kompatibilní s JUnit formátem. Proto náš PowerShellový skript obsahuje funkcionalitu, která provede transformaci pomocí XSLTransform souboru (o problému s JUnit a PHPUnit si můžete přečíst třeba zde nebo zde). XSL Transform soubor, který jsem použil vypadá takto:

 <?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="https://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
    <xsl:element name="testsuites">
        <xsl:for-each select="//testsuite[@file]">
                 <xsl:copy-of select="." />
        </xsl:for-each>
    </xsl:element>
</xsl:template>
</xsl:stylesheet>

Uložil jsem ho společně s testy do složky Test jako phpunit2junit.xsl.

Jakmile toto máme hotové, musíme vytvořit task, který sesbírá výsledky testů a nahraje je do VSTS. Z předpřipravených tasků vybereme Publish Test Results (najdete ho v kategorii Test) a nakonfigurujeme ho pro JUnit formát společně s cestou, kde budou výsledky testů uložené (v mém případě Test\Logs\junit-fixed.xml).

Nyní máme PHPUnit nakonfigurovaný a výsledky testů se nám budou ukazovat ve VSTS společně s každým buildem. Díky této funkcionalitě můžeme například využít možnosti pro automatické vytváření Bugu, kdykoliv nějaký test selže a podobně.

vsts_test_result

Publikace

Poslední částí je publikace/nasazení projektu. V našem případě budeme publikovat artefakty do VSTS a následně projekt publikovat na náš server.

Artefakty

Tento krok je dobrovolný, jde prakticky o to, že vezmeme např. vybuildovaný projekt a nahrajeme ho do VSTS nebo do nějakého úložiště. Součástí artefaktů mohou být detailní logy, screenshoty nebo další soubory.

Publikace do Azure Web App

Abychom mohli do Azure Web App publikovat, musíme přidat task zvaný AzureRM App Service. Jednoduše provažte vaše VSTS s Azure předplatným, zvolte web, případně i slot a máte vše nachystané.

vsts_build_task_azurewa_v2

Závěrem

V tomto článku jsme si ukázali jak nastavit jednoduchý DevOps cyklus s nasazením do Azure Web Apps pomocí VSTS. Podobně byste mohli nastavit nasazení na virtuální nebo fyzický server s IIS nebo projekt nasadit na server s Linuxem pomocí možnosti SSH file copy. Výborně na tom je to, že tyto tasky jsou pro vás již připravené.

Dalším z praktických využití by mohla být publikace pluginů pro WordPress. Konkrétně celý proces balení pluginu a publikace může být v jistých případech problematická a zbytečně komplikovaná – když třeba používáte závislosti z Composeru. Navíc by bylo možné automatizovat celou část publikace přes Subversion a skrýt ji tak pod jeden z build tasků.

VSTS vs. nasazení přímo do App Service

Možná již víte, že App Service podporuje možnosti pro customizaci nasazení pomocí vašich vlastních deployment skriptů, které vám usnadní práci při vývoji menších aplikací (například i doplněk Composer toho využívá). Ale pokud chcete spouštět testy nebo chcete využít Composeru v App Service na Linuxu – VSTS bude vaším dobrým společníkem. A díky vlastnímu build agentovi můžete do build procesu začlenit jakýkoliv další nástroj bez omezení.