L'erreur invisible du zip web
Une build web pour itch doit porter index.html à la racine de l'archive. Zipper le dossier au lieu de son contenu casse la lecture navigateur, sans message utile. C'est l'erreur la plus banale du format, elle se produit toujours à la main, et j'ai écrit un script pour la rendre impossible.
Une erreur banale, et muette
Pour livrer un jeu web sur itch, on téléverse une archive zip. La règle est simple : index.html doit se trouver à la racine de l'archive. Or le geste naturel, clic droit sur le dossier de build, produit une archive dont la racine est un dossier. La page itch casse alors net, sans rien dire d'utile. C'est l'erreur la plus banale du format, et elle a deux propriétés qui la rendent dangereuse : elle est invisible au moment où on la commet, et elle se produit toujours à la main, dans le geste le moins surveillé de toute la chaîne, celui d'après les tests.
En préparant la démo 0.1.0 de Sillon, j'ai décidé que cette erreur ne devait plus être possible, plutôt que d'espérer ne plus la commettre.
Le script qui refuse de rendre la main
L'empaqueteur zippe le contenu du dossier, jamais le dossier. Puis, et c'est le cœur, il rouvre l'archive qu'il vient d'écrire et exige d'y trouver index.html à la racine, sinon il refuse de rendre la main. Il ne fait pas confiance à son propre geste : il vérifie le résultat, comme le ferait le navigateur.
Et ce contrôle a été éprouvé contre la vraie faute, pas seulement contre la bonne archive : j'ai fabriqué exprès le zip fautif, celui qui enferme un dossier, et vérifié qu'il était bien refusé. Un garde-fou qu'on n'a jamais vu rouge ne protège de rien ; on ne sait même pas s'il s'exécute.
Ce que l'empaqueteur exige encore
Deux autres refus sont entrés dans le script, chacun payé par une quasi-mésaventure.
Il filtre les fichiers du harnais de test : mon harnais web vit dans le même dossier servi que la page du joueur, et un fichier de test livré au public serait une porte ouverte sur les suites. Le tri entre ce qui se livre et ce qui reste chez moi n'est plus un souvenir, c'est une ligne de code.
Il exige un numéro de version, lu dans la configuration du projet. Sans numéro, le premier correctif serait indistinguable de l'original et aucun retour de joueur ne pourrait être rattaché à un état du code. Le jour où je l'ai écrit, la clé de version n'existait pas encore dans le projet : le script s'arrêtait et le disait, plutôt que d'inventer un numéro. C'est lui qui m'a forcé à poser le 0.1.0.
La capture qui a vu ce que les tests ne voyaient pas
La même livraison a corrigé un défaut d'un tout autre genre, trouvé par la préparation de la page itch. Sur l'écran de détail du catalogue, le prix mordait le texte : posé à une hauteur fixe, calée sur la fiche la plus longue du jour où elle avait été écrite. La fiche du frein fait cinq lignes ; la pièce et son nombre tombaient au milieu de la quatrième, sur l'écran que le joueur ouvre à chaque achat. Des semaines d'existence, aucune assertion. Il a fallu une capture d'écran prise pour illustrer la page de vente.
La position se mesure désormais sur le pavé rendu, et l'assertion qui la garde ouvre les huit fiches en lisant les nœuds posés à l'écran. Refaire la formule de placement dans l'assertion serait la comparer à elle-même.
La porte du gel
Dernier rôle du script, moins visible : il marque la fin du droit de modifier. Toute retouche de code après l'export périme d'un coup les captures de la page et les assertions jouées sur la build livrée. L'empaqueteur n'est pas un utilitaire de fin de chaîne, c'est la porte du gel : ce qui passe après lui s'appelle la version suivante.
L'essai à blanc a rendu ses comptes : 38,6 Mo zippés pour Windows, 12,3 Mo pour le web, 9 fichiers. Des chiffres qu'aucun de mes souvenirs de zip manuel n'aurait su donner.
Ce que j'en retiens
Les erreurs de livraison vivent précisément là où les tests s'arrêtent : dans les gestes manuels d'après la validation. La réponse n'est pas plus de vigilance, c'est un outil qui exécute le geste, vérifie son propre résultat, et a prouvé une fois qu'il sait dire non.