(Re-)Bonjour à tous :-)

Les dernières discussions autour de Ncooker m'ont amené à réfléchir sur les 
fichiers utilisés pour créer un paquet NBUILD.

Je viens de terminer le traitement du fichier changelog dans la commande 
Check. Pour analyser les informations qui se trouvent dans ce fichier, j'ai 
du écrire un mini-parser qui permet de récupérer les éléments qui constituent 
les entrées de ce fichier (entête, informations ...). Dernièrement, Geekitus 
a proposé d'ajouter une nouvelle information dans les nbuilds pour connaître 
le motif de sortie d'une release (security-fix, bug-fix ...). Et l'ajout de 
cette information oblige à modifier le parser pour qu'il puisse reconnaître 
la nouvelle syntaxe.
En fait, la moindre modification de syntaxe dans ce fichier oblige à modifier 
le parser. Par exemple, chaque entrée du fichier changelog à une entête de la 
forme :

% <release> <author name and email> <creation date>

le simple fait de changer l'ordre des informations, par exemple :

% <author name and email> <creation date> <release>

oblige à revoir le parser.
Et ce problème n'est pas spécifique au fichier changelog, car pour le fichier 
« desc », c'est pareil. Chaque description commence par un entête 
%Summary[<lang>] ou %Description[<lang>]. La moindre modification ou ajout 
d'informations oblige à revoir le parser du fichier desc.

Le seul fichier qui ne pose pas ce type de problème est le fichier « infos ». 
Et la raison en est simple : il n'y a pas besoin de développer un parser, 
parce que le parser c'est ... Bash lui-même ! :-) En effet, le fichier infos 
est un simple script bash contenant des déclarations de variables. Il est 
donc possible de profiter des différentes types de déclaration fournis par 
bash dans ce fichier (simple variable, tableau ...). Toute modification ou 
ajout d'informations dans ce fichier ne pose donc aucun problème, on utilise 
ce que bash propose.
Avec ce fichier, il y a un problème d'un autre ordre : vu qu'il s'agit d'un 
script bash, un petit malin peut très bien y glisser de « vicieuses » 
commandes, comme un « rm -rf $HOME » par exemple. Pour limiter ce genre de 
choses, les variables du fichier infos sont évaluées « à la demande ». Par 
exemple, lorsque la commande check doit vérifier la valeur de la variable 
NPKG_VERSION, on ne fait pas :

source infos

qui exécuterait tout le fichier (et donc les commandes qui s'y trouvent), 
mais :

eval `cat infos | grep "^NPKG_VERSION"`

pour exécuter uniquement la déclaration de la variable NPKG_VERSION.
Pour autant, cela ne protège pas de tout. Il suffit de déclarer la variable 
NPKG_VERSION comme ceci dans le fichier infos :

NPKG_VERSION=`rm -rf $HOME; echo 0`

pour que notre répertoire home soit irrémédiablement effacé.

Pour essayer de contourner ce problème, j'ai proposé d'écrire un petit jeu de 
fonctions pour récupérer une variable et sa valeur dans le fichier infos sans 
exécuter les éventuelles commandes qui pourraient y être glissées. C'est ce 
sur quoi marc[i1] s'est proposé de travailler. Le fichier infos ne serait 
alors plus un script bash, mais un simple fichier texte. 
Après réflexion, je me suis dit que ça consistait finalement à écrire à 
nouveau un parser qui analyserait la syntaxe du fichier infos pour retrouver 
la variable demandée et sa valeur. Du coup, on retrouve le même risque que 
celui évoqué pour les fichiers changelog et desc :-) : la moindre 
modification dans la syntaxe va nous obliger à revoir le code de ce parser.


Idéalement, il faudrait que les fichiers utilisés pour créer un nbuild (ou 
tout du moins les fichiers infos, desc et changelog) soient de simples 
fichiers textes pour empêcher l'exécution de commandes. Mais il faudrait 
aussi que la syntaxe utilisée pour y déclarer des informations soit 
suffisamment souple pour pouvoir ajouter de nouvelles informations ou 
modifier la manière dont sont déclarées les informations existantes sans 
avoir à retoucher le code du parser (« extensibilité » de la syntaxe).
Pour cela, il y a deux solutions possibles : soit on crée une nouvelle syntaxe 
permettant de répondre à ces besoins, ainsi que le parser associé ; soit on 
regarde du coté des solutions existantes.

La solution qui m'est venue à l'esprit, ou plutôt revenue, est le format XML. 
Je dis « revenue » parce que j'avais envisagé d'utiliser ce langage pour les 
fichiers de configuration il y a déjà un petit moment, mais je n'y voyais par 
de réel intérêt. Maintenant que la version de développement de Ncooker a pas 
mal avancé, les problèmes évoqués plus haut sont apparus (bien que certains 
existent déjà avec la version actuelle de Ncooker) et plusieurs 
caractéristiques du langage XML sont devenues intéressantes pour les 
résoudre : les fichiers XML sont de simples fichiers textes non exécutables, 
ce qui rend impossible l'exécution de commandes malicieuses, et le langage 
XPath permet d'accéder directement à une information donnée d'un fichier XML, 
comme ce qu'on cherche à faire avec la commande grep ci-dessus.


J'ai imaginé la solution suivante en utilisant ce langage : l'idée est de 
réunir le contenu des fichiers infos, desc et changelog en un unique fichier 
XML nommé infos ; comme desc et changelog contiennent des informations qui 
concerne le paquet et son contenu au même titre que infos, autant les réunir 
dans un même fichier.

Voici un exemple :

<NPkg>
    <project>
        <version>21.2</version>
        <license>GPL</license>
        <author>RMS</author>
        <copyrights>Richard Stallman</copyrights>
        <homepage>http://www.gnu.org/software/emacs/emacs.html</homepage>

        <files>
            <archive file="emacs-$version.tar.gz" 
checksum="442fed534043a8d66976bdfd7b80eedf">
                <url>mirror://gnu/emacs/</url>
                <url>ftp://ftp.gnu.org/emacs/</url>
            </archive>
        </files>

        <domain>
            <node>/kind/application</node>
            <node>/interface/text/command-line</node>
            <node>/topic/system/console/terminal</node>
            <node>/audience/end-user</node>
        </domain>

        <purpose lang="en">
            <summary>A full-featured text editor.</summary>
            <description>here is the full description of the 
software.</description>
        </purpose>
        <purpose lang="fr">
            <summary>Un éditeur de texte avancé.</summary>
            <description>Voici la description complète du 
logiciel</description>
        </purpose>
    </project>

    <package release="1">
        <maintainer name="Gontran" email="[EMAIL PROTECTED]" />
        <changelog>
            <release version="21.2-nga1" author="Gontran <[EMAIL PROTECTED]>" 
date="2005-07-08T18:03:00Z" purpose="security-fix bug-fix">
                - First package release for version 21.2
            </release>
        </changelog>
    </package>
</NPkg>

Ici, le fichier est composé de deux parties :
* il y a les informations sur le logiciel parmi lesquelles on retrouve sa 
version, sa licence, son auteur, son copyright et l'url de son site web. Puis 
viennent les fichiers sources à télécharger, suivis de la définition du 
domaine d'application du logiciel et enfin sa description dans différentes 
langues.
* il y a les informations sur le paquet lui-même (<package>) qui comprennent 
son mainteneur et les modifications qui ont été apportées aux releases 
successives (<changelog>).

Quels sont les outils permettant d'utiliser un fichier XML en bash ?

Il y a le toolkit StarLet (http://xmlstar.sourceforge.net/) qui offre un 
ensemble complet d'outils pour manipuler des fichiers XML en ligne de 
commande. Il permet de valider la structure d'un document XML par rapport à 
une DTD (Document Type Definition, document décrivant la structure que doit 
avoir un document XML), de récupérer des informations à l'aide de chemins 
définis avec le langage XPath, et même de modifier ou d'ajouter des 
informations au sein du fichier. Ces outils sont écrits en C et sont basés 
sur les librairies libxml2 et libxslt.

Récupérer la version du logiciel dans une variable de Ncooker peut se faire 
comme ceci :

NPKG_VERSION=`xml sel -t -v "/NPkg/project/version" infos`

Une fois cette syntaxe connue, il suffit de changer le chemin entre guillemets 
pour accéder au contenu de n'importe quel élément. Mais le toolkit StarLet 
permet de faire des opérations bien plus puissantes :-)


Pour moi, cette solution présente les avantages suivants :

- réduction de 50% du nombre de fichiers nécessaires pour créer un paquet 
nbuild, puisqu'on passe de 4 fichiers (desc, changelog, infos et build) à 2 
(infos et build). Combinée à la proposition que j'ai faite pour le fichier 
build dans un autre post, un unique fichier infos peut suffir pour créer 
certains paquets NBUILD :-)

- Sécurité : en définissant une DTD, on peut utiliser StarLet pour vérifier la 
syntaxe et la structure du fichier infos. Toute anomalie sera alors détectée, 
qu'il s'agisse d'un texte mal placée ou de l'utilisation d'une balise non 
reconnue ;

- Evolutivité : si on a besoin d'ajouter des informations comme la raison 
d'une nouvelle release d'un paquet proposé par Geekitus, il suffit de mettre 
à jour la DTD. Pas de parser à mettre à jour : les développeurs peuvent se 
concentrer sur le coeur même du programme.

- Souplesse : l'ordre dans lequel apparaissent les éléments importe peu dans 
le fichier XML, seule l'imbrication des balises doit être respectée. On peut 
indifféremment déclarer la section <package> avant <project> ou inversement 
sans que cela pose problème.

- Facilité d'utilisation des Nbuilds par des applications tierce : comme de 
nombreux langages de développement dispose de fonctions XML (Python, Ruby, 
Perl, ...), des applications développées dans ces langages (comme Nsetup) 
peuvent directement lire les informations contenues dans un nbuild sans avoir 
à passer par Ncooker. De même, Le site web étant fait en PHP, on peut faire 
très facilement une page affichant les informations des nbuilds situés sur le 
site puisque PHP permet d'utiliser facilement des fichiers XML. Mieux : il 
suffit simplement d'extraire le fichier infos d'un nbuild avec PHP et de le 
renvoyer au navigateur accompagné d'une feuille de style pour obtenir une 
belle page présentant toutes les informations :-)

Voilà, dites-moi ce que vous en pensez ;-)

++
Gontran

Répondre à