Intégration continue en PHP : Hudson/Jenkins Ant ou Phing, ...
Par François Lasselin le mardi, décembre 6 2011, 08:16 - Génie Logiciel - Lien permanent
L'intégration continue est une pratique de génie logiciel ayant pour origine le besoin de vérifier que l'avancement des travaux de développement ne provoque pas de régression. Cette pratique agile nécessite un outillage significatif : serveur d’intégration continue, script de déploiement, outil d'analyse, tests unitaires ... On ne détaillera pas ici l’intérêt évident de cet outillage, mais sa mise en œuvre pour PHP avec Jenkins sous Ubuntu/Debian.
Le développement de projet utilisant des méthodes agiles tend à se généraliser. Si la méthode Scrum est plébiscitée mais peu utilisée, on observe que les différentes équipes piochent dans la boite à outils de l'agilité en fonction de leurs besoins, de leur culture et du projet. Parmi ces outils, on trouve l'intégration continue.
L’intégration continue est la coordination de 5 actions:
- Partager
- Ordonnancer
- Déployer
- Tester
- Mesurer
(voir le billet Integration continue en PHP : Hudson/Jenkins Ant ou Phing, ... pour plus d'informations)
Il existe peu d'outils d’intégration continue pour le php. Petit état de l'art:
- Hudson / Jenkins: issue du monde java
- PHPundercontrol est un add-on de Cruisecontrol. Cruisecontrol est un outil écrit en Java.
- Xinc : outil d’intégration en PHP 5, peu populaire.
On s'intéressera ici à Hudson/Jenkins sous Debian/Ubuntu.
Pourquoi 2 noms Hudson ou Jenkins?
Hudson est le projet d'origine soutenu par Oracle. Suite à une évolution dans le positionnement d'Oracle vis à vis de l'open-source et des noms de marques, la communauté à voté le fork du projet pour Jenkins. Les 2 outils évoluent maintenant en parallèle.
Installation Hudson / Jenkins
En 4 lignes de commande :
wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
sudo echo "deb http://pkg.jenkins-ci.org/debian binary/" > /etc/apt/sources.list.d/jenkins.list
sudo aptitude update
sudo aptitude install jenkins
Le serveur écoute sur :http://localhost:8080
Un serveur d'integration continue seul, ne sert pas à grand chose. Divers outils de scripting (phing) tests (phpunit) et d'analyse 'CodeSnifffer, phpcpd, php_depend, phpcr,..) sont nécessaires :
sudo pear upgrade pear
sudo pear channel-discover pear.phpunit.de
sudo pear channel-discover pear.xplib.de
sudo pear channel-discover pear.pdepend.org
sudo pear channel-discover pear.phing.info
sudo pear channel-discover pear.phppro.fr
sudo pear channel-discover components.ez.no
sudo pear update-channels
sudo pear upgrade pear
sudo pear install --alldeps phpunit/PHPUnit
sudo pear install phpunit/phpcpd
sudo pear install --alldeps PHP_CodeSniffer
sudo pear install --alldeps --force pdepend/PHP_Depend
sudo pear install --alldeps phing/phing
sudo pear install phppro/phpcr
sudo pear install PhpDocumentor
sudo pear install XML_Beautifier
pear install phpunit/phploc
Il est nécessaire d'installer des plugins,les plug-ins vont faire le lien entre les outils en ligne de commande et hudson en permettant à hudson d'importer les contenus, d'en faire l'historique, etc ...Via la page "plugins" d'Hudson on installe par exemple:
- Checkstyle
- Clover
- DRY
- Geen Balls (pour avoir du vert au lieu du bleu en cas de succès)
- Git, SVN, ou votre référentiel de source préféré)
- PHPUnit
- Plot (pour traiter le rapport de PHPloc)
Configurer un build
Il faut d'abord recenser les actions à réaliser. Jenkins/Hudson prend en charge le checkout des sources.
Mais il faut encore configurer l'application, jouer les tests, etc ...
Il faut ensuite créer un buil. Pour cela on va utiliser Phing et écrire un script sous la forme d'un fichier xml. Ant et Phing sont très similaires la syntaxe des fichiers xml diffère à peine. La documentation de Phing n'est pas particulièrement complète.
L'exemple est découpé en différentes taches: les "targets". On peut les lancer de manière indépendante(ex: phing configure) ou groupées (ex phing configure phpcs phpcpd pdepend phpunit). Quelques détails:
- la définition des properties: Une des grandes difficultés ce sont les chemins ! Les différents outils appelés en ligne de commande vont prendre des chemins avec / ou sans / à la fin, séparés par des espaces ou des virgules, d'où une multiplication des variables environnements pour les chemins.
- configure: On charge un fichier de configuration au format xml.Configure remet l'application dans un état pré-déterminé. Notamment, on recharge les données à partir d'un dump
- phpcs codesniffer
- phpcpd: copy/past detector
- pdepend
- phpunit
- phploc
- packing: crée une archive du projet pour livrer le client.
- delivery: on upload en ftp l'archive générée précédemment en ftp. passive="true" utilisera le mode passif de transfert. Ce point est absent de la documentation.
Il faut rentrer dedans mais ce n'est pas si compliqué. Surtout la visibilité apporté par ces outils est assez confortable. On en vient à se demander comment on faisait avant. Toutefois, la démarche n'est vraiment complète et ne dévoile son potentielle qu'associé à une vrai démarche de tests de non-régression.
<?xml version="1.0" encoding="UTF-8"?>
<project basedir=".." name="nalis" default="dist">
<property name="projectdir" value="/" override="true" />
<property name="builddir" value="./${projectdir}build" override="true" />
<property name="dir_code_local" value="./${projectdir}code" override="true" />
<property name="excluded_path" value="./${projectdir}*" override="true" />
<property name="srcdir" value="${dir_code_local} " override="true" />
<property name="srcdir-slashed" value="${dir_code_local}/ " override="true" />
<property name="srcdir-coma" value="${dir_code_local}/" override="true" />
<property name="pdo.driver" value="mysql" />
<target name="configure">
<echo msg="configure project " />
<xmlproperty file="${projectdir}/conf.xml.${nalis.target}" prefix="nalis" />
<pdo url="${pdo.driver}:host=${nalis.db.host};dbname=${nalis.db.name}" userId="${nalis.db.username}" password="${nalis.db.password}" onerror="abort">
DROP DATABASE IF EXISTS `${nalis.db.name}`;
CREATE DATABASE `${nalis.db.name}`;
USE `${nalis.db.name}`;
</pdo>
<pdo url="${pdo.driver}:host=${nalis.db.host};dbname=${nalis.db.name}" userId="${nalis.db.username}" password="${nalis.db.password}" onerror="abort">
<fileset dir="${projectdir}/dumps/">
<include name="*.sql" />
</fileset>
UPDATE password set `value`='${nalis.adminpass}' WHERE field ='adminpass';
</pdo>
<exec command="pwd" escape="false" logoutput="true" />
<exec command="rm -rf ./${projectdir}cache" escape="false" logoutput="true" />
<exec command="mkdir ./${projectdir}cache" escape="false" logoutput="true" />
</target>
<target name="phpcs">
<echo msg="PHP CodeSniffer..." />
<exec command="phpcs -n --standard=ZEND --extensions=php,phtml --report=checkstyle ${srcdir}/ > ${builddir}/checkstyle-result.xml" escape="false" />
</target>
<target name="phpcpd">
<echo msg="PHP Copy/Paste detect..." />
<exec command="phpcpd --log-pmd ${builddir}/pmd.xml ${srcdir} " escape="false" />
</target>
<target name="pdepend">
<echo msg="PHPDepend Analysis..." />
<exec command="pdepend --jdepend-xml=${builddir}/jdepend.xml ${srcdir-coma}" logoutput="true"/>
</target>
<target name="phpunit">
<echo msg="Run PHP unit Analysis..." />
<exec command="phpunit --log-junit ${builddir}/phpunit.xml --coverage-clover ${builddir}/clover.xml --coverage-html ${builddir}/ ./${projectdir}tests/PHPUnit/" logoutput="true"/>
</target>
<target name="packing">
<tstamp/>
<echo msg="Packaging and Delivery Project" />
<exec command="tar -zcvf nalis-${DSTAMP}.tar.gz ${builddir}/* --exclude ${builddir}/dumps" escape="false" />
</target>
<target name="delivery">
<tstamp/>
<echo msg="Uploading ..." />
<ftpdeploy host="host" username="username" password="ftppassword" dir="${DSTAMP}/" level="debug" passive="true">
<fileset dir=".">
<include name="*${DSTAMP}.tar.gz"/>
</fileset>
</ftpdeploy>
</target>
</project>
Pour aller plus loin, 2 articles très complets.
http://blog.pascal-martin.fr/post/integration-continue-jenkins-installation-configuration
http://blog.pascal-martin.fr/post/integration-continue-jenkins-projet-php
La discussion continue ailleurs
URL de rétrolien : http://blog.nalis.fr/index.php?trackback/109
Commentaires
J'essaye de m'y mettre mais c''est pas au point (bcp de nouveautés en même temps)
1° phing (ou ant) c'est conseillé ou obligatoire ?
j'avais essayé de mettre ceci moi.
/usr/bin/phpcs -pv --report-checkstyle=$WORKSPACE/checkstyle-result.xml --standard=Zend $WORKSPACE
mais ca me donne
20:17:59 Build step 'Exécuter un script shell' marked build as failure
20:17:59 [CHECKSTYLE] Skipping publisher since build result is FAILURE
(alors que le fichier checkstyle-result.xml a bien été créé )
2° dans l'exemple de phing ci-dessus
project basedir=".." name="nalis" default="dist"
mais il n'y a aucun target dist...
ai-je loupé quelque chose ?
Bonjour Moosh,
1/ Oui, c'est obligatoire. L'un ou l'autre.
Dans ton exemple, le problème vient de $WORKSPACE qui devrait être interpréter par phing ou ant et que le shell essaye de traiter.
pour lancer un script phing:
$ phing target -Dparametre1=value1
2/ En effet, il n'y a pas de target dist ... bien vu.
Merci pour ta vigilance.
Tu n'hésites pas à me contacter en direct (formulaire de contact) si besoin.
Bonne continuation.
Pour les pakages pear, il y a plus court ici :
http://jenkins-php.org/
et qui fait notamment :
pear config-set auto_discover 1
pear install pear.phpqatools.org/phpqatools pear.netpirates.net/phpDox
Bonjour,
Je suis actuellement en train de déployer un tel outil. Serait-il possible d'y inclure Selenium afin de réaliser des tests complémentaires ?
Si oui auriez-vous un conseil à donner ?
Bonjour Guillaume,
Oui, c'est possible. Il y a même plusieurs façon de faire. Tu peux passer par phpunit pour executer du selenium (voir http://blog.nalis.fr/index.php?post/2010/01/14/Les-tests-%3A-la-pratique-pour-le-Web-et-le-PHP ) ou interagir directement avec un serveur selenium et utiliser les plug-in selenium de Jenkins/Hudson.
Cordialement,
Finalement je me tourne vers phpUnderControl qui me semble plus adapté à des projets PHP que Jenkins.