Introduction

Je gère de plus en plus de choses sous Git: mon blog, mes exemples de code, mes configurations, mes projets de développement, etc. A la longue, on finit par avoir de nombreux projets et il devient presque indispensable de pouvoir disposer d'une interface pour mieux les gérer, les consulter, les évaluer. Mon travail avec QGIS m'amène souvent à utiliser l'interface de GitHub pour consulter le code source du projet lorsque je remonte des bugs. De fait, j'ai trouvé que c'était assez intéressant de pouvoir naviguer rapidement dans le code, directement depuis son navigateur web, en quelques clics.

En effet, si je prends l'exemple de QGIS, cloner le dépôt revient à télécharger près de 1Go de données. Quand c'est juste pour faire un grep pour trouver une variable ou une méthode, ça fait cher de la recherche ! Par ailleurs, un dépôt Git de code a une vie bien à lui. Par exemple, certains dépôts sont très actifs pendant une période donnée et après, parce que le projet est abouti, le dépôt devient une archive. Sur ce cas particulier, on peut penser que le dépôt ne sera plus cloné ailleurs que sur le serveur lui-même et, si on a besoin de consulter le code, il faudra à nouveau le cloner, le consulter, faire des recherches, etc... Bref, se replonger dans le code. Avec le temps, vous aurez sans doute également oublié l'URL qui permet de cloner le dépôt.

C'est là qu'une interface web qui vous permet de naviguer de manière centralisée dans vos dépôts se révèle être un grand atout. Vous avez une seule URL à retenir pour accéder au résumé de vos projets. Si vous n'êtes pas chez vous, vous pouvez cloner facilement vos dépôts, soit à l'aide de Git soit en téléchargeant une archive. Vous avez accès à l'historique de votre développement ce qui vous permet de retrouver facilement ce que vous avez mis en place précédemment. Enfin, comme sous GitHub ou GitLab, vous pouvez lire le code source directement ce qui est une fonctionnalité très pratique au quotidien, notamment pour la revue de bogues.

Les plateformes GitHub, BitBucket, GitLab et consors présentent ces fonctionnalités depuis leurs débuts. Mais comme vous le savez si vous lisez ce blog, je pense qu'il est important de décentraliser le maximum de ressources sur Internet. Dans cet objectif, je vous propose de mettre en place une solution de consultation de dépôts Git que vous pourrez administrer par vos propres soins, de manière complètement autonome et indépendante des silos habituels...

Besoins et réponse

Il est temps de reprendre et de lister ce dont nous avons besoin:

  • Il nous faut un navigateur "Web" de dépôts Git.
  • Cette interface de navigation doit être un logiciel libre.
  • Cette interface sera disponible uniquement via HTTPS.
  • Elle imposera une authentification HTTP.
  • La gestion de droits sera triviale: celui qui peut accéder à l'interface aura un accès complet à l'interface.
  • L'interface doit être capable de gérer automatiquement tout nouveau projet (il ne faut PAS devoir modifier la configuration de l'interface).
  • Le programme de l'interface doit être rapide et léger. Il est destiné à tourner sur un plugcomputer, une machine qui possède à peu près les caractéristiques techniques des serveurs d'il y a 15 ans !
  • Si possible, l'interface ne doit pas imposer l'installation d'un framework lourd (JAVA/Ruby/Qt/Gtk/etc. excluded !).
  • L'interface doit permettre de consulter le source des projets.
  • L'interface doit offrir une visualisation des différents commits.
  • Il faudra pouvoir télécharger des archives de code depuis l'interface.

La réponse se trouve sous la forme d'un excellent programme nommé CGit. Il est écrit en C et se propose d'être une interface très (hyper) rapide de consultation de dépôts Git. Il se présente sous la forme d'un binaire CGI qui génère des pages Web en se basant sur le contenu d'un fichier de configuration et surtout du contenu de vos dépôts Git.

Attaquons maintenant sa mise en place et sa configuration.

Installation de cgit

Si vous êtes sous Debian Jessie (version dans laquelle vous DEVRIEZ être depuis le mois de mai 2015), un simple

# apt install cgit

devrait suffire.

