Catégories: Article | Subversion | Svn | Outil
Les hooks de Subversion
Un article de ToutProgrammer.com.
Un référentiel est une base de données qui nécessite une rigueur quant aux ressources qui y sont ajoutées. Subversion propose une méthode élégante et puissante pour sécuriser l'ajout et la modification des ressources du dépôt en fournissant un mécanisme appelé "hook".
Sommaire |
[modifier] Qu'est-ce qu'un hook dans Subversion?
Subversion est un gestionnaire de versions certainement promis à un grand avenir. Il offre un grand nombre d'atouts par rapport à son principal concurrent qu'est CVS. De bonnes idées déjà utilisées dans CVS ont été ré-implémentées dans Subversion, mais en améliorant grandement le système. C'est le cas par exemple avec les hooks de Subversion qui sont une adaptation des fichiers commitinfo, loginfo, taginfo, et verifymsg de CVS.
Les hooks sont des programmes (scripts ou binaires) "côté serveur" appelés à des moments particuliers lors de certaines opérations du serveur Subversion. Chaque dépôt Subversion sur le serveur dispose de ses propres hooks situés dans le répertoire hooks. Suivant la plateforme, le nom des programmes peut être légèrement différent. En effet, si sur un système compatible Unix le programme porte le nom du hook, sur un système Windows, le nom correspondra au nom du hook auquel est ajouté une extension .exe, .com ou .bat.
Subversion exploite les 5 hooks suivants (ordre chronologique):
- start-commit,
- pre-commit,
- post-commit,
- pre-revprop-change,
- post-revprop-change.
Comme nous ne verrons au long de cet article, les 3 premiers hooks sont exécutés lors d'une transaction commit Subversion alors que les 2 derniers hooks sont exécutés lors de la mise à jour de propriétés Subversion.
Lors de la création d'un dépôt sur le serveur, une arborescence similaire à celle-ci est créée:
. |-- README.txt |-- conf | `-- svnserve.conf |-- dav | `-- activities |-- db | |-- ... | `-- ... |-- format |-- hooks | |-- post-commit.tmpl | |-- post-revprop-change.tmpl | |-- pre-commit.tmpl | |-- pre-revprop-change.tmpl | `-- start-commit.tmpl `-- locks |-- db-logs.lock `-- db.lock
Dans le répertoire des hooks du dépôt, Subversion ajoute un modèle (avec l'extension .tmpl) pour chacun des hooks disponibles. Vous découvrirez le contenu de ces modèles dans la suite de cet article.
Lors de l'invocation de chacun de ces hooks, des paramètres sont transmis afin de fournir des informations complémentaires sur l'opération en cours. En retour, certains hooks indiquent si l'opération peut continuer normalement ou s'il faut la stopper prématurément. Dans le cas d'un arrêt prématuré, tout message sur la sortie des erreurs STDERR est retourné au client Subversion pour informer l'utilisateur de la raison de l'échec.
| Note importante |
|---|
| Dans la suite de cet article, il sera utilisé les termes de transaction commit et commit de transaction. Ceux-ci bien que proches sont assez différents. Un dépôt Subversion est une base de données. Comme pour toute base de données, Subversion doit commiter (confirmer) ou rollbacker (annuler) la transaction en cours. S'il y a confirmation, ce sera le commit de transaction. Pendant l'exécution de certains hooks, il est possible de lire le contenu d'une transaction de base de données. IL NE FAUT JAMAIS MODIFIER le contenu d'une transaction pendant l'appel d'un hook. La transaction commit correspond quant-à elle à l'utilisation de la sous-commande Subversion commit' lorsque vous faites par exemplesvn commit. |
Voyons maintenant à quoi servent ces différents hooks et comment les mettre en oeuvre.
[modifier] start-commit
Ce hook est appelé par le serveur Subversion juste avant la création de la transaction du commit Subversion dans le dépôt.
Le bloc en jaune sur ce graphique représente la transaction commit (txn) de Subversion. Il ne faut pas confondre ce commit Subversion avec le commit de la base de données (en noir).
Voici le modèle de hook start-commit ajouté dans le répertoire hooks d'un dépôt:
#!/bin/sh # START-COMMIT HOOK # # The start-commit hook is invoked before a Subversion txn is created # in the process of doing a commit. Subversion runs this hook # by invoking a program (script, executable, binary, etc.) named # 'start-commit' (for which this file is a template) # with the following ordered arguments: # # [1] REPOS-PATH (the path to this repository) # [2] USER (the authenticated user attempting to commit) # # The default working directory for the invocation is undefined, so # the program should set one explicitly if it cares. # # If the hook program exits with success, the commit continues; but # if it exits with failure (non-zero), the commit is stopped before # even a Subversion txn is created. # # On a Unix system, the normal procedure is to have 'start-commit' # invoke other programs to do the real work, though it may do the # work itself too. # # Note that 'start-commit' must be executable by the user(s) who will # invoke it (typically the user httpd runs as), and that user must # have filesystem-level permission to access the repository. # # On a Windows system, you should name the hook program # 'start-commit.bat' or 'start-commit.exe', # but the basic idea is the same. # # Here is an example hook script, for a Unix /bin/sh interpreter: REPOS="$1" USER="$2" commit-allower.pl --repository "$REPOS" --user "$USER" || exit 1 special-auth-check.py --user "$USER" --auth-level 3 || exit 1 # All checks passed, so allow the commit. exit 0
Comme nous le voyons, 2 paramètres sont donnés à ce script avec la signification suivante:
| Rang | Nom | Description |
|---|---|---|
| 1 | REPOS-PATH | Chemin complet vers le dépôt/référentiel Subversion sur le serveur. |
| 2 | USER | Identifiant de l'utilisateur qui demande le commit de ressources dans le dépôt. Si l'utilisateur n'est pas authentifié, ce paramètre aura pour valeur une chaine vide. |
start-commit est souvent utilisé pour autoriser ou non le commit d'un utilisateur dans le dépôt indiqué.
Voici un exemple simple pour vérifier qu'un auteur à bien le droit d'écrire dans le dépôt demandé:
#!/bin/sh umask 002 base="/var/lib/svn" repo=$1 user=$2 PATH='/usr/bin:/usr/local/bin' if [ $user = "user1" -a $repo = "$base/repos1" ]; then exit 0 fi if [ $user = "user2" -a $repo = "$base/repos2" ]; then exit 0 fi echo "Bad authenticate for the repository $repo." 1>&2 exit 1
Ainsi, l'utilisateur user1 pourra écrire dans le dépôt /var/lib/svn/repos1 mais pas l'utilisateur user2. A l'inverse, seul l'utilisateur user2 sera autorisé à écrire dans le dépôt /var/lib/svn/repos2 alors que user1 pourra seulement éventuellement le lire.
[modifier] pre-commit
Ce hook est appelé juste avant le commit de la transaction du dépôt (qui fonctionne comme une base de données). A ce stade, les ressources sont prêtent à être confirmées dans le dépôt. Mais le serveur Subversion est encore en mesure d'annuler les modifications/ajouts de ces ressources du dépôt car le commit de la transaction base n'a pas encore eu lieu (en noir ci-dessous).
Lors de la création d'un dépôt, le modèle suivante est créé:
#!/bin/sh # PRE-COMMIT HOOK # # The pre-commit hook is invoked before a Subversion txn is # committed. Subversion runs this hook by invoking a program # (script, executable, binary, etc.) named 'pre-commit' (for which # this file is a template), with the following ordered arguments: # # [1] REPOS-PATH (the path to this repository) # [2] TXN-NAME (the name of the txn about to be committed) # # The default working directory for the invocation is undefined, so # the program should set one explicitly if it cares. # # If the hook program exits with success, the txn is committed; but # if it exits with failure (non-zero), the txn is aborted and no # commit takes place. The hook program can use the 'svnlook' # utility to help it examine the txn. # # On a Unix system, the normal procedure is to have 'pre-commit' # invoke other programs to do the real work, though it may do the # work itself too. # # *** NOTE: THE HOOK PROGRAM MUST NOT MODIFY THE TXN. *** # This is why we recommend using the read-only 'svnlook' utility. # In the future, Subversion may enforce the rule that pre-commit # hooks should not modify txns, or else come up with a mechanism # to make it safe to do so (by informing the committing client of # the changes). However, right now neither mechanism is # implemented, so hook writers just have to be careful. # # Note that 'pre-commit' must be executable by the user(s) who will # invoke it (typically the user httpd runs as), and that user must # have filesystem-level permission to access the repository. # # On a Windows system, you should name the hook program # 'pre-commit.bat' or 'pre-commit.exe', # but the basic idea is the same. # # Here is an example hook script, for a Unix /bin/sh interpreter: REPOS="$1" TXN="$2" # Make sure that the log message contains some text. SVNLOOK=/usr/bin/svnlook $SVNLOOK log -t "$TXN" "$REPOS" | \ grep "[a-zA-Z0-9]" > /dev/null || exit 1 # Check that the author of this commit has the rights to perform # the commit on the files and directories being modified. /usr/lib/subversion/hook-scripts/commit-access-control.pl "$REPOS" "$TXN" commit-access-control.cfg || exit 1 # All checks passed, so allow the commit. exit 0
Deux paramètres sont donnés à ce script avec la signification suivante:
| Rang | Nom | Description |
|---|---|---|
| 1 | REPOS-PATH | Chemin complet vers le dépôt/référentiel Subversion sur le serveur. |
| 2 | TXN-NAME | Nom de la transaction qui va être commitée. |
Les différences notables entre start-commit et pre-commit sont comme vous pouvez le voir sur les paramètres données aux hooks. Comme nous l'avons vu, start-commit proposait uniquement le chemin de dépôt concerné et l'utilisateur faisant la demande de commit. pre-commit quant-à lui ne propose que le chemin du dépôt et l'identifiant de la transaction de base de donnée.
Le hook pre-commit peut être utilisé par exemple pour des vérifications d'état sur les données à commiter (fichier valide, log renseignées, keywords indiqués, ...). Nous verrons ci-dessous qu'il est également possible de l'utiliser d'autres manières.
Comment exploiter le paramètre TXN-NAME? Ceci est très simple puisqu'il suffit de le donner à la commande désirée du programme svnlook fournit avec Subversion.
Le premier exemple de mise en oeuvre du hook pre-commit permettra de voir comment utiliser la commande cat de svnlook. Nous souhaitons que chaque fichier commité dans le dépôt comporte le keyword $Id$. Au cas ou le développeur aurait oublié de le mettre, un message d'information lui sera retourné et la transaction commit sera annulée:
#!/bin/sh
# /!\ ATTENTION: cet exemple part du principe qu'un seul fichier
# est commité à la fois. Pour gérer plusieurs
# fichiers, il est nécessaire de mettre en place
# une boucle. Il faudrait également vérifier que le
# test ne se fait que sur des fichiers.
repos="$1"
txn="$2"
SVNLOOK=/usr/bin/svnlook
# Récupère le nom du fichier concerné par le commit
changed=`$SVNLOOK changed -t "$txn" "$repos"`
file=`echo $changed | awk '{print $2}'`
# Vérifie que le keyword $Id$ est bien présent
$SVNLOOK cat -t "$txn" "$repos" "$file" | grep -q "\$Id\\$"
RET=$?
if [ $RET -ne 0 ]
then
echo "You must use \$Id$ keyword in all files." 1>&2
exit 1
fi
exit 0
La commande cat de svnlook envoie le contenu du fichier indiqué pour la transaction $TXN sur la commande Unix grep. Si la commande grep trouve une ligne, elle retourne un code égal à 0. Si ce n'est pas le cas, nous retournons le message d'information.
La seconde mise en oeuvre de pre-commit sera un petit programme chargé de vérifier que le quota du dépôt indiqué n'est pas dépassé. Si nous souhaitons un strict respect de cette règle, nous devons placer ce test uniquement dans le hook pre-commit. En effet, si ce test est placé dans start-commit et que le quota n'est pas encore dépassé, il y a risque de voir celui-ci énormément dépassé à la fin du commit.
Voici le code pour un système Unix:
#!/bin/sh umask 002 repo=$1 txn=$2 PATH='/usr/bin:/usr/local/bin' MAX_SIZE=20480 repo_size=`du -s $repo | cut -f 1` if [ $repo_size -gt $MAX_SIZE ]; then echo "Repositroy $repo has exceeded maximum size: $MAX_SIZE" 1>&2 exit 1 fi exit 0
Dans ce cas, la quota est limité à 20Mo sur la constante MAX_SIZE. Si la taille du dépôt (retournée par la commande Unix du) est supérieure au quota autorisé, un message d'erreur est retourné au client et la transaction Subversion est "rollbackée".
Le choix entre start-commit et pre-commit doit se faire en fonction de ce que le programme hook doit faire mais aussi en fonction des paramètres donnés aux hooks. Si start-commit servira principalement à déterminer si un utilisateur à le droit de modifier un dépôt, pre-commit servira le plus souvent à faire de la validation sur les ressources qui seront ajoutées au dépôt.
[modifier] post-commit
Ce hook est appelé lorsque la transaction a été commitée et que la version a été créée. Tout code retour renvoyé par le hook est ignoré car il n'est plus possible d'interrompre le traitement (le commit ayant déjà eu lieu).
Voici le modèle post-commit.tmpl créé par svnadmin create:
#!/bin/sh # POST-COMMIT HOOK # # The post-commit hook is invoked after a commit. Subversion runs # this hook by invoking a program (script, executable, binary, etc.) # named 'post-commit' (for which this file is a template) with the # following ordered arguments: # # [1] REPOS-PATH (the path to this repository) # [2] REV (the number of the revision just committed) # # The default working directory for the invocation is undefined, so # the program should set one explicitly if it cares. # # Because the commit has already completed and cannot be undone, # the exit code of the hook program is ignored. The hook program # can use the 'svnlook' utility to help it examine the # newly-committed tree. # # On a Unix system, the normal procedure is to have 'post-commit' # invoke other programs to do the real work, though it may do the # work itself too. # # Note that 'post-commit' must be executable by the user(s) who will # invoke it (typically the user httpd runs as), and that user must # have filesystem-level permission to access the repository. # # On a Windows system, you should name the hook program # 'post-commit.bat' or 'post-commit.exe', # but the basic idea is the same. # # Here is an example hook script, for a Unix /bin/sh interpreter: REPOS="$1" REV="$2" /usr/lib/subversion/hook-scripts/commit-email.pl "$REPOS" "$REV" commit-watchers@example.org
Les 2 paramètres donnés à ce script ont la signification suivante:
| Rang | Nom | Description |
|---|---|---|
| 1 | REPOS-PATH | Chemin complet vers le dépôt/référentiel Subversion sur le serveur. |
| 2 | REV | Numéro de la révision que vient juste d'être commitée. |
post-commit est souvent utilisé pour envoyer un courrier électronique indiquant les modifications qui viennent d'avoir lieu sur le dépôt (avec svnlook).
Les créateurs de Subversion proposent également un script hot-backup.py en Python permettant de faire des sauvegardes à chaud du dépôt, fonctionnalité proposée par Berkeley DB.
[modifier] pre-revprop-change
Subversion permet l'ajout de méta-données (appelées propriétés) sur les révisions des ressources. pre-revprop-change est invoqué par Subversion juste avant qu'une des propriétés d'une révision soit modifiée. Si ce hook retourne un code différent de 0, c'est que l'opération ne doit pas être effectuée.
Le template créé par Subversion est le suivant:
#!/bin/sh # PRE-REVPROP-CHANGE HOOK # # The pre-revprop-change hook is invoked before a revision property # is modified. Subversion runs this hook by invoking a program # (script, executable, binary, etc.) named 'pre-revprop-change' (for which # this file is a template), with the following ordered arguments: # # [1] REPOS-PATH (the path to this repository) # [2] REVISION (the revision being tweaked) # [3] USER (the username of the person tweaking the property) # [4] PROPNAME (the property being set on the revision) # # [STDIN] PROPVAL ** the property value is passed via STDIN. # # If the hook program exits with success, the propchange happens; but # if it exits with failure (non-zero), the propchange doesn't happen. # The hook program can use the 'svnlook' utility to examine the # existing value of the revision property. # # WARNING: unlike other hooks, this hook MUST exist for revision # properties to be changed. If the hook does not exist, Subversion # will behave as if the hook were present, but failed. The reason # for this is that revision properties are UNVERSIONED, meaning that # a successful propchange is destructive; the old value is gone # forever. We recommend the hook back up the old value somewhere. # # On a Unix system, the normal procedure is to have 'pre-revprop-change' # invoke other programs to do the real work, though it may do the # work itself too. # # Note that 'pre-revprop-change' must be executable by the user(s) who will # invoke it (typically the user httpd runs as), and that user must # have filesystem-level permission to access the repository. # # On a Windows system, you should name the hook program # 'pre-revprop-change.bat' or 'pre-revprop-change.exe', # but the basic idea is the same. # # Here is an example hook script, for a Unix /bin/sh interpreter: REPOS="$1" REV="$2" USER="$3" PROPNAME="$4" if [ "$PROPNAME" = "svn:log" ]; then exit 0; fi exit 1
Les paramètres donnés à ce script sont les suivants:
| Rang | Nom | Description |
|---|---|---|
| 1 | REPOS-PATH | Chemin complet vers le dépôt/référentiel Subversion sur le serveur. |
| 2 | REV | Numéro de la révision concernée. |
| 3 | USER | Identifiant de l'utilisateur qui demande la modification de la propriété. |
| 4 | PROPNAME | Nom de la propriété modifiée sur la révision. |
| STDIN | PROPVAL | Valeur qui sera affectée à la propriété. |
Comme Subversion ne versionne pas les propriétés, ce hook permet de limiter leurs modifications.
[modifier] post-revprop-change
Tout comme pre-revprop-change, post-revprop-change est appelé lors de la modification de propriétés. Ce hook est appelé une fois la modification terminée:
Lors de la création du référentiel, le modèle de script suivant est aussi créé:
#!/bin/sh # POST-REVPROP-CHANGE HOOK # # The post-revprop-change hook is invoked after a revision property # has been changed. Subversion runs this hook by invoking a program # (script, executable, binary, etc.) named 'post-revprop-change' # (for which this file is a template), with the following ordered # arguments: # # [1] REPOS-PATH (the path to this repository) # [2] REV (the revision that was tweaked) # [3] USER (the username of the person tweaking the property) # [4] PROPNAME (the property that was changed) # # Because the propchange has already completed and cannot be undone, # the exit code of the hook program is ignored. The hook program # can use the 'svnlook' utility to help it examine the # new property value. # # On a Unix system, the normal procedure is to have 'post-revprop-change' # invoke other programs to do the real work, though it may do the # work itself too. # # Note that 'post-revprop-change' must be executable by the user(s) who will # invoke it (typically the user httpd runs as), and that user must # have filesystem-level permission to access the repository. # # On a Windows system, you should name the hook program # 'post-revprop-change.bat' or 'post-revprop-change.exe', # but the basic idea is the same. # # Here is an example hook script, for a Unix /bin/sh interpreter: REPOS="$1" REV="$2" USER="$3" PROPNAME="$4" /usr/lib/subversion/hook-scripts/propchange-email.pl "$REPOS" "$REV" "$USER" "$PROPNAME" watchers@example.org
Les paramètres donnés à ce script sont les suivants:
| Rang | Nom | Description |
|---|---|---|
| 1 | REPOS-PATH | Chemin complet vers le dépôt/référentiel Subversion sur le serveur. |
| 2 | REV | Numéro de la révision concernée. |
| 3 | USER | Identifiant de l'utilisateur qui demande la modification de la propriété. |
| 4 | PROPNAME | Nom de la propriété modifiée sur la révision. |
Ce hook est surtout utilisé pour archivage. En effet, comme la propriété a déjà été modifiée, il n'est plus possible d'annuler l'opération. C'est pour cette raison aussi que le code retour du hook est ignoré.






