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.

<?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>
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.

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