Suivi de bugs avec le module Bugnicar 0.9 de NARVAL

Nicolas CHAUVAT

Bruno VAN FRACHEM


Table of Contents

Installation et paramétrage
Mise en oeuvre
Exécution de Bugnicar
Création de la base de données
Utilisation par courriels
Utilisation d'une interface web
Machine d'état correspondante

Abstract

Ce HOW-TO explique comment utiliser le module Bugnicar 0.9 de Narval pour gérer le suivi des bugs et corrections associées d'un programme par l'intermédiaire du courrier électronique ou de formulaires web.

Installation et paramétrage

Téléchargez l'archive contenant les fichiers de Bugnicar et décompressez-la. Vous obtenez un répertoire bugnicar-0.9/ prêt à l'emploi que nous appellerons BUG_HOME.

Nous utiliserons BUG_HOME/data/bugnicar-memory.xml pour donner la bonne valeur à l'attribut path de l'élément mailbox.

Depuis un shell, lancez python, puis importez le module AccessControl qui figure dans BUG_HOME/modules/ et exécutez la commande AccessControl.compile_password('turlututu'). Recopiez la chaîne obtenue comme valeur de l'attribut "password" associé à un login "bugnicar" comme ci-dessous.

<memory>
...
<access-control-list>
    <access-control-item login='bugnicar' password='resultat-de-turlututu'/>
</access-control-list>
<memory>

Pour ne pas prendre de risque, j'ai rajouté les lignes suivantes dans mon .procmailrc et j'ai donné cette boîte à lire à Bugnicar en mettant la même valeur pour l'attribut path de l'élément mailbox.

:0:
* ^Subject: \[BUGNICAR\]
mail/bugnicar

et

<memory>
...
  <mailbox path='/home/nico/mail/bugnicar'/>
...
<memory>

Mise en oeuvre

Exécution de Bugnicar

Pour lancer l'application utilisons le script ~/bin/bugnicar.sh suivant :

#!/bin/sh
cd ~/Narval
export PYTHONPATH=~/Narval
python narval/Engine.py --home ~/bugnicar-0.9/ \
    --start-plan "email.Mail_Sensor" \
    --start-plan "bugnicar.Catch-mail" \
    --start-plan "bugnicar.Task-process" \
    --load-memory-file ~/bugnicar-0.9/data/bugnicar-memory.xml \
    --socket-manager $1

avec BUG_HOME égal à ~/bugnicar-0.9/.

Lancer ~/bin/bugnicar.sh --debug depuis la ligne de commande permet de tester la recette, dont le résultat doit apparaître dans BUG_HOME/data/bugnicar.html. Ce fichier peut-être obtenu simplement en transformant BUG_HOME/data/bugnicar-database.xml avec BUG_HOME/transforms/Bugnicar/bug2htmlreport.xslt.

Création de la base de données

Bugnicar sauvegarde la liste des bugs dans le fichier BUG_HOME/data/bugnicar-database.xml. Pour créer une nouvelle base de données, il suffit de remplacer ce fichier avec la chaîne <bugnicar-database/>. On peut éventuellement y spécifier dès maintenant le nom du projet et l'URL de sa page d'accueil en préférant cette seconde chaîne : <bugnicar-database project='Narval' url='http://www.logilab.org/narval/'/>.

Cette nouvelle version de Bugnicar autorise des commandes supplémentaires pour la gestion des bugs, et utilise en conséquence un format de sauvegarde différent pour sa base de données. Si vous utilisiez déjà Bugnicar 0.1, placez-vous dans le répertoire BUG_HOME et exécutez les commandes suivantes pour convertir votre base au nouveau format :

  mv data/bugnicar-database.xml data/bugnicar-database.xml.old

  4xslt data/bugnicar-database.xml.old extensions/bugnicar-convert-database.xslt \
    > data/bugnicar-database.xml
  

Utilisation par courriels

Pour ajouter un nouveau bug à la base, il suffit d'envoyer un courriel dont le sujet est [BUGNICAR] bug report about toto et le corps du message contient les détails.

Pour ajouter un commentaire au rapport de bug numéro 1, il suffit d'envoyer un courriel dont le sujet est [BUGNICAR] bug comment 1 avec vos remarques dans le corps du message.

Pour assigner le bug numéro 1 à quelqu'un (identifié par son adresse électronique), il suffit d'envoyer un courriel ayant pour sujet [BUGNICAR] bug assign 1 pass turlututu to toi@la-bas.org, en joignant éventuellement un commentaire dans le corps du message. Cette commande n'est accessible qu'aux administrateurs en utilisant le mot de passe, ici 'turlututu'.

Pour changer le contenu du champ 'about' du bug numéro 1, il suffit d'envoyer un courriel ayant pour sujet [BUGNICAR] bug set 1 pass turlututu about titi, pas toto, en joignant éventuellement un commentaire dans le corps du message. Cette commande n'est accessible qu'aux administrateurs.

