Afin de pouvoir héberger plusieurs dépôts de version sur mon serveur, j'ai tendance à dupliquer la configuration Apache suivante :
<Directory "/path/to/repository">
AuthType Basic
AuthName "repository login"
AuthGroupFile /dev/null
AuthUserFile /path/to/repository/.htpasswd
<LimitExcept GET>
require valid-user
</LimitExcept>
AllowOverride None
Options None
Order allow,deny
Allow from all
</Directory>
ScriptAliasMatch ^/repository(.*) /path/to/repository/hgweb.cgi$1
Seulement, cette configuration oblige à copier le script CGI hgweb.cgi dans tous les dépôts, ce qui n'est évidemment pas très propre. Une solution plus acceptable serait de placer ce script dans le dossier CGI standard d'Apache (/usr/local/www/apache22/cgi-bin sous FreeBSD), et d'utiliser toujours le même.
J'ai donc regardé le contenu de ce script (ici sans les commentaires) :
#!/usr/bin/env python from mercurial import demandimport; demandimport.enable() from mercurial.hgweb.hgweb_mod import hgweb import mercurial.hgweb.wsgicgi as wsgicgi import os repo_path = os.getcwd() application = hgweb(repo_path, os.path.basename(repo_path)) wsgicgi.launch(application)
Très rapidement, on voit que les modules de mercurial sont importés, puis le script récupère le chemin du répertoire courant et lance l'application hgweb à partir de ce paramètre.
Dans l'état actuel des choses, si on place ce script dans le dossier cgi-bin d'Apache, repo_path contiendra toujours le chemin de ce dossier. L'idée du hack est donc de transmettre l'information du chemin du dépôt au script CGI.
La première solution qui vient à l'esprit est d'utiliser une variable d'environnement. Seulement, dans le cas où plusieurs dépôts doivent être gérés dans le même VirtualHost (ou pour le serveur global), une seule variable d'environnement est utilisable.
Mon idée est de le passer en paramètre du script CGI à l'aide de la ligne suivante :
ScriptAliasMatch ^/repository(.*) /path/to/repository/hgweb.cgi$1
En effet, le script peut récupérer la chaîne $1, située après le chemin du script. En ajoutant les séparateurs /_/ permettant d'isoler le chemin du dépôt, on obtient par exemple :
ScriptAliasMatch ^/repository(.*) /path/to/repository/hgweb.cgi/_/path/to/repository/_/$1
Il reste ensuite à modifier le script CGI de manière à ce qu'il récupère le chemin en question, le retire des variables d'environnement concernées (afin que l'application hgweb ne soit pas perturbée), et lance l'application comme avant :
#!/usr/bin/env python
from mercurial import demandimport; demandimport.enable()
from mercurial.hgweb.hgweb_mod import hgweb
import mercurial.hgweb.wsgicgi as wsgicgi
import os
repo_path = os.getcwd()
path_info = os.getenv('PATH_INFO', '')
if path_info.startswith('/_/'):
repo_path, new_path_info = path_info[2:].split('/_', 1)
os.environ['PATH_INFO'] = new_path_info
os.environ['PATH_TRANSLATED'] = os.environ['DOCUMENT_ROOT'] + new_path_info
application = hgweb(repo_path, os.path.basename(repo_path))
wsgicgi.launch(application)
Quelques explications s'imposent. Comme avant, on récupère le chemin du répertoire où se situe le script CGI. Puis, on récupère la variable d'environnement PATH_INFO. Si elle commence par notre séparateur, alors on isole le chemin du dépôt des paramètres de hgweb. On met à jour les variables d'environnement PATH_INFO et PATH_TRANSLATED (même si je pense que seule la première est utilisée par hgweb). Enfin, on lance l'application en utilisant, soit le chemin où se situe le script CGI, soit le chemin passé en paramètre.
Une fois ce script installé, la configuration d'Apache est légèrement différente :
<Location "/repository">
AuthType Basic
AuthName "repository login"
AuthGroupFile /dev/null
AuthUserFile /path/to/repository/.htpasswd
<LimitExcept GET>
require valid-user
</LimitExcept>
AllowOverride None
Options None
Order allow,deny
Allow from all
</Location>
ScriptAliasMatch ^/repository(.*) /usr/local/www/apache22/cgi-bin/hgweb.cgi/_/path/to/repository/_/$1
L'autorisation ne s'effectue plus sur le répertoire du dépôt /path/to/repository/, mais sur l'emplacement /repository.
Ce hack est certainement faisable d'une manière plus propre, mais comme je n'ai rien trouvé de mieux pour l'instant, je le propose. Si quelqu'un connaît une autre manière de faire, je lui serai gré de m'en faire part dans les commentaire parce qu'elle m'intéresse !