Dans cet article je vais parler en vrac de tous les petits trucs qui font que tout fonctionne et qu'on voit moins : le flux RSS, le déploiement chaque matin, des problèmes aussi…
Le flux RSS c'est une brique qui est devenue quasiment inconnu alors que c'est un outil hyper pratique : vous pouvez suivre les publications de n'importe qui (pour peu qu'il mette à disposition un flux RSS) sans aucun réseau social, sans aucune brique centralisée, en décidant vous-même à quelle fréquence vous souhaitez relever les flux (adieu la pollution de l'attention à cause des notifications), en décidant vous-même de qui vous allez suivre (adieu les réseaux comme Youtube qui vous désabonne sans rien dire ou comme Twitter (non je dirais pas "X") qui vous force à suivre son propriétaire mégalo), avec le client de votre choix pour avoir la vue qui vous convient le mieux.
Par exemple, j'ai fait le choix d'avoir ma propre instance FreshRSS que je consulte essentiellement via mon smartphone et l'application Read You
Différentes vue de Read You sur mon smartphone
Je l'utilise pour suivre des gens que je connais (coucou Nicolas Brondin-Bernard 👋 qui apparait en screenshot, coucou aussi Siegfried 👋), des gens que je ne connais pas (en tout cas pas personnellement, mais les années passant je "connais" bien leur identité en ligne, par exemple Sebsauvage, Seboss666, Korben ou Pierre de minimachines.net), je suis aussi les mises à jours sur des projets Github (par exemple, je suis les mises à jour de FreshRSS comme ça pour avoir tout le détail des releases), Lobsters (qui est un agrégateur de contenu collaboratif), l'actualité mobile / jeux-vidéos / mangas, etc. Je fais l'essentiel de ma veille via du RSS.
Je l'ai montré plus haut : je suis aussi mon propre flux RSS. Pas par égocentrisme, mais pour m'assurer qu'il fonctionne et qu'on voit bien apparaitre mon contenu dans ce flux, vérifier que le contenu est complet aussi, qu'il n'y a pas de bug particulier. (et c'est en disant ça que je vois un bug qui fait que les images ne s'affichent pas, donc je fais une pause dans l'écriture pour corriger ça 🫠)
Il y a des bonnes librairies qui permettent de générer un flux RSS, mais au fond ce n'est qu'un fichier XML, avec des balises spécifiques, donc c'est assez facile de le générer avec un script maison. C'est donc ce que j'ai fait : je produis le flux RSS à la "main", à chaque build du blog. Vous pouvez donc être informé à chaque nouvel article via mon flux RSS (qui est aussi compatible Atom pour les curieux, je vous laisse aller voir le détail) et lire mon contenu sans même vous rendre sur mon blog : je veux partager, pas générer du trafic, je vous laisse donc le choix.
Je ne vais pas refaire un laïus sur le sujet (j'en ai déjà fait un dans la partie 1 de la série) : Deno c'est trop bien, y'a pas de débat pour moi.
J'ai donc choisi cette plateforme pour coder mon blog. Mais comme je vous le dis depuis le début de la série : tout est statique. En effet j'ai écrit un ensemble de script en TypeScript qui vient lire mon contenu Markdown, ajouter ce qu'il faut et produire un site statique. Environ 1200 lignes de TypeScript cerveau-codé (par opposition à vibe-codé, merci Siegfried pour cette expression que j'aime beaucoup), réparti sur une quinzaine de scripts qui font chacun une partie du traitement en séquence.
Sans chercher l'optimisation, mais en utilisant des console.time()/console.timeEnd() pour mesurer le temps de chaque partie (pour savoir si je dois optimiser quelque part un jour), j'ai sur ma machine ça comme mesure de temps avec Deno 2.6.3 :
Moins d'une seconde donc, et on voit que les parties longues c'est la conversion Markdown (presque 400ms) et la copie des fichiers statiques (300ms) pour un total sous la seconde.
Côté pipeline (Github Actions), je suis plutôt autour de 2.5 secondes :
On voit là que c'est l'écriture sur le disque qui consomme vraiment beaucoup plus de temps (x4.5) alors que la conversion des fichiers Markdown en HTML n'augmente pas tant que ça (x2).
Dans tous les cas, la performance actuelle de mon système me convient très bien. J'avais peur qu'en étant codant le truc aussi simplement et avec si peu d'effort côté performance ce soit particulièrement lent mais à date ce n'est pas le cas (même si je pourrais clairement faire des optimisations).
Je vous donne mon pipeline qui n'a rien de bien fou :
name:Deployon:push:branches:masterpull_request:branches:masterschedule:# every weekday at 6:00AM (for real: between 6:00AM and 6:30AM)# les serveur Github sont en UTC donc décalés d'1h donc mettre 6:00 donne des builds entre 7h00 et 7h30-cron:"0 5 * * 1-5"jobs:deploy:name:Deployruns-on:ubuntu-latestpermissions:id-token:write# Needed for auth with Deno Deploycontents:read# Needed to clone the repositorysteps:-name:Clonerepositoryuses:actions/checkout@v4-name:InstallDenouses:denoland/setup-deno@v2with:deno-version:v2.x-name:Installsteprun:"deno install"-name:Fmtsteprun:"deno task fmt:check"-name:Lintsteprun:"deno task lint"-name:Buildsteprun:"deno task build"-name:UploadtoDenoDeployuses:denoland/deployctl@v1with:project:"anthonypena-blog"entrypoint:"./main.ts"root:"./dist"
Yaml
J'ai fait simple. À chaque commit sur la branche master, ou pull request ou chaque matin (j'y reviens plus loin), le pipeline s'enclenche. Ce pipeline va cloner le dépôt dans une image ubuntu (potentiellement je pourrais faire plus optimal ici maintenant que j'y pense), j'installe Deno, j'installe les dépendances de mon projet, je vérifie le formatage du code (pas des Markdown, juste le code du moteur de blog que j'ai codé), je vérifie le lint (que du moteur encore une fois), je lance la construction du blog et pour finir je pousse pour le déploiement (j'y reviendrais plus loin).
Le pipeline complet prend environ 50 secondes.
J'utilise peu de branche (car je travaille seul) mais j'aime bien avoir le pipeline au besoin.
J'ai ajouté une exécution chaque matin pour ajouter le dynamisme qui manquait au côté statique : pour mettre en ligne un article j'ai besoin de recompiler le blog, or, je publie généralement le mardi matin avec un post sur LinkedIn / Twitter à 8h00. Si j'ai tout programmé côté réseaux sociaux ce n'est pas pour avoir une action manuelle pour mettre en ligne l'article lui-même, donc j'ai mis en place une mécanique simple pour vérifier si un article doit être "public" ou pas qui va afficher ou non l'article à partir de la bonne date. Je peux donc au besoin programmer quelques articles d'avance sans effort : tout fonctionne tout seul côté infra.
Par contre, vous l'avez peut-être lu dans les commentaires, j'ai un peu joué avec les heures d'exécution. À la base je voulais une exécution à 7h00 du lundi au vendredi (donc un cron 0 7 * * 1-5 (minute = 0 ; heure = 7 ; jour du mois = * (=tous) ; mois = * (=tous) ; du lundi au vendredi = 1-5)). Mais ce n'est pas aussi simple… Déjà les runner Github Actions fonctionnent en UTC. Donc demander une exécution à 7h00 UTC implique une exécution à 8h00 heure de Paris… Soit. Je suis passé à 0 6 * * 1-5. Et là je me suis rendu compte que l'exécution programmée à "7h00" était plutôt vers "7h30-35" (surement pour des questions de charge de runner), donc comme je veux que l'article soit en ligne à 8h00 pour les réseaux sociaux et un peu avant pour que vous l'ailliez avant ça dans vos lecteurs RSS, ça n'allait pas… Donc j'ai avancé à 0 5 * * 1-5 pour avoir une construction du blog avant 7h.
Note : si vous pensez que je parle le cron parfaitement, vous vous trompez. Je suis un utilisateur efficace de Crontab Guru.
Démonstration de Crontab Guru
Clairement cette histoire de cron c'est ce qui m'a pris le plus de temps côté pipeline, et je m'y attendais pas… Comme quoi le diable se cache dans les détails… 🥲
Avec la refonte du blog, j'en ai aussi fini avec le self host. Je suis parfaitement capable d'héberger à la maison ou sur un serveur, mais j'ai choisi la simplicité : je suis passé par Deno Deploy.
Deno Deploy, comme son nom l'indique, est la partie cloud accolé à Deno. Vous n'êtes pas obligé de pousser des applications Deno sur Deno Deploy mais quoi qu'il arrive le moteur qui exécutera votre code sera Deno.
De mon côté, j'ai un site entièrement static ! Ou presque… En fait il y a une toute petite partie qui est dynamique : le routing pour gérer les redirections.
En effet sur Deno Deploy il n'y a pas de manière déclarative de définir des redirections (en self host j'aurai bêtement généré un fichier de config pour nginx par exemple), j'ai donc un script Deno d'une quarantaine de lignes qui prend chaque requête en entrée, regarde si le fichier existe pour le servir directement, sinon fait quelques manipulations :
tenter d'ajouter un /index.html pour voir ça existe, ce qui permet que https://anthonypena.fr/2026/01/27/nouveau-blog-partie-3-js, https://anthonypena.fr/2026/01/27/nouveau-blog-partie-3-js/ et https://anthonypena.fr/2026/01/27/nouveau-blog-partie-3-js/index.html vous donne la page de l'article ;
voir si une redirection existe dans la liste des redirections pour vous renvoyer un code HTTP 301 (Moved Permanently, donc votre navigateur vous redirigera directement si vous redemandez l'URL) avec la nouvelle URL ;
gérer quelques cas de 404 proprement ;
En effet avec Ghost, j'avais des URLs en k49.fr.nf/ovh-et-vieux-kernel alors que maintenant j'ai décidé que les URLs aurait la forme anthonypena.fr/2016/05/13/ovh-et-vieux-kernel (avec donc la date dans l'URL), je ne voulais pas casser 9 ans de référencement à droite et à gauche, donc j'ai fait en sorte de gérer au maximum les URLs. Je sais qu'il en manque sur des pages plus techniques comme /tag/tutoriel/ et /page/2/ (remontées par Duck Duck Go via la recherche site:k49.fr.nf) qui vous redirigeront vers la page d'accueil à date, mais c'est plus marginal je pense.
Évidemment, comme j'ai aussi changé de nom de domaine, j'ai aussi mis en place une redirection pour passer de l'ancien domaine au nouveau de manière transparente pour vous.
Je n'avais jamais réalisé ça mais mon blog commence à subir le poids des années : le dossier dist pèse un peu plus de 700Mo à date. Vous vous en doutez : c'est essentiellement les images qui pèsent lourd dans la balance.
J'ai été un peu bourrin en copiant tout sans plus de réflexion, mais je sais que je pourrais optimiser ça, déjà en ne copiant que les images vraiment utilisées dans les articles pour éviter de copier des images pour rien. Je pense que je pourrais économiser énormément de poids et donc de temps à uploader le contenu.
Je sais aussi que la page d'accueil est assez longue à charger : il y a beaucoup d'images, là encore ça ne s'invente pas. Mais j'ai des pistes d'améliorations faciles !
Passer toutes les images en lazy loading pour que le navigateur les charges en dernier (ça implique de bien gérer la taille des images pour éviter que vous n'aillez trop de layout shift, mais c'est pas compliqué) ;
Profiter d'avoir un peu de code côté serveur pour le routing pour gérer un peu de cache réseau avec les headers de cache adéquat car pour l'instant je me repose entièrement sur le navigateur pour faire les choses bien, et je peux faire mieux ;
produire une version miniature des images spécifiquement pour la page d'accueil de sorte à éviter de charger la version grand format de l'image de couverture ;
Je pense que je pourrais optimiser encore plus, mais c'est des pistes évidentes et facile. Je ferai ça dans le futur quand j'aurai du temps pour vraiment me poser là-dessus, en attendant je peux publier du contenu et vous pouvez me lire !
Cette refonte me simplifie la vie côté production de contenu, mais évidemment m'ajoute une complexité ailleurs vu que je dois aussi corriger l'affichage sur des appareils divers et variés… Merci à ceux qui me ping pour me pointer un bug, je note et je vais prendre en compte, il y a juste des bugs parfois un peu subtils (oui je pense à toi Romain et ton iphone 8, promis je suis en train de regarder 🫠).
Ça m'ouvre énormément de portes à commencer par la possibilité d'avoir des pages complètement personnalisés pour centraliser tout sur mon site, qui ne sera plus seulement un blog (même si ça restera l'activité principale !).
Merci à ceux qui ont lu toute la série ! Merci évidemment aussi à ceux qui me lisent tout court (que ce soit tout le temps ou de temps en temps) !
Crédit photo : Générée via Mistral AI avec le prompt suivant :
Une scène chaleureuse et immersive, inspirée par l’esthétique poétique de Studio Ghibli, représentant un port spatial artisanal en bois clair et vieilli, baigné d’une lumière dorée et bleutée tamisée, comme un crépuscule d’automne. Au centre, un panda roux anthropomorphe (Firefox), vêtu d’une veste de capitaine en tissu texturé, supervise le chargement de caisses en bois estampillées "Static HTML" et "Deno Land" sur un vaisseau spatial fait de bois et de cuivre. Le vaisseau, inspiré des machines volantes de Ghibli, est entouré de lanternes en papier flottantes qui guident le chemin vers les étoiles.
Autour du panda roux, des robots miniatures (inspirés des créatures de Ghibli, comme un mélange entre un Totoro et un assistant technique) aident à charger les caisses, tandis qu’un écran holographique (avec un cadre en bois sculpté) affiche une carte stellaire avec des routes vers des planètes nommées "GitHub Pages", "Netlify", et "Cloudflare". Des néons bleutés et des motifs de code binaire flottent dans l’air, comme des étoiles filantes numériques.
En arrière-plan, une grande fenêtre donne sur un ciel étoilé, où des nuages en forme de code binaire reflètent la lumière des lanternes. Une tasse de thé fumant et des parchemins déroulés (symbolisant les logs de déploiement) sont posés sur un bureau en bois, à côté d’un petit robot endormi.
À droite, en bas, le panda roux est légèrement tourné en 3/4 dos, lové sur un coussin, entouré d’un halo lumineux subtil. Il ne dépasse pas le tiers de la hauteur de l’image et observe la scène avec satisfaction, comme s’il contemplait le voyage de son site vers le web.
L’ambiance est cosy, onirique et high-tech : les couleurs sont douces (beiges, bleus pâles, oranges chauds), avec des jeux de lumière tamisée qui créent des ombres chaleureuses. Les détails sont soignés, avec des textures visibles (grain du bois, tissu de la veste, pelage du panda roux), et une lumière qui met en valeur les éléments tech et artisanaux. Le tout respire la sérénité et l’inspiration, comme un moment suspendu entre la magie et la technologie.""Une scène chaleureuse et immersive, inspirée par l’esthétique poétique de Studio Ghibli, représentant un port spatial artisanal en bois clair et vieilli, baigné d’une lumière dorée et bleutée tamisée, comme un crépuscule d’automne. Au centre, un panda roux anthropomorphe (Firefox), vêtu d’une veste de capitaine en tissu texturé, supervise le chargement de caisses en bois estampillées "Static HTML" et "Deno Land" sur un vaisseau spatial fait de bois et de cuivre. Le vaisseau, inspiré des machines volantes de Ghibli, est entouré de lanternes en papier flottantes qui guident le chemin vers les étoiles.
Autour du panda roux, des robots miniatures (inspirés des créatures de Ghibli, comme un mélange entre un Totoro et un assistant technique) aident à charger les caisses, tandis qu’un écran holographique (avec un cadre en bois sculpté) affiche une carte stellaire avec des routes vers des planètes nommées "GitHub Pages", "Netlify", et "Cloudflare". Des néons bleutés et des motifs de code binaire flottent dans l’air, comme des étoiles filantes numériques.
En arrière-plan, une grande fenêtre donne sur un ciel étoilé, où des nuages en forme de code binaire reflètent la lumière des lanternes. Une tasse de thé fumant et des parchemins déroulés (symbolisant les logs de déploiement) sont posés sur un bureau en bois, à côté d’un petit robot endormi.
À droite, en bas, le panda roux est légèrement tourné en 3/4 dos, lové sur un coussin, entouré d’un halo lumineux subtil. Il ne dépasse pas le tiers de la hauteur de l’image et observe la scène avec satisfaction, comme s’il contemplait le voyage de son site vers le web.
L’ambiance est cosy, onirique et high-tech : les couleurs sont douces (beiges, bleus pâles, oranges chauds), avec des jeux de lumière tamisée qui créent des ombres chaleureuses. Les détails sont soignés, avec des textures visibles (grain du bois, tissu de la veste, pelage du panda roux), et une lumière qui met en valeur les éléments tech et artisanaux. Le tout respire la sérénité et l’inspiration, comme un moment suspendu entre la magie et la technologie.