Subversion (SVN) ist unumstritten eines der populärsten Versionsverwaltungssysteme auf dem Markt. Große Opensource Projekte wie z.B. PHP, Ruby uvm. nutzen Subversion als Versionsverwaltung.
Neben dem recht einfachen Einstieg bietet SVN eine Reihe von fortgeschrittenen Funktionen, die ein großes Maß an Kontrolle über den verwalteten Quelltext ermöglichen. Eine dieser Funktionen sind die sogenannten Hooks - Aktionen die zu einem bestimmten Zeitpunkt ausgelöst werden.
Wikipedia Hook (EDV)
SVN bietet also über Hooks die Möglichkeit den Ablauf zu modifizieren. Konkret bedeutet das, dass wir beispielsweise beliebige Programme anstoßen können, bevor ein Commit passiert (pre-commit)- so auch eine eigene PHP-CLI Applikation
Zunächst brauchen wir also eine Übersicht über die aktuell 9 verschiedenen Hooks. Jeder Hook wird an einer anderen Stelle ausgeführt, stellt andere Parameter zur Verfügung und reagiert anders auf die Exit Codes der ausgeführten Programme. Es folgt also zuerst eine Übersicht über die verfügbaren Hooks - durch einen Klick auf den jeweiligen Hook wird eine kurze Beschreibung, Art und Anzahl der Parameter sowie mögliche Einsatzzwecke eingeblendet
Der start-commit Hook wird ausgeführt bevor die Commit-Transaktion existiert. In der Regel wird er genutzt um die Rechte des Benutzers zu überprüfen
Wenn das von dem Hook ausgeführte Programm einen Rückgabewert ungleich null zurück gibt, wird der Commit gestoppt bevor die Commit-Transaktion erstellt wurde. Alle auf dem stderr-Stream
getätigten Ausgaben werden an den Client übermittelt.
Die folgenden Kommandozeilen-Argumente, in der Reihenfolge der Auflistung, werden an das Hook Programm gesendet:
Zugriffskontrolle
Der pre-commit Hook wird ausgeführt bevor die Commit-Transaktion zu einer neue Revision führt. In der Regel wird er genutzt um den übermittelten Inhalt oder die log message zu validieren (Quelltext-Überprüfung, log message muss ticket id enthalten usw.).
Wenn das von dem Hook ausgeführte Programm einen Rückgabewert ungleich null zurück gibt, wird der Commit abgebrochen und die Commit-Transaktion entfernt. Alle auf dem stderr-Stream
getätigten Ausgaben werden an den Client übermittelt.
Die folgenden Kommandozeilen-Argumente, in der Reihenfolge der Auflistung, werden an das Hook Programm gesendet:
Validierung und Kontrolle der übermittelten Änderungen sowie der log message.
Der post-commit Hook wird ausgeführt nachdem die Transaktion committed und eine neue Revision angelegt wurde. Meistens wird dieser Hook verwendet um zusammenfassende E-Mails zu verschicken oder andere Tools anzustoßen. Manchmal wird dieser Hook auch als Auslöser für Sicherungen genutzt.
Wenn das von dem Hook ausgeführte Programm einen Rückgabewert ungleich null zurück gibt, wird der Commit nicht abgebrochen da die Transaktion bereits beendet und die neue Revision angelegt wurde. Alle auf dem stderr-Stream
getätigten Ausgaben werden an den Client übermittelt.
Die folgenden Kommandozeilen-Argumente, in der Reihenfolge der Auflistung, werden an das Hook Programm gesendet:
Commit Benachrichtigung, Tool Integration
Der pre-revprop-change Hook wird ausgeführt bevor eine Änderung einer Revisions-Eigenschaft außerhalb des Kontextes eines normalen Commits durchgeführt werden soll. Anders als bei anderen Hooks wird die gewünscht Aktion immer abgebrochen wenn dieser Hook nicht existiert oder der Rückgabewert nicht gleich null ist.
Wenn das von dem Hook ausgeführte Programm einen Rückgabewert ungleich null zurück gibt, nicht ausführbar ist oder der Hook nicht existiert wird die Änderung der Eigenschaft unterbunden. Alle auf dem stderr-Stream getätigten Ausgaben werden an den Client übermittelt.
Die folgenden Kommandozeilen-Argumente, in der Reihenfolge der Auflistung, werden an das Hook Programm gesendet:
Zugriffskontrolle. Validierung und Kontrolle.
Der post-revprop-change Hook wird ausgeführt nachdem eine Änderung einer Revisions-Eigenschaft außerhalb des Kontextes eines normalen Commits durchgeführt wurde. In der Regel wird er verwendet um Benachrichtigungen zu verschicken.
Wenn das von dem Hook ausgeführte Programm einen Rückgabewert ungleich null zurück gibt wird die Änderung der Eigenschaft nicht unterbunden da diese bereits vollzogen wurde. Alle auf dem stderr-Stream getätigten Ausgaben werden an den Client übermittelt.
Die folgenden Kommandozeilen-Argumente, in der Reihenfolge der Auflistung, werden an das Hook Programm gesendet:
Benachrichtigung
Der pre-lock Hook wird ausgeführt bevor ein Pfad gesperrt(lock) werden soll. Er kann genutzt werden um das Sperren gänzlich zu unterbinden oder an Richtlinien zu binden. Er kann außerdem genutzt werden um bereits existierende Sperren von anderen Benutzern "klauen" zu lassen.
Wenn das von dem Hook ausgeführte Programm einen Rückgabewert ungleich null zurück gibt, wird die Sperrung abgebrochen. Alle auf dem stderr-Stream getätigten Ausgaben werden an den Client übermittelt.
Die folgenden Kommandozeilen-Argumente, in der Reihenfolge der Auflistung, werden an das Hook Programm gesendet:
Zugriffskontrolle
Der post-lock Hook wird ausgeführt nachdem ein Pfad gesperrt(lock) wurde. In der Regel wird er eingesetzt um Benachrichtigungen über die Sperrung zu verschicken.
Wenn das von dem Hook ausgeführte Programm einen Rückgabewert ungleich null zurück gibt, wird die Sperrung nicht abgebrochen da sie bereits durchgeführt wurde. Alle auf dem stderr-Stream getätigten Ausgaben werden an den Client übermittelt.
Die folgenden Kommandozeilen-Argumente, in der Reihenfolge der Auflistung, werden an das Hook Programm gesendet:
Benachrichtigung
Der pre-unlock Hook wird ausgeführt wenn eine Sperrung aufgehoben werden soll. Er kann benutzt werden um zu entscheiden welcher Benutzer Sperren entfernen darf (auch von anderen Benutzern).
Wenn das von dem Hook ausgeführte Programm einen Rückgabewert ungleich null zurück gibt, wird das Aufheben der Sperre abgebrochen. Alle auf dem stderr-Stream getätigten Ausgaben werden an den Client übermittelt.
Die folgenden Kommandozeilen-Argumente, in der Reihenfolge der Auflistung, werden an das Hook Programm gesendet:
Zugriffskontrolle
Der post-unlock Hook wird ausgeführt wenn eine Sperrung aufgehoben wurde. In der Regel wird er eingesetzt um Benachrichtigungen über die Entsperrung zu verschicken.
Wenn das von dem Hook ausgeführte Programm einen Rückgabewert ungleich null zurück gibt, wird das Aufheben der Sperre nicht abgebrochen da die Entsperrung bereits passiert ist. Alle auf dem stderr-Stream getätigten Ausgaben werden an den Client übermittelt.
Die folgenden Kommandozeilen-Argumente, in der Reihenfolge der Auflistung, werden an das Hook Programm gesendet:
Benachrichtigung
Auf *nix-Systemen gibt es drei Standard Streams (Standard-Datenströme). Der stderr-Stream ist, wer hätte das gedacht, für Fehlerausgaben vorgesehen. Ein echo in PHP sendet auf dem stdout-Stream, dem Stream für Standardausgaben. Glücklicherweise stellt uns PHP eine simple Methode zur Verfügung um auf nahezu beliebigen Streams zu senden. So sendet zum Beispiel folgendes Listing ein simples "Fehler aufgetreten" auf dem stderr-Stream:
<?php
Exit Codes auf *nix-Systemen sind ganzzahlige Rückgabewerte von 0 bis 255, die ein Prozess nach dessen Beendigung an die aufrufende Instanz zurück gibt. Über den Exit Code informiert der aufgerufene Prozess über Erfolg oder Misserfolg der eigenen Ausführung. Es gibt keinen Standard für die 256 möglichen Werte, nur die Konvention, dass der Exit Code 0 (null) für Erfolg steht und alles ungleich 0 Misserfolg bedeutet.
In PHP kann man den Exit Code über die Funktion exit(int $status) (gleichbedeutend zu die()) senden. Wird exit() ohne Parameter oder garnicht aufgerufen, so sendet das PHP-Script den Exit Code 0 (also erfolgreich) zurück. Der Exit Code 255 ist für den PHP-Prozess reserviert und sollte nicht verwendet werden.
Wer auch nur eine der obigen Hook-Beschreibungen gelesen hat, versteht jetzt vielleicht, was mit dem Gespann Exit Code und stderr-Stream machbar ist:
Mit dem Exit Code können wir dem ausgewählten Hook mitteilen, ob dieser erfolgreich ausgeführt wurde und per stderr-Stream können wir dem SVN-Client per Text mitteilen, was im Falle eines Exit Codes ungleich null falsch gelaufen ist.
Das Verzeichnis hooks sollte folgende Hook Templates enthalten:
entwicklung:repository AKirchner$ ls -al drwxr----- 2 AKirchner .. 4096 27. Sep 22:53 conf drwxr-S--- 6 AKirchner .. 4096 27. Sep 22:53 db -rwxr----- 1 AKirchner .. 2 27. Sep 22:53 format drwxr----- 2 AKirchner .. 4096 27. Sep 22:53 hooks drwxr----- 2 AKirchner .. 4096 27. Sep 22:53 locks -rwxr----- 1 AKirchner .. 229 27. Sep 22:53 README.txt
Um nun einen Hook zu aktivieren genügt es, die Endung .tmpl zu entfernen. Das in der Hook-Datei enthaltende Shell-Script wird ab sofort an entsprechender Stelle ausgeführt.
entwicklung:repository AKirchner$ ls -al hooks -rwxr----- 1 AKirchner .. 2083 26. Sep 01:52 post-commit.tmpl -rwxr----- 1 AKirchner .. 1690 26. Sep 02:36 post-lock.tmpl -rwxr----- 1 AKirchner .. 2307 26. Sep 02:36 post-revprop-change.tmpl -rwxr----- 1 AKirchner .. 1606 26. Sep 02:36 post-unlock.tmpl -rwxr----- 1 AKirchner .. 2982 26. Sep 02:36 pre-commit.tmpl -rwxr----- 1 AKirchner .. 2038 26. Sep 02:36 pre-lock.tmpl -rwxr----- 1 AKirchner .. 2764 26. Sep 02:36 pre-revprop-change.tmpl -rwxr----- 1 AKirchner .. 1980 26. Sep 02:36 pre-unlock.tmpl -rwxr----- 1 AKirchner .. 2758 26. Sep 02:36 start-commit.tmpl
$ mv post-commit.tmpl post-commit
#!/bin/sh REPOS="$1" REV="$2" /pfad/zum/hook/script/post-commit.php "$REPOS" "$REV"
Wie in der Auflistung der Hooks nachzulesen ist, enthält der post-commit Hook zwei Parameter. Das vorangegangene Shell-Script ruft das PHP-Script post-commit.php mit diesen beiden Parametern auf.
Ich habe im Folgenden ein paar Beispielscripte angefügt. Mit einem Klick auf den Dateinamen wird der Inhalt eingeblendet. Dort sind auch rudimentäre Kommentare enthalten, die allerdings zum Verständnis ausreichen sollten.
Plain TextQuelltext herunterladen
#!/usr/bin/php <?php include 'Stream.php'; include 'Mail.php'; include 'SVNLook.php'; try { // Neue Instanz von SVNLook mit Übergabe des Repository Pfades $oRepoLook = new SVNLook($argv[1]); // Beliebige Benachrichtigung (hier per mail()) } catch(Exception $e) { // Wenn irgendwas schief geht, wird die Exception-Message aus dem STDERR-Stream gesendet // SVN leitet diese Nachricht an den SVN Client weiter Stream::send($e->getMessage(), Stream::STDERR); // Exit Code ungleich Null um die Fehlermeldung dem SVN Client anzuzeigen }
Plain TextQuelltext herunterladen
<?php class Stream { const STDOUT = 'php://stdout'; const STDERR = 'php://stderr'; public static function send( $sMessage, $sStream = self::STDOUT ) { } }
Plain TextQuelltext herunterladen
<?php public static function send( $sTo, $sSubject, $sMessage ) { } }
Plain TextQuelltext herunterladen
<?php class SVNLook { protected $_sSVNLookPath = '/usr/bin/svnlook'; protected $_sRepoPath = ''; protected $_sAuthor = ''; protected $_sDate = ''; protected $_sMessage = ''; public function __construct( $sRepoPath ) { throw new Exception('Repository Path fehlt'); } $this->_sRepoPath = $sRepoPath; $this->_fetchLook(); } protected function _fetchLook() { // Kommando zusammensetzen $sCommand = $this->_sSVNLookPath .' info '. $this->_sRepoPath; // exec() gibt den Output aus STDOUT in einem Array zurück $iReturnVar = 0; // Kommando ausführen // Wenn Exit Code ungleich null, wird eine Exception geschmissen if($iReturnVar) { throw new Exception('SVNLook nicht erfolgreich'); } $this->_parseLookOutput($aOutput); } protected function _parseLookOutput( $aOutput ) { // Erste Zeile des Outputs enthält den Commit-Autor $this->_sAuthor = $aOutput[0]; // Zweite Zeile des Outputs enthält das Datum $this->_sDate = $aOutput[1]; // Ab der vierten Zeile folgt die log-message } public function getAuthor() { return $this->_sAuthor; } return $this->_sDate; } public function getMessage() { return $this->_sMessage; } }
Grundsätzlich gibt es zwei Wege PHP-Scripte per Command Line Interface auszuführen (ja ja es gibt noch mehr..):
1.: Wie in der Beispiel-Datei post-commit.php: Dort wird der Pfad des zu nutzenen Interpreters in der ersten Zeile definiert (hier: #!/usr/bin/php). Anschließend fehlt nur noch das Ausführungsrecht (execute = x) und das Script kann wie folgt ausgeführt werden:
$ chmod u+x post-commit.php $ ./post-commit.php
$ /usr/bin/php post-commit.php
Mit dem Einsatz von Hooks hält man ein mächtiges Werkzeug in den Händen. Die Einsatzmöglichkeiten sind nahezu unbegrenzt jedoch nicht immer sinnvoll. Nils von PHP hates me hat sich in dem Artikel "Warum SVN Pre Commit Hooks böse sind!" Gedanken um den Einsatz des pre-commit Hooks gemacht.
Der Einsatz von PHP erleichtert vielen PHP-Entwicklern die Implementierung auch von komplexen Prüf-Abläufen.