Un rapide coup d'oeil au paquet permet de voir que l'implémentation de CGit est très simple. En effet, l'essentiel du programme est concentré dans le fichier binaire /usr/lib/cgit/cgit.cgi qui fait tout le travail. Le paquet Debian livre un fichier de configuration de base (/etc/cgitrc) ainsi qu'une configuration pour Apache v2.4 (/etc/apache2/conf-available/cgit.conf). Enfin, quelques scripts de filtre sont disponibles dans /usr/lib/cgit/filters. Ces derniers permettent de customiser la mise en forme des rendus des pages d'information, des adresses de courrier électronique ainsi que de la visualisation des codes sources.

Note: Pour les personnes qui sont encore sous Debian Wheezy, vous pouvez installer le paquet cgit sans aucun problème si vous disposez d'une machine sous l'architecture armel. C'est le cas des Sheevaplugs par exemple. En effet, le paquet Debian disponible sous Debian Jessie ne nécéssite que la version 2.7 de la libc sous armel alors qu'il nécéssite la version 2.14 de la libc dans les autres architectures. Sous Debian Wheezy, la libc est en version 2.13 ce qui est amplement suffisant pour faire tourner le binaire.

Pour information, la configuration d'Apache (v2.4) sous Debian Jessie qui est livrée par défaut pour cgit ne me convient pas. Il faut donc la modifier pour prendre en compte nos besoins, notamment sur la partie authentification. Pour ma part, je souhaite que l'interface soit disponible sur le virtualHost principal sous l'URL /cgit/ uniquement sur un canal chiffré par TLS. De plus, je ne souhaite pas que le dépôt soit public. Je souhaite utiliser l'authentification HTTP d'Apache pour définir qui a droit ou non d'accéder au dépôt. Voici ce que j'ai ajouté à mon fichier de configuration de site dédié au HTTPS /etc/apache2/sites-available/medspx.fr.tls:

ScriptAlias /cgit/ "/usr/lib/cgit/cgit.cgi/"
RedirectMatch ^/cgit$ /cgit/
Alias /cgit-css "/usr/share/cgit/"
<Directory "/usr/lib/cgit/">
   AllowOverride None
   Options ExecCGI FollowSymlinks
   AuthType Basic
   AuthName "CGit authentication"
   AuthUserFile /etc/apache2/webdav-users
   Require valid-user
</Directory>

Ensuite, la configuration d'Apache est classique. Il faut juste prendre le temps de supprimer la configuration de cgit qui s'enclenche par défaut lors de l'installation du paquet:

# a2disconf cgit
# systemctl restart apache2

Configuration générale de cgit

Cgit concentre sa configuration dans un seul fichier texte: /etc/cgitrc. La syntaxe de ce dernier est assez simple à comprendre. Je vous invite à lire la page de manuel de cgitrc (man cgitrc) qui est très bien faite. Elle décrit l'ensemble des options disponibles.

Voici mon fichier de configuration commenté:

# cgit config
# see cgitrc(5) for details

# Un peu de cosmétique
css=/cgit-css/cgit.css
logo=/favicon.ico
favicon=/favicon.ico

# Customisation de la page d'accueil
root-title=Medspx Git repositories
root-desc=Git repositories navigation
root-readme=/etc/cgit-about.md

# Configuration d'options de base
enable-index-links=1
enable-commit-graph=1
enable-log-filecount=1
enable-log-linecount=1
side-by-side-diffs=1

# Je ne souhaite pas que les robots puissent indexer ce dépôt
robots=noindex, nofollow

# Ici, je veux indiquer une URL pour cloner le dépôt
clone-url=https://$HTTP_HOST/git/$CGIT_REPO_URL

# cgit peut générer des archives à télécharger suivant vos tags
snapshots=tar.gz tar.bz2 zip

# Coloration syntaxique des page A propos et du visualisateur de fichiers
source-filter=/usr/lib/cgit/filters/syntax-highlighting.py
about-filter=/usr/lib/cgit/filters/about-formatting.sh

# La taille des binaires affichés dans le visualisateur est limitée à 200 Ko
max-blob-size=200

# Gestion des mimetypes
mimetype-file=/etc/mime.types

# Les fichiers readme par défaut auront les noms qui suivent
readme=:README.md
readme=:readme.md

# Les dépôts git peuvent implémenter leurs propres filtres
enable-filter-overrides=1

# L'endroit où trouver les dépôts git (le truc le plus important)
scan-path=/var/local/git

