Installation und Einrichtung von phpUnderControl auf Linux-Server

phpUnderControl ist ein AddOn für CruiseControl um seine PHP-Projekte mit Continuous Integration auszustatten.

Am Ende soll ein phpUC Prozess laufen welcher ein git-Repository (Möglich ist hier aber fast jede Versionskontrolle, u.a. auch SVN) nach Änderungen prüft, und bei Änderungen die Fassung pullt und den Buildprozess anstößt.

Installation

Java installieren

Da CruiseControl auf Java aufbaut, wird die java-jre benötigt. Damit alles richtig läuft, sollte hier die Sun-, bzw. jetzt Oracle-Java Version verwendet werden.

User anlegen und CruiseControl installieren

Als erstes legen wir einen eigenen User für CruiseControl an. Under Ubuntu sollte hier adduser verwendet werden, für den Rest gibt es useradd.

skaverat@ratbox:~$ sudo adduser cruisecontrol
Adding user `cruisecontrol' ...
Adding new group `cruisecontrol' (1007) ...
Adding new user `cruisecontrol' (1007) with group `cruisecontrol' ...
Creating home directory `/home/cruisecontrol' ...
Copying files from `/etc/skel' ...
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
Changing the user information for cruisecontrol
Enter the new value, or press ENTER for the default
Full Name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n] y

Jetzt über su cruisecontrol zum neu erstellen User wechseln

su cruisecontrol
cd ~

CruiseControl runterladen und entpacken (Versionsnummer mit der gerade aktuellen Version ersetzen):

wget -O cruisecontrol-bin-2.8.4.zip http://sourceforge.net/projects/cruisecontrol/files/CruiseControl/2.8.4/cruisecontrol-bin-2.8.4.zip/download
unzip cruisecontrol-bin-2.8.4.zip
mv cruisecontrol-bin-2.8.4 cruisecontrol

Damit ist CC einsatzfertig "installiert" - Fehlt noch phpUC

phpUnderControl installieren

Der bevorzugte weg ist laut phpUC über den PEAR-Channel

Falls PEAR noch nicht installiert ist:

sudo aptitude install php-pear
sudo pear update-channels
sudo pear upgrade-all

Nun phpUC installieren:

sudo pear channel-discover components.ez.no
sudo pear channel-discover pear.phpundercontrol.org
sudo pear install --alldeps phpuc/phpUnderControl-beta

Jetzt kann CC durch phpUC gepatched werden:

cruisecontrol@ratbox:~$ phpuc install ~/cruisecontrol/

dies installiert einige zusätzliche Ressourcen und tauscht Templates der originalen Metrics aus.
phpUnderControl ist damit fertig installiert.

CruiseControl konfigurieren

Die config.xml

Die offizielle Doku

Hier werden generelle- und projektspezifische Einstellungen vorgenommen:

<cruisecontrol>
<!-- hier den projektnamen/order (siehe nächster Abschnitt) angeben -->
<project name="phpuctestproject" buildafterfailed="false">
<!-- git plugin laden -->
<plugin name="git" classname="net.sourceforge.cruisecontrol.sourcecontrols.Git"></plugin>
<!-- wenn eine Modifikation gefunden wurde noch 60sek warten um alle Änderungen mitzubekommen -->
<modificationset quietperiod="60">
<git localworkingcopy="projects/${project.name}/source/"></git>
</modificationset>
<!-- Git Bootstrapping auf den source-ordner -->
<bootstrappers>
<gitbootstrapper localWorkingCopy="projects/${project.name}/source" />
</bootstrappers>

<schedule interval="60">
<ant anthome="apache-ant-1.7.0" buildfile="projects/${project.name}/build.xml"></ant>
</schedule>

<listeners>
<currentbuildstatuslistener file="logs/${project.name}/status.txt"></currentbuildstatuslistener>
</listeners>

<!-- php-Unit ergebnisse werden rüberkopiert -->
<log dir="logs/${project.name}">
<merge dir="projects/${project.name}/build/logs/"></merge>
<merge dir="projects/${project.name}/source/test/log/"></merge>
</log>

<!-- hier werden coveragereports und die PHP-Doc in den entsprechenden CC-logordner kopiert -->
<publishers>
<artifactspublisher dir="projects/${project.name}/build/api" dest="artifacts/${project.name}" subdirectory="api"></artifactspublisher>
<artifactspublisher dir="projects/${project.name}/source/test/log/report" dest="artifacts/${project.name}" subdirectory="coverage"></artifactspublisher>
<execute command="phpuc graph logs/${project.name} artifacts/${project.name}"></execute>
</publishers>
</project>
</cruisecontrol>

Projekt erstellen

Um ein Projekt anzulegen muss lediglich ein Ordner in "projects/" angelegt werden. Zusätzlich brauchen wir noch eine Ordnerstruktur die der obigen Konfiguration entspricht:

cd ~/cruisecontrol/projects/
mkdir -p phpuctestproject/{source,build}
mkdir  phpuctestproject/build/{api,logs}

Die build.xml

Die build.xml befindet sich im Projektverzeichnis. Hier also projects/phpuctestproject/build.xml
Hier wird festgelegt welche Programme ausgeführt werden und in welcher Reihenfolge dies geschehen soll. So kann auch festgelegt werden, dass der Buildprozess gar nicht erst weitergeführt werden soll, sobald z.B. PHPUnit fehlschlägt.