Pour classer le bug numéro 1 qui a été réparé, il suffit d'envoyer un courriel ayant pour sujet [BUGNICAR] bug close 1 pass turlututu , en joignant éventuellement un commentaire dans le corps du message. Cette commande n'est accessible qu'aux administrateurs.

Pour réouvrir le bug classé numéro 1, il suffit d'envoyer un courriel ayant pour sujet [BUGNICAR] bug reopen 1 pass turlututu , en joignant éventuellement un commentaire dans le corps du message. Cette commande n'est admissible qu'aux administrateurs.

Utilisation d'une interface web

Il est désormais également possible d'utiliser une interface web pour consulter les reports de bugs et envoyer des commandes ; NARVAL reçoit alors des requêtes HTTP et non plus des courriels. Un exemple d'interface utilisant HTML et PHP3 est fourni dans BUG_HOME/data/bugnicar/.

Pour adapter l'interface à votre système, placez-vous dans le répertoire BUG_HOME/data/bugnicar/ et exécutez le script suivant, où MACHINE doit être remplacé par le nom du serveur sur lequel tourne NARVAL ; ce script modifiera les formulaires en leur donnant la bonne adresse.

  #!/bin/sh
  my_machine=MACHINE
  for i in `ls *.php3`
  do a=`cat $i`;
     printf "$a" | sed "s/action=\"http:\/\/.*:/action=\"http:\/\/$my_machine:/g" > $i
  done
  

Machine d'état correspondante

<?xml version="1.0"?>