Je n'ai pas positionné les directives liées à la gestion du cache de CGit. En effet, mon site web a un traffic très faible et j'ai d'abord créé le dépôt Git pour moi-même. Les besoins sont donc d'un niveau de performance assez restreint. Je peux donc me permettre de conserver la configuration par défaut du cache de CGit qui suffit très largement. N'oubliez pas de remplir le fichier /etc/cgit-about.md avec un descriptif de votre site, ce sera mieux pour l'affichage.

A ce stade, vous disposez d'une interface Web disponible sur votre serveur HTTP à l'URL /cgit/. Si vous avez des dépôts Git dans le répertoire /var/local/git, CGit vous les affichera et vous pourrez commencer à naviguer dedans. Vous pourrez lire le code, visualiser les diffs entre deux commits, voir l'historique du projet, etc. Autre élément d'intérêt, sachez que lorsque la directive snapshots est activée, vous pourrez télécharger une archive depuis chaque commit de votre projet. Si vous avez utilisé des tags, vous pourrez télécharger chaque archive selon vos tags directement depuis la page d'accueil du dépôt. C'est CGit qui génère l'archive et qui la place dans son cache. Pas besoin de gérer ces éléments à la main et c'est tant mieux !

Néanmoins, il manque pas mal de choses sur vos dépôts, le plus frappant étant l'absence de description de chaque dépôt ainsi que les noms bruts des répertoires hébergeants les dépôts. Voyons comment y remédier...

Gérer la configuration de cgit pour chaque dépôt

En règle générale, pour ce qui concerne l'administration système, j'aime les choses statiques. On configure un élément une bonne fois pour toute et après, la configuration est statique et quasiment immuable dans le temps. Donc, sur un serveur en production, tout changement intempestif de ce qui se trouve dans /etc est à éviter (en dehors des évolutions de système). Dans notre cas, le contenu des dépôts Git va évoluer. CGit présente des éléments du projet comme une description, un fichier readme ou about, des commentaires, des statistiques basiques, etc. Or, bien souvent, au cours du projet, ces éléments peuvent varier. Par exemple vous voulez sans doute changer le nom d'un dépôt ou sa description détaillée. Ou alors, un développeur utilise le fichier readme.md pour autre chose que la présentation succincte du projet (ou bien le nom de ce fichier a tout bonnement changé dans le dépôt).

Dans ces situations, il faudra modifier la configuration du dépôt dans cgit. Par défaut, vous devrez donc aller modifier à la main le fichier /etc/cgitrc situé sur le serveur hébergeant le service. Je trouve cette pratique assez fastidieuse. Essayons de trouver un moyen un peu différent pour nous permettre de parvenir à rendre tout ça plus automatique et modulable.

En lisant la documentation de cgit, on se rend compte que la directive enable-git-config permet de mettre les éléments de paramétrage de chaque dépôt dans la configuration de chaque dépôt (le répertoire .git). Voilà qui est déjà mieux... mais pas suffisant ! En effet, la configuration Git du dépôt n'est pas un objet de la base Git. Ainsi, si vous avez réalisé une configuration sur votre clone, elle ne pourra pas atteindre le répertoire .git du dépôt sur le serveur. Par ailleurs, il faut savoir que par défaut, CGit scanne chaque dépôt à la recherche d'un fichier cgitrc qui contient les éléments de configuration du dépôt.

Après de nombreux tests et surtout, une bonne lecture du source de CGit, je me suis rendu compte que ce fichier cgitrc ne peut pas être stocké dans le dépôt. En effet, CGit recherche le fichier dans le répertoire .git. Mais Git est très modulable, notamment avec ses hooks. J'ai donc eu l'idée de permettre de recopier le fichier cgitrc du dépôt dans le répertoire .git afin que CGit puisse en faire bon usage. Voici donc le source du hook post-update qui se déclenche sur le serveur Git une fois l'ensemble des modifications répercutées sur la copie locale du serveur:

#!/bin/sh

# Hook script to copy cgitrc file into the .git directory

CGITCONFIG=$(git ls-tree --name-only master .cgitrc)
if [ "$CGITCONFIG" = ".cgitrc" ]; then
    echo "Copying cgitrc into .git directory..."
    git archive master --format=tar .cgitrc | /bin/tar -x -C $GIT_DIR
    if [ -e "$GIT_DIR/.cgitrc" ]; then
        mv $GIT_DIR/.cgitrc $GIT_DIR/cgitrc
    fi
fi

exit 0

