Buildscripts mit PHING – Teil 1

Avatar von Alberto Assmann

Als Dienstleister veröffentlicht man selten Software direkt auf dem Zielserver.
Meist übergibt man dem Auftraggeber ein Paket, welches sämtlichen Projektcode enthält und eine entsprechende Installationsanleitung.
Nun ist das Vorbereiten eines solchen Paketes eine mühsame Angelegenheit.
Es müssen alle Testdateien entfernt werden, da diese auf einem Produktivsystem nichts zu suchen haben.
Verschiedene SQL-Dateien müssen zu einer zusammengefügt werden, der Kunde möchte schließlich beim Deploy so einfache Anweisungen wie möglich haben.
Diese und viele weitere Aufgaben fallen an, bevor eine Software an den Kunden ausgeliefert werden kann.
Nun entwickeln wir Software, und es kommt auch bei noch so gründlicher Entwicklung und Tests immer wieder zu Bugs.
Nach dem Fixen des Bugs muss also nochmals der Deploymentprozess Schritt für Schritt durchgegangen werden.
All das kostet Zeit, da bei jeder Übergabe an den Kunden die immer gleichen Schritte durchgeführt werden müssen.

Warum also nicht automatisieren?
Ein bekanntes Tool um diese Automatisierung des Deployments zu ermöglichen ist ANT.
ANT hat für uns PHP-Entwickler aber einen großen Nachteil. Es ist in Java geschrieben.
Da wir PHP gewohnt sind, hat sich jemand die Mühe gemacht und das Konzept von ANT portiert.
Daraus ist PHING entstanden.

PHING selbst ist ein PEAR Package, welches ab PHP 5.2 läuft und kann folgendermaßen installiert werden:

pear channel-discover pear.phing.info
pear install phing/phing

Zusätzlich sollte man, sofern mit SVN gearbeitet wird, das VersionControl_SVN PEAR Package installieren:

pear install VersionControl_SVN

Die Verwendung wird folgend an einem virtuellen Projekt demonstriert.
Das Projekt ist ein Zend Frameworkprojekt, hat also die folgende Ordnerstruktur:

|--application
|  <code>{{EJS0}}</code>--models
|  <code>{{EJS1}}</code>--configs
|--library
|  <code>{{EJS2}}</code>--sql
|--public
|  <code>{{EJS3}}</code>--css
|--tests
|  <code>{{EJS4}}</code>--models
|  `--library

Unsere Schritte für das Bilden des Paketes sind, aufgeteilt auf drei Artikel:

Teil 1:
1) Laden der Einstellungsdatei
2) Abfragen der SVN Nutzerdaten
3) Das Projekt aus einem SVN Repository exportieren

Teil 2:
7) Alle SQL-Files im Data Ordner zusammenführen

Teil 3:
8) Alle dotfiles löschen, außer .htaccess
9) Den Testordner löschen
10) Data Ordner löschen
11) Archiv des Quellcodes schnüren
12) Archiv hashen und den Hash in eine Datei schreiben
13) Generiertes SQL-File, Hashfile und Archiv in den Zielordner verschieben
14) Abarbeitung der einzelnen Targets

PHING Buildfiles sind XML-Dateien die build.xml heißen und aus einem Projekt bestehen, welches unterteilt ist in Targets.
Alternative kann das Buildscript beliebig benannt werden, dann muss PHING jedoch mit dem Argument -buildfile filename aufgerufen werden.

Ein Target ist eine einzelne Aufgabe und kann separat ausgeführt werden über phing <targetname>

Das Gerüst für ein Buildfile sieht wie folgt aus:

<?xml version="1.0" encoding="UTF-8"?>
<project name="FooBar" default="help" basedir=".">
    <target name="help" description="Help page">
        <!-- do something -->
    </target>
</project>

Im default Attribut des project Tags wird das Target angegeben, welches bei einem Aufruf des Buildscripts ohne Angabe eines Targets aufgerufen wird.
Das basedir Attribut gibt den Pfad an, auf den sich alle nicht absoluten Pfadangaben innerhalb des Buildfiles beziehen.

Ein Target hat immer einen eindeutigen Namen und sollte stets im description Attribut eine Beschreibung enthalten, was dieser Task tut.
Innerhalb eines Targets können verschiedene vordefinierte, bzw. eigene Tasks aufgerufen werden.
Ein Task ist ein Einzelschritt, beispielsweise das Löschen einer Datei.
Eine Übersicht über die vorhandenen Tasks ist in der Dokumentation unter Appendix B und Appendix C zu finden.

Unsere erste Aufgabe wird es sein eine Konfigurationsdatei einzulesen.
Konfigurationsdateien sind einfach Ini-Dateien, in welchen Key-Value-Paare abgespeichert sind.
PHING kann von Haus aus nicht mit Sektionen umgehen, es empfiehlt sich also diese nicht in der Konfigurationsdatei zu verwenden, da PHING ansonsten immer das letzte Vorkommen des Keys als Wert nimmt.
Auch auf Anführungszeichen um Werte sollte man verzichten, da diese als zum Wert zugehörig angenommen werden.
Unsere Konfigurationsdatei (build.ini) besteht vorerst nur aus der Angabe der URL zum SVN Repository:

; SVN repository url
svn.url = svn://svn.demo.de/svnroot/trunk

Einlesen können wir diese Datei in unser Buildfile folgendermaßen:

<target name="define" descprition="Defines all needed properties">
    <property file="path/to/file/build.ini" /> 
</target>

Eine andere Möglichkeit ist es Variablen direkt zu definieren, ebenfalls mit Hilfe des property Tasks:

<target name="define" descprition="Defines all needed properties">
    <property name="svn.user" value="anonymous" override="true"/> 
</target>

Das name Attribut gibt den Variablennamen an, unter dem innerhalb des Buildscripts auf die Variable zugegriffen werden kann und das value Attribut den entsprechenden Wert.
Das Attribut override gibt an ob eine gleichnamige Variable überschrieben werden soll, sofern eine solche existiert.
Das normale Verhalten ist diese nicht zu überschreiben.

Der Zugriff auf eine definierte Variable innerhalb des Buildscripts erfolgt durch
${variablenname}, in unserem Fall also durch ${svn.url}, bzw. ${svn.user}.

Nun gibt es Daten, wie etwa Passwörter, die weder in eine Konfigurationsdatei, noch direkt in das Buildscript gehören.
Solche Daten kann man innerhalb des Buildprozesses vom Nutzer abfragen.
Der zugehörige Task heißt PropertyPrompt.

<propertyprompt propertyname="svn.password" defaultvaule="" 
prompttext="Enter SVN password: "/>

Genutzt werden kann die Eingabe wiederum durch ${svn.password}.
Sollte keine Eingabe erfolgt sein, so wird der Wert aus dem defaultvalue Attribut genommen.

Da nun alle wichtigen Daten des SNV Repositories vorhanden sind kann ein Export gestartet werden.
Der SVN Export unterscheidet sich vom Checkout dadurch das keine .svn Dateien übertragen werden, was einiges an Aufräumarbeit erspart.
Auch für diese Aufgabe gibt es wieder einen vordefinierten Task, welcher SvnExport heißt.
Für das Exportieren wird ein neues Target angelegt, welches als Abhängigkeit das define Target ausführt bevor es selbst zur Ausführung kommt:

<target name="export" description="Get all files from svn repository" depends="define">
    <svnexport
        svnpath="/usr/bin/svn"
        username="${svn.user}"
        password="${svn.password}"
        force="true"
        nocache="true"
        repositoryurl="${svn.url}"
        todir="./svn_export"
        ignoreexternals="false"
        />
</target>
Dieses Target holt alle Dateien aus dem angegebenen SVN Repository und kopiert diese in den unter todir angegebenen Ordner.

Das Attribut force sorgt dafür das eventuell schon vorhandene Dateien überschrieben werden und ignoreexternals stellt sicher das auch externe Verweise mit kopiert werden.

Das bisherige Buildfile sollte in etwa wie folgt aussehen:

<?xml version="1.0" encoding="UTF-8"?>
<project name="FooBar" default="export" basedir=".">
    <target name="define" descprition="Defines all needed properties">
        <property file="path/to/file/build.ini" />
        <property name="svn.user" value="anonymous" override="true"/>
        <propertyprompt propertyname="svn.password" defaultvaule="" 
         prompttext="Enter SVN password: "/> 
    </target>
    <target name="export" description="Get all files from svn repository" depends="define">
        <svnexport
            svnpath="/usr/bin/svn"
            username="${svn.user}"
            password="${svn.password}"
            force="true"
            nocache="true"
            repositoryurl="${svn.url}"
            todir="./svn_export"
            ignoreexternals="false"
        />
    </target>
</project>

Im nächsten Teil werden wir uns anschauen wie eigene Task geschrieben und ausgeführt werden können.

Avatar von Alberto Assmann

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert


Für das Handling unseres Newsletters nutzen wir den Dienst HubSpot. Mehr Informationen, insbesondere auch zu Deinem Widerrufsrecht, kannst Du jederzeit unserer Datenschutzerklärung entnehmen.