<state-machine xmlns:xupdate="http://www.xmldb.org/xupdate" name="buggy">
 <object nodename="bug">
  <storage type="plain-file" uri="$NARVAL_HOME/data/bugnicar-database.xml">
   <location>/bugnicar-database</location>
  </storage>
  <get-object-from-storage>
   <event>
    <element called="operation" nodename="bugnicar-task">
     <match>@for</match>
     <match>not(bug-report)</match>
    </element>
   </event>
   <query>@id = #operation#/@for</query>
  </get-object-from-storage>
 </object>

 <!-- States declaration -->

 <state id="not-assigned">
  <description>
   <match>@state = 'not-assigned'</match>
  </description>
 </state>


 <state id="assigned">
  <description>
   <match>@state = 'assigned'</match>
  </description>
 </state>


 <state id="fixed">
  <description>
   <match>@state = 'fixed'</match>
  </description>
 </state>

 <!-- Initialization declaration -->

 <transition type="init" id="add-new" out="not-assigned">
  <event>
   <element called="operation" nodename="bugnicar-task">
    <match>bug-report</match>
   </element>
  </event>
  <actions>
   <create-object>
    <xupdate:attribute name="id">
     <xupdate:value-of select="position()"/>
    </xupdate:attribute>
    <xupdate:attribute name="state">not-assigned</xupdate:attribute>
    <xupdate:element name="label">
     <xupdate:value-of select="#operation#/about/text()" />
    </xupdate:element>
    <xupdate:element name="report-info">
     <from><xupdate:value-of select="#operation#/bug-report/@sender" /></from>
     <date><xupdate:value-of select="#operation#/bug-report/@date" /></date>
     <description><xupdate:value-of select="#operation#/text()" /></description>
    </xupdate:element>
    <xupdate:element name="comments" />
   </create-object>
  </actions>
 </transition>


 <!-- Destroy declaration -->
 <transition type="destr" id="remove" in="any">
  <event>
   <element called="operation" nodename="bugnicar-task">
    <match>remove</match>
   </element>
  </event>
  <actions>
   <xupdate:remove select="#object#" />
  </actions>
 </transition>


 <!-- Transitions declaration -->

 <transition id="set-label-not-assigned" in="not-assigned" out="not-assigned">
  <event>
   <element called="operation" nodename="bugnicar-task">
    <match>set-label</match>
   </element>
  </event>
  <actions>
   <xupdate:remove select="#object#/label"/>
   <xupdate:element name="label">
    <xupdate:value-of select="#operation#/about/text()" />
   </xupdate:element>
  </actions>
 </transition>

 <transition id="set-label-assigned" in="assigned" out="assigned">
  <event>
   <element called="operation" nodename="bugnicar-task">
    <match>set-label</match>
   </element>
  </event>
  <actions>
   <xupdate:remove select="#object#/label"/>
   <xupdate:element name="label">
    <xupdate:value-of select="#operation#/about/text()" />
   </xupdate:element>
  </actions>
 </transition>

 <transition id="set-label-fixed" in="fixed" out="fixed">
  <event>
   <element called="operation" nodename="bugnicar-task">
    <match>set-label</match>
   </element>
  </event>
  <actions>
   <xupdate:remove select="#object#/label"/>
   <xupdate:element name="label">
    <xupdate:value-of select="#operation#/about/text()" />
   </xupdate:element>
  </actions>
 </transition>

 <transition id="add-comment" in="not-assigned" out="not-assigned">
  <event>
   <element called="operation" nodename="bugnicar-task">
    <match>bug-comment</match>
   </element>
  </event>
  <actions>
   <modify-object>
    <xupdate:append select="#object#/comments">
     <xupdate:element name="comment">
      <xupdate:attribute name='from'><xupdate:value-of select="#operation#/bug-comment/@sender" /></xupdate:attribute>
      <xupdate:attribute name='date'><xupdate:value-of select="#operation#/bug-comment/@date" /></xupdate:attribute>
       <xupdate:value-of select="#operation#/text()" />
     </xupdate:element>
    </xupdate:append>
   </modify-object>
  </actions>
 </transition>


 <transition id="add-comment-when-assigned" in="assigned" out="assigned">
  <event>
   <element called="operation" nodename="bugnicar-task">
    <match>bug-comment</match>
   </element>
  </event>
  <actions>
   <modify-object>
    <xupdate:append select="#object#/comments">
     <xupdate:element name="comment">
      <comment>
       <xupdate:attribute name='from'><xupdate:value-of select="#operation#/bug-comment/@sender" /></xupdate:attribute>
       <xupdate:attribute name='date'><xupdate:value-of select="#operation#/bug-comment/@date" /></xupdate:attribute>
       <xupdate:value-of select="#operation#/text()" />
      </comment>
     </xupdate:element>
    </xupdate:append>
   </modify-object>
  </actions>
 </transition>


 <transition id="assign" in="not-assigned" out="assigned">
  <event>
   <element called="operation" nodename="bugnicar-task">
    <match>bug-assign</match>
    <match>@password</match>
   </element>
  </event>
  <actions>
   <modify-object>
    <xupdate:append select="#object#">
    <xupdate:attribute name="state">assigned</xupdate:attribute>  
     <xupdate:element name="worker">
      <xupdate:attribute name="id"><xupdate:value-of select="#operation#/worker-id/text()" /></xupdate:attribute>
      <xupdate:attribute name='date'><xupdate:value-of select="#operation#/bug-assign/@date" /></xupdate:attribute>
     </xupdate:element>
    </xupdate:append>
   </modify-object>
  </actions>
 </transition>


 <transition id="unassign" in="assigned" out="not-assigned">
  <event>
   <element called="operation" nodename="bugnicar-task">
    <match>bug-unassign</match>
    <match>@password</match>
   </element>
  </event>
  <actions>
   <modify-object>
    <xupdate:append select="#object#">
     <xupdate:attribute name="state">not-assigned</xupdate:attribute>  
    </xupdate:append>
    <xupdate:remove select="#object#/worker"/>
   </modify-object>
  </actions>
 </transition>


 <transition id="change-assignment" in="assigned" out="assigned">
  <event>
   <element called="operation" nodename="bugnicar-task">
    <match>bug-assign</match>
    <match>@password</match>
   </element>
  </event>
  <conditions>
   <match-cond>#operation#/worker-id/text()</match-cond>
  </conditions>
  <actions>
   <modify-object>
    <xupdate:remove select="#object#/worker"/>
   </modify-object>

   <modify-object>
    <xupdate:append select="#object#">
     <xupdate:element name="worker">
      <xupdate:attribute name="id"><xupdate:value-of select="#operation#/worker-id/text()" /></xupdate:attribute>
      <xupdate:attribute name='date'><xupdate:value-of select="#operation#/bug-assign/@date" /></xupdate:attribute>
     </xupdate:element>
    </xupdate:append>
   </modify-object>
	
  </actions>
 </transition>


 <transition id="close-bug" in="assigned" out="fixed">
  <event>
   <element called="operation" nodename="bugnicar-task">
    <match>bug-close</match>
    <match>@password</match>
   </element>
  </event>
  <actions>
   <modify-object>
    <xupdate:append select="#object#">
    <xupdate:attribute name="state">fixed</xupdate:attribute>
    <xupdate:attribute name='date'><xupdate:value-of select="#operation#/bug-close/@date" /></xupdate:attribute>
    </xupdate:append>
   </modify-object>
	
  </actions>
 </transition>



 <transition id="reopen-bug" in="fixed" out="assigned">
  <event>
   <element called="operation" nodename="bugnicar-task">
    <match>bug-reopen</match>
    <match>@password</match>
   </element>
  </event>
  <actions>
   <modify-object>
    <xupdate:append select="#object#">
     <xupdate:attribute name="state">assigned</xupdate:attribute> 
    </xupdate:append>
   </modify-object>
	
  </actions>
 </transition>


</state-machine>