Artisan Numérique

/développement/gestionnaire de version/sgv/cvs/subversion/ Synchroniser Subversion et CVS

La problématique peut sembler étrange mais la preuve que cela arrive vu que j'y suis confronté. Je travaille depuis longtemps sur Subversion et pour rien au monde je ne reviendrais sous CVS. D'un autre côté certaines forges (typiquement cette de Drupal.org) sont eux sous CVS. Et vu que je suis une feignasse sans nom, l'idée de m'amuser à synchroniser cela à la main ne m'a pas amusé bien longtemps. Et une fois de plus, bash me sauve la mise ;-)

L'idée est de construire un script qui sache :

  • Vérifier que le commit correspondant à une révision touche bien notre projet.
  • Extraire le commentaire d'une révision SVN donnée.
  • Mettre à jour un dépôt temporaire du projet.
  • Synchroniser le dépôt temporaire SVN avec un dépôt temporaire cible CVS.
  • Récupérer la liste des modifications effectuées entre les deux (ajout/suppression).
  • Appliquer les ajouts/suppression au dépot temporaire CVS.
  • Commité les modifications sur le serveur CVS en utilisant le même commentaire que Subversion.

Et pour simplifier encore la manoeuvre, le script en question sera appelé par un hook subversion histoire que tout cela se fasse automatiquement. Tout d'abord commençons par la préparation de tout cela.

La première chose à faire est de créer un dossier à un emplacement donné, par exemple /home/www/repos/mon_projet. Il faut que ce dossier soit accessible par apache avec les droits qui vont bien pour cela, en lecture ET écriture. En effet, il ne faut pas oublier que le hook subversion, c'est Apache qui va le lancer...

Pour être sur de ne pas être coincé par cet aspect droit, le mieux est de bidouiller temporairement le fichier /etc/passwd et de modifier le dossier HOME et l'interpréteur de commande d'apache en quelque chose comme cela :

apache:x:88:88:system user for apache:/home/www:/bin/sh

Attention, ne surtout pas laisser les choses ainsi une fois le tout installé. Le problème de devoir laisser apache faire cela n'est déjà pas très sécurisé comme méthode, mais si vous laissez cela, c'est un très joli trou de sécurité. Lorsque vous aurez fini, veillez à changer l'interpréteur en /bin/false.

Ensuite, faites un sudo apache, vous devriez déboucher dans vote dossier /home/www. Allez dans le sous-dossier repos/mon_projet et faite un check-out du projet subversion. Ceci fait, renommez la racine du projet en svn. Recommencez l'opération pour CVS. Et renommez aussi mais cette fois en cvs. Dans les deux cas, je pars du principe que vos deux outils cvs et subversion sont configurés pour mémoriser les mots de passe. Là aussi, ce n'est pas le top d'un point de vue sécurité. N'utilisez cela que sur un serveur Apache de confiance, genre interne.

Maintenant que nous avons nos deux dépôts, il suffit de coller le script suivant dans le dossier hooks de votre dépôt subversion :

#! /bin/sh

cd $1/$2/cvs

# check if it is the right target
right_target=$(svn log -vr$3 ../svn | grep $2)
if [ -z "$right_target" ] ; then
        exit;
fi

# Grabbing revision log
svn log -r$3 ../svn/ |  awk '
/r'$3'/ { next }
/^\-+/  { skip=1 ; next }
/.*/ {
  if (skip==1) {
    skip=0 ; next
  } else {
    print $0 }
}' > ~/tmp/message


# Updating SVNsandbox
svn up ../svn/

# Updating CVS sandbox
cvs up -Pd

# Synchronizing SVN with CVS
rsync -a --cvs-exclude --delete ../svn/ .

# building CVS update script
function cvs_apply {
   root=$(cat CVS/Root | cut -d":" -f4)/$(cat CVS/Repository)/
   let root_length=${#root}+1

   cvs status | awk '
     /^\?/ { print "cvs add "$2 }
     /Needs Checkout/ { status="cvs delete" }
     /Locally Modified/ { status="" }
     /Up-to-date/ { status="" }
     /Repository/ {
       if  (length(status)>0) {
         print status" "substr($4,'$root_length',length($4)-'$root_length'-1)
       }
     }' > ~/tmp/cvs-update.sh
    if [ -s ~/tmp/cvs-update.sh ] ; then
      sh ~/tmp/cvs-update.sh
      return 1
    else
      return 0
    fi
}

res=1
while [ $res == 1 ] ; do
  cvs_apply
  res=$?
done
cvs commit -F ~/tmp/message
sync-cvs.sh

Maintenant il ne reste plus qu'à donner les droits apache:apache et +x à ce fichier, et ajouter à la fin du script post-commit :

/mon_depot_subversion/hooks/cvs-sync.sh /home/www/repos mon_projet $2 2>&1 | logger

L'argument $2 contient le numéro de révision qui vient d'être commité. Sauvez maintenant le tout, et testez. Le | logger vous permet de voir si des erreurs se produisent dans vos traces syslog.

Conclusion

Outre l'intérêt fonctionnel, ce script permet de voir à quel point la commande awk est aussi peu connue que pratique. C'est un véritable couteau suisse facile à mettre en oeuvre pour parser rapidement des textes.