<?xml version="1.0" encoding="UTF-8"?>
<project name="phpuctestproject" default="build" basedir=".">
<!-- PHPUnit soll im source/test ordner laufen. Hier liegen alle Unittests -->
<target name="phpunit">
<exec executable="phpunit" dir="${basedir}/source/test" failonerror="on">
</exec>
</target>

<target name="php-documentor">
<exec executable="phpdoc" dir="${basedir}/source">
<!-- mit -d werden die Ordner ausgewählt welche dokumentiert werden sollen, -f schließt einzelne Dateien mit ein -->
<arg line="-ct type -ue on -t ${basedir}/build/api
-tb /usr/share/php/data/phpUnderControl/data/phpdoc
-o HTML:Phpuc:phpuc
-f foo/Bar.php
-d Ordner1,MyLibs">
</arg>
</exec>
</target>

<target name="php-codesniffer">
<exec executable="phpcs" dir="${basedir}/source" output="${basedir}/build/logs/checkstyle.xml">
<!-- auch hier: die gewählten order und Dateien werden beim CodeSniffer mit eingeschlossen -->
<arg line="--report=checkstyle
--standard=ZEND
Order1/ MyLibs foo/bar.php">
</arg>
</exec>
</target>
<!-- Die Reihenfolge in "depends" bestimmt die Reihenfolge des Buildvorgangs -->
<target name="build" depends="phpunit,php-documentor,php-codesniffer"></target>
</project>
< /pre>

Git Repository initialisieren

Damit das Git-Plugin auch weiß von wo es pullen soll, muss der source-Ordner einmal mit git initialisiert werden:

cd source/
git clone example.com/projectrepository.git .

CruiseControl starten

Bevor CC gestartet werden kann, sollten noch einige Anpassungen am Startscript vorgenommen werden.

So finde ich es z.B. eher unpraktisch, dass die CC-Instanz direkt in den Hintergrund geschickt wird. Um dies zu ändern öffnen wir die cruisecontrol.sh im CC-Ordner und löschen das & am Ende von Zeile 109.
Dadurch bleibt der Prozess im Vordergrund und wir können genau sehen was gerade geschieht. Im Produktion sollte das & wieder hinzugefügt werden, oder der Server in einem screen laufen.
Weiterer Vorteil: Ctrl+C kill den Server direkt gracefully.

Ausserdem habe ich auf Port 8080, der Port auf dem CruiseControl starten will, schon einen Dienst laufen. Die webport-Nummer auf den Zeilen 107 und 109 kann daher auf einen freien Port gelegt werden, falls benötigt.

Zum Schluss: Mir wird beim Start der Fehler
./cruisecontrol.sh: line 109: /bin/java: No such file or directory

entgegengeworfen.

Laut whereis liegt mein Java unter /usr/bin/java, daher habe ich dann schnell einen Link darauf angelegt:

cd /bin
sudo ln -s /usr/bin/java java

Ein ./cruisecontrol.sh startet den CC-Server, und kann wie oben erwähnt mit Ctrl-C wieder gekillt werden.
Nach einigen Sekunden kann dann unter localhost:8080/cruisecontrol/ (bzw der oben eingestellte Port) phpUnderControl aufgerufen werden.

Daemonize CruiseControl

Wer die CC-Installation lieber aus dem home-Verzeichnis verbannen, und den Prozess direkt über ein richtiges init.d-Script starten möchte, kann folgendes Startscript verwenden, welches ich bei felixdv gefunden habe.

Dazu muss der CC-Ordner erstmal nach /opt verschoben werden

sudo mv /home/cruisecontrol/cruisecontrol /opt/

Und dann foldendes Startscript als /etc/init.d/cruisecontrol angelegt werden:

#!/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:
. /lib/lsb/init-functions
JAVA_HOME=/usr
NAME=cruisecontrol
DAEMON=/opt/cruisecontrol/cruisecontrol.sh
PIDFILE=/opt/cruisecontrol/cc.pid

test -x $DAEMON || exit 5

RUNASUSER=cruisecontrol
UGID=$(getent passwd $RUNASUSER | cut -f 3,4 -d:) || true

case $1 in
start)
log_daemon_msg "Starting Cruisecontrol server" "cc"
if [ -z "$UGID" ]; then
log_failure_msg "user "$RUNASUSER" does not exist"
exit 1
fi
cd /opt/cruisecontrol/
./cruisecontrol.sh > /dev/null 2>&1
log_end_msg $?
;;
stop)
log_daemon_msg "Stopping Cruisecontrol server" "cc"
start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE
log_end_msg $?
rm -f $PIDFILE
;;
restart|force-reload)
$0 stop && sleep 2 && $0 start
;;
status)
pidofproc -p $PIDFILE $DAEMON >/dev/null
status=$?
if [ $status -eq 0 ]; then
log_success_msg "Cruisecontrol server is running."
else
log_failure_msg "Cruisecontrol server is not running."
fi
exit $status
;;
*)
echo "Usage: $0 {start|stop|restart|force-reload|status}"
exit 2
;;
esac

Das ganze muss noch über sudo chmod +x /etc/init.d/cruisecontrol ausführbar gemacht werden, und schon kann man mit dem cruisecontrol-user das Script verwenden:

cruisecontrol@ratbox:~$ /etc/init.d/cruisecontrol
Usage: /etc/init.d/cruisecontrol {start|stop|restart|force-reload|status}