Voici un exemple de configuration d'un fichier cgitrc d'un dépôt:

name=org
desc=My life in Org-Mode
section=Publications
readme=:readme.md
owner=Médéric Ribreux <mederic.ribreux@medspx.fr>

Bien entendu, avec cette méthode, il faut copier le hook dans chaque répertoire .git/hooks de chaque projet. Mais, dans mon cas, l'initialisation d'un dépôt Git même vide implique de se connecter vis SSH et de jouer du shell directement sur le serveur. Je n'ai en effet pas automatisé ce processus pour des questions de simplicité. Néanmoins, j'applique une procédure manuelle assez simple à suivre. Pour éviter d'oublier ce hook, une méthode assez simple consiste à l'installer dans le répertoire de template par défaut. Sous Debian, il s'agit de /usr/lib/git-core/templates/'. Pour une plus grande personnalisation, nous allons créer un répertoire dédié dans/usr/local/`. Puis nous allons y mettre nos templates et enfin, nous allons configurer git pour qu'il utilise ce répertoire par défaut.

# mkdir -p /usr/local/share/git-core/templates/
# cp -r /usr/share/git-core/templates/* /usr/local/share/git-core/templates/
# cp /home/user/post-update /usr/local/share/git-core/templates/hooks/
# chmod 755 /usr/local/share/git-core/templates/hooks/post-update
# git config --system init.templatedir /usr/local/share/git-core/templates

Pour profiter de notre hook, il suffit de créer un fichier .cgitrc à la racine du projet et de faire un commit suivi d'un push !

Une dernière précision ! Vous devez vous assurer que les permissions dans le dépôt Git soient suffisantes pour que l'utilisateur qui va effectuer les opérations via git-http-backend (souvent www-data sous Debian) puisse écrire le fichier au bon endroit. A vos chmod !

Conclusion

CGit est assez intéressant dans son approche. Il est très léger et très performant. De plus, ses fonctionnalités le rendent très intéressant pour les personnes qui ont des besoins assez simples dans la gestion de leurs dépôts Git. Les développeurs confirmés, travaillant avec une communauté de développement étoffée, auront sans doute des besoins plus rigoureux en termes de sécurité et de fonctionnalités de partage.

Par ailleurs, je n'ai pas abordé la gestion des droits en profondeur car cgit n'offre que très peu d'éléments dessus mais il est tout à fait possible de combiner cgit avec un système de gestion de droits comme gitolite par exemple.

Pour ma part, CGit est également un logiciel intéressant de par lui-même. D'abord, il est écrit en C, ce qui, en dehors des logiciels du type outils de traitement bruts ou noyau est rare. De plus, je n'ai pas souvenir d'avoir déjà rencontré un logiciel qui produit du HTML codé en pur C. Ensuite, il reste très performant et finalement assez facile à configurer. CGit est la preuve qu'il est possible combiner développement pointu en C et monde du Web. C'est quelquechose que je souhaiterais avoir plus souvent car cette proposition permet d'utiliser de petites configurations (tant qu'on reste dans des limites correctes) et profiter de fonctionnalités quand même avancées.

Comme point de comparaison, on peut prendre GitLab qui tient une liste de pré-requis techniques sur son site Web. On peut y lire qu'il faut quand même beaucoup de choses pour le faire tourner à minima, notamment Ruby (qui reste assez gourmand sur un plugcomputer) et qu'il faut au moins 1 coeur de CPU pour faire tourner moins de 100 utilisateurs; enfin, 512Mo de RAM est un minimum. Le ticket d'entrée de GitLab est clairement au dessus de ce que peut fournir une petite machine ARM. Certes les fonctionnalités de GitLab sont largement supérieures à celles de CGit mais comme qui peut le plus peut le moins, l'utilisateur qui voudrait utiliser uniquement les fonctions basiques de GitLab serait obligé de disposer d'une puissance que ne pourrait lui fournir un Raspberry Pi 2 par exemple.

A l'inverse, pour le développeur autonome, CGit permet de commencer à mettre quelquechose en place avec sans doute moins de tâches d'administration système (ce simple article en est l'illustration pratique).

Pour terminer, un grand nombre de développeurs Debian utilisent CGit pour leur convenance personnelle (Lars Wizenius par exemple) ou directement dans l'infrastructure de Debian.

Posted ven. 15 mai 2015 19:49:00