Fin août, j’ai publié la version 0.9.3 d’einvoicing, ma gem de facturation électronique. Elle n’ajoute ni ne corrige aucune fonctionnalité. La 0.9.2 ne se chargeait pas dans un projet qui n’avait pas déjà bigdecimal, alors que ses 317 tests passaient chez moi.

Chez moi, ça marche

La gem utilise bigdecimal pour les montants et rexml pour lire le XML. Ruby les installait autrefois avec lui, il faut maintenant les déclarer comme dépendances, et je ne l’avais pas fait. Sur mon poste, d’autres gems les installaient déjà. Ailleurs, require "einvoicing" échouait immédiatement, et la validation Peppol dès qu’elle lisait du XML.

Les tests tournent dans le repo. Le package publié ne contient que les fichiers listés dans le gemspec et les dépendances déclarées, et il s’installe sur un Ruby que je ne contrôle pas. Aucun test ne regardait ce package.

Deux blocs reliés par la commande gem build. À gauche, le repo : les fichiers, les gems déjà installées sur le poste, un Gemfile.lock résolu en Ruby 4.0, vérifié par les 317 tests. À droite, le package : seulement les fichiers listés, seulement les dépendances déclarées, installé sur le Ruby de l'utilisateur dès 3.2, vérifié par le nouveau contrôle. En dessous, ce qui peut se perdre au passage : une dépendance, un fichier, une version compatible 3.2.
Les tests vérifient le repo, l'utilisateur installe le package.

Trois écarts entre le repo et le package

Les contrôles ajoutés

Le premier construit la gem comme pour une publication, l’installe dans un environnement vide et la charge. Il compare ensuite, fichier par fichier, ce que le repo utilise à l’exécution et ce que le package contient. Une dépendance non déclarée fait échouer le chargement, un fichier manquant est identifié.

Le second parcourt le lockfile et vérifie que chaque version accepte Ruby 3.2. Une gem qu’il n’a pas pu vérifier, par exemple quand RubyGems limite les requêtes, compte comme un échec.

Ces contrôles avaient eux-mêmes des trous, signalés en relecture. Le premier chargeait le package sans regarder son contenu, si bien que retirer un fichier de traduction laissait tout au vert. Corrigé, il vérifiait qu’un glob du gemspec trouvait quelque chose au lieu de vérifier chaque fichier attendu. J’ai reproduit chaque trou en cassant le package exprès avant de le corriger.

Au-delà de Ruby

Le même écart existe partout où l’on teste une chose et en livre une autre : un installeur essayé sur la machine qui l’a compilé, une image Docker construite avec un cache local, une application mobile validée sur le téléphone du développeur.

Si vous faites développer un logiciel, demandez sur quoi tourne la dernière vérification avant livraison. Si elle tourne sur la machine du prestataire plutôt que sur ce qui vous est livré, elle ne couvre pas ce que vous recevez.

La 0.9.3 et la 0.4.0 d’einvoicing-connect sont en ligne depuis le 31 août. Leur intégration continue lance désormais les tests sur Ruby 3.2 et 4.0, l’installation du package dans un environnement vide sur ces deux versions et la vérification du lockfile.