Sommaire
Des EPUB de vos contenus et commentaires
LinuxFr.org vous permet de lire les contenus et commentaires du site, au format EPUB3, par exemple dans votre liseuse préférée. Il y a une exception à cela, les liens, parce que certes ça ferait des EPUB tout mignons, mais surtout petits voire un poil inutiles. Le lien EPUB est présent automatiquement sur chaque contenu (hormis les liens donc).
Le principe est simple : on donne un lien vers un contenu HTML Ă epub, il le demande Ă la partie Ruby on Rails du site, ainsi que les images associĂ©es, convertit le tout au format EPUB3 et le renvoie Ă la personne qui lâa demandĂ©. Techniquement epub n'est pas exposĂ© frontalement mais se trouve derriĂšre un nginx.
CÎté code Ruby on Rails
Câest assez basique : on ajoute juste sur chaque contenu un lien pour tĂ©lĂ©charger au format EPUB. Ainsi, y compris sur cette dĂ©pĂȘche, vous allez trouver un lien Ă la fin pour rĂ©cupĂ©rer le tout au format EPUB (et un autre pour rĂ©cupĂ©rer le source en Markdown mais câest un autre sujet).
app/views/news/_news.atom.builder: epub = content_tag(:div, link_to("Télécharger ce contenu au format EPUB", "#{url}.epub"))
app/views/polls/_poll.atom.builder: epub = content_tag(:div, link_to("Télécharger ce contenu au format EPUB", "#{url}.epub"))
app/views/posts/_post.atom.builder: epub = content_tag(:div, link_to("Télécharger ce contenu au format EPUB", "#{url}.epub"))
app/views/nodes/_actions.html.haml: = link_to "EPUB", "#{path_for_content node.content}.epub", title: "Télécharger ce contenu au format EPUB", class: "action download"
app/views/diaries/_diary.atom.builder: epub = content_tag(:div, link_to("Télécharger ce contenu au format EPUB", "#{url}.epub"))
app/views/wiki_pages/_wiki_page.atom.builder: epub = content_tag(:div, link_to("Télécharger ce contenu au format EPUB", "#{url}.epub"))
CÎté epub
Le service est plutĂŽt simple, par rapport Ă img, car il nâa pas de dĂ©pendance sur redis par exemple, et quâil a, au final, peu de paramĂ©trage (un couple adresse+port dâĂ©coute, un fichier de trace et un hĂŽte pour aller chercher les contenus).
Il est possible de faire un GET /status et on obtient une rĂ©ponse HTTP 200 avec un contenu OK. Câest utile pour tester que le service est lancĂ© (depuis lâintĂ©rieur de la plateforme).
Sinon on lui demande une dĂ©pĂȘche, un journal, une entrĂ©e de forum, un sondage, une entrĂ©e de suivi ou une page wiki en prenant le chemin sur LinuxFr.org et ajoutant un petit .epub
Ă la fin, et il va renvoyer un fichier EPUB. Ou bien il va rĂ©pondre un contenu non trouvĂ© HTTP 404 sâil y a un souci. Et vu son fonctionnement, si on a un souci de HTML non valide ou si img a un problĂšme avec une image, alors derriĂšre epub pourrait avoir le mĂȘme souci.
epub est un binaire dynamique en Go. Il impose le https pour lâhĂŽte (du coup on aura tous les liens en HTTPS en interne normalement). Il ne peut pas vraiment ĂȘtre compilĂ© statiquement (on a besoin de libxml2, libonig2 et de la mĂȘme version de la libc au dĂ©ploiement). Il ne gĂšre pas les images in-line.
Dans les logs on va trouver des infos comme :
2024/11/03 16:34:02 Status code of http:/example.invalid/exemple.png is: 404
(âŠ)
2024/11/03 16:38:23 Fetch https://linuxfr.org/news/capitole-du-libre-2024-au-programme-du-16-et-17-novembre
2024/11/03 16:38:24 Fetch https://linuxfr.org/users/liberf0rce/journaux/libreast-2006-is-out-of-order
Historique
epub a Ă©tĂ© crĂ©Ă© par Bruno Michel en 2013 et Bruno est le seul Ă travailler dessus (48 commits) jusquâen 2018. Comme img, on peut considĂ©rer que epub a fait le job pendant ce temps-lĂ , sans besoin de retouche.
Mon premier commit de 2021 concerne la gestion dâun cas de collision de nommages des images.
En 2022, Bruno quitte lâĂ©quipe du site, et par ailleurs il y a des montĂ©es de versions et des migrations Ă faire sur les serveurs de LinuxFr.org, et epub fait partie des services Ă reprendre en main. Ce qui veut dire le comprendre, le documenter et au besoin lâamĂ©liorer.
Bref je dĂ©cide de me plonger dans epub (2022-2024), dans la foulĂ©e de img, car a priori ce nâest pas un composant compliquĂ© du site (il vit dans son coin, il offre une interface, câest du Go, donc on a un binaire seulement Ă gĂ©rer - divulgĂąchage en fait non pas seulement).
Le choix est le mĂȘme que pour img (cf la dĂ©pĂȘche prĂ©cĂ©dente) : ajouter un Dockerfile permettant de recompiler epub dans un conteneur, en contrĂŽlant la version de Go utilisĂ©e, en effectuant une dĂ©tection dâĂ©ventuelles vulnĂ©rabilitĂ©s au passage avec govulncheck. Cela me permet de valider que lâon sait produire le binaire dâune part, et que lâon offre Ă tout le monde la possibilitĂ© de contribuer facilement sur ce composant. Et de dĂ©couvrir quâune version statique nâest pas facilement envisageable.
Puis je vais tester le composant pour vĂ©rifier quâil fonctionne comme je le pense et quâil fait ce quâon attend de lui. Je vais ajouter une suite des tests qui couvrent les diffĂ©rentes fonctionnalitĂ©s et les vĂ©rifient en IPv4 et en IPv6, en HTTP 1.1 et en HTTP 2.0. Les tests utilisent Hurl et docker-compose, et encore une fois lâidĂ©e de donner la possibilitĂ© de contribuer facilement. Ils comprennent des tests de types de contenus non pris en charge, le test de la limite Ă 5 MiB, diffĂ©rents types de contenus, le test de vie, des appels erronĂ©s (mauvais chemin, mauvaise mĂ©thode, etc). Et surtout de vĂ©rifier avec epubcheck que le fichier epub produit est correct. Le choix des cas de tests est basĂ© sur le trafic rĂ©ellement constatĂ© sur le serveur de production, sur les diffĂ©rents cas dans le code et un peu sur lâexpĂ©rience du testeur.
Les différents travaux effectués vont permettre de détecter et corriger quelques soucis :
Et Ă la fin, jâĂ©cris une dĂ©pĂȘche pour parler de tout cela.
Ăvolutions rĂ©centes
Dockerfile
Le fichier Dockerfile du projet permet :
- de partir dâune image officielle Go dâune version donnĂ©e, basĂ©e sur une distribution Debian (en raison des dĂ©pendances)
- de lâutiliser pendant la construction en prenant la liste des dĂ©pendances de compilation, en les tĂ©lĂ©chargeant, en prenant lâunique fichier source epub.go et en le compilant dynamiquement avec lâoption pour retirer les chemins de compilation
- de rechercher les éventuelles vulnérabilités avec govulncheck
- de tester avec golangci/golangci-lint le code (fait Ă la construction de lâimage, car on dispose de toutes les dĂ©pendances Ă ce moment-lĂ )
- de repartir dâune base Debian en y mettant les autoritĂ©s de certification, les dĂ©pendances de fonctionnement et le binaire issus de la partie construction, de dĂ©clarer le port dâĂ©coute et de lancer le binaire avec des variables disposant de valeurs par dĂ©faut.
La suite de tests
Pour lâutiliser, câest assez simple, il faut aller dans le rĂ©pertoire tests et lancer un docker-compose up --build, qui va produire le conteneur contenant epub, et dĂ©marrer le nginx-cert qui fournit les certificats et le nginx prĂ©configurĂ© pour les tests. Si tout va bien, on attend, et au bout dâun moment il sâaffiche :
linuxfr.org-epub-test_1 | All tests look good!
tests_linuxfr.org-epub-test_1 exited with code 0
Rentrons un peu dans les détails.
Dâabord un fichier docker-compose.yaml qui dĂ©crit le rĂ©seau IPv4/IPv6 utilisĂ© pour les tests, lâimage nginx-cert qui sera utilisĂ©e pour crĂ©er une autoritĂ© de certification et un certificat serveur de test, lâimage nginx qui sera utilisĂ©e avec sa configuration et ses fichiers Ă servir pour les tests, lâimage epub et son paramĂ©trage (dont lâaccĂšs au nginx) ainsi que le rĂ©pertoire de lâautoritĂ© de certification de tests et enfin lâimage de la suite de tests qui est construit avec son Dockerfile et son rĂ©pertoire de dĂ©pĂŽt des fichiers EPUB.
Le Dockerfile de tests est basĂ© sur une image Hurl (un outil pour faire des tests HTTP). On ajoute les fichiers de tests en .hurl, le script shell qui pilote le tout, on prĂ©voit dâavoir les paquets dont on aura besoin : bash (pas par dĂ©faut dans les Alpine), curl, openjdk17 (pour epubcheck), openssl, unzip (transitoirement), bind-tools et shellcheck. On installe epubcheck. Et on lance les tests par dĂ©faut.
La configuration nginx de test Ă©coute en HTTP sur le port 80 en IPV4 et IPv6 et permet de dĂ©finir des chemins avec des rĂ©ponses en HTTP 301, 302, 308, 400, 401, 403, etc. jusquâĂ 530 et mĂȘme 666 pour les codes invalides, ainsi quâune redirection infinie.
Dans les données de tests servies par nginx, on trouve des contenus du mauvais type, des contenus dans divers formats, une image trÚs grande et des images qui ne seront pas accessibles.
Sont aussi présents deux fichiers de tests avec une extension en .hurl :
- le test de vie et les chemins hors des contenus autorisés
- les tests sur les contenus
Vient enfin le script shell qui pilote le tout :
- on dĂ©finit les variables pour les cibles IPv4/IPv6 que lâon veut utiliser dans les autres conteneurs Docker
- on purge le stockage des EPUB sur disque
- on lance les premiers tests (en IPv4 et IPv6, en HTTP 1.1 et en HTTP 2.0)
- sur chaque EPUB produit, on lance epubcheck et on regarde si la validation donne le résultat attendu (succÚs ou échec)
- si on est arrivé jusque-là on écrit que tout va bien et on déclenche un sourire de satisfaction.
Les problématiques restantes
Il y a quelques entrées encore ouvertes dans le suivi :
-
les images trop grandes (en octet), non rĂ©cupĂ©rables, de format inconnu, etc. : la suite de tests actuelle « couvre » le cas des images de plus de 5 MiB ou non rĂ©cupĂ©rables, avec des tests qui Ă©chouent, comme prĂ©vu, vu que câest img qui est censĂ© faire le job de les Ă©viter. Cependant il pourrait ĂȘtre sympa de remplacer toute image non disponible/invalide par une image de remplacement « Image indisponible » du bon Content-Type et du bon nom (vu quâelle est dĂ©clarĂ©e dans le MANIFEST).
-
les images trop grandes (en pixel) : globalement on revient Ă la question des images que laisse passer img
-
les epub non fonctionnels en rédaction et modération : pour des questions de droits, la génération EPUB ne marche pas dans les espaces de rédaction et de modération, à voir si on trouve un contournement ou si on évite de proposer le lien.
Il y a la question habituelle de la montĂ©e de versions des dĂ©pendances (pour nous actuellement contraintes celles du code Ruby on Rails). Et des questions Ă se poser sur lâavenir de nginx ?. Les dĂ©pendances pendant le fonctionnement amĂšnent aussi leur lot de contraintes.
Conclusion ?
Encore une fois, sans surprise et me rĂ©pĂ©tant, il reste des problĂ©matiques et du code Ă faire pour les gĂ©rer (câest rare un composant sans demandes dâĂ©volution ou de correction). Yapuka (mais probablement plus tard, il faut aussi partager le temps avec les autres composants, ou avoir plus de contributions).
epub rend la fonction que lâon attend de lui, mĂȘme si on pourrait faire un peu mieux. Plonger dans ce composant sâest avĂ©rĂ© assez intĂ©ressant et formateur (et nĂ©cessaire) : techniquement cela a Ă©tĂ© lâoccasion de faire du Go, du docker et du docker-compose, du nginx, du hurl, de lâHTTP et de gĂ©rer des problĂ©matiques statique/dynamique et des dĂ©pendances. Il sâagissait encore de comprendre ce que faisait un code Ă©crit par une autre personne, de se poser des questions pour choisir les tests et le contenu de la documentation, de se demander pour quelles raisons tel ou tel choix a Ă©tĂ© fait, de rendre ce composant plus « contribuable », et de complĂ©ter le tout de façon dĂ©taillĂ©e avec une dĂ©pĂȘche.