Petit édito du printemps (nouveau blog, Metroid & spleen)

Cet article (plutôt) personnel portera principalement sur la mise à jour de mon blog. J'y parlerai aussi un peu de Metroid et de mon sentiment général de la situation actuelle.

Petit édito du printemps (nouveau blog, Metroid & spleen)

Hello world

Parfois, j'ai simplement envie d'une plateforme pour exprimer ce que je fais et ce que je pense. Oui, Twitter et d'autres réseaux sont faits pour cela, mais je m'y sens incroyablement limité et submergé. J'ai tenu des blogs successifs depuis 2015 pour ceux qui me connaissent, et j'apprécie disposer d'une plateforme sous mon contrôle où je peux m'exprimer relativement librement.

D'habitude, mes articles suivent des thématiques particulières, et plus récemment j'ai abordé la sécurité informatique sous différents angles. J'ai eu des retours positifs globalement, ce qui me fait plaisir évidemment. Toutefois j'insiste à rappeler que ces articles sont à prendre avec un certain recul : je ne suis pas omniscient, et vous ne devez pas faire confiance à des inconnus sur Internet. Je vous invite donc au cross-check en toutes circonstances : recoupez des sources que vous estimez suffisamment fiables.

Refonte du blog

On m'a parfois suggéré d'implémenter un système de commentaires, ce que je sais faire et que j'ai déjà fait par le passé. J'étais réticent à l'idée, car je vois ce blog comme ma plateforme personnelle. N'y voyez rien d'égoïste, mais peut-être vous comprendrez l'idée… Mais j'ai changé d'avis, car au final je passe à côté d'interactions intéressantes, de critiques pertinentes et justes ; car je ne suis justement pas omniscient (surprise !).

Outre l'ajout d'un système de commentaires, j'ai décidé de procéder à une refonte du blog. En gros, voici ce qui a été changé :

  • Mise à jour de Ghost 3.x vers 4.x (et je me déteste pour ça).
  • Utilisation d'un thème personnalisé basé sur Casper 4.x.
  • Ajout d'un système de commentaires : say hi to Commento.
  • Passage de l'infrastructure Docker sous gVisor.

Et c'est déjà pas mal !

Ghost 4.x : not just a blogging platform anymore

Je le dis franchement, je n'aime pas ce qu'est devenu Ghost. Pourquoi ? Eh bien, passez sur leur site et vous pouvez lire ceci :

Turn your audience into a business.

Ghost reste un CMS open-source, c'est bien, et je conçois qu'il y a besoin de monétiser la chose pour en vivre. C'est juste que Ghost ne correspond plus à ce que je cherche vraiment dans un blog. Et qu'est-ce que je cherche ? Pour ça, je vous réfère à la description de Ghost il y a quelques années :

Just a blogging platform.

Mais voilà, ce n'est plus juste une plateforme de blogging. Depuis plusieurs versions Ghost devient de plus en plus fourni en fonctionnalités, donc cette tendance n'est pas nouvelle et n'est pas sans rappeler ce qu'est très vite devenu Wordpress qui était longtemps critiqué par le CEO de Ghost.

En fait, la version 4.x a atteint le point de non-retour :

  • Intégration intrusive des paiements dans l'interface d'administration.
  • La gestion des membres, optionnelle avant, est maintenant forcée.
  • Pas de SMTP custom pour la newsletter, "allez payer Mailgun".
  • Le thème par défaut, Casper, a régressé sur certains points.

Le second point m'éverne le plus : cette intrusion de la gestion des membres. Je n'en veux pas, d'accord ? Et le pire, ouvrez une console développeur sur cette page et admirez :

WTF

Quoi, pardon ? Not a bug, it's a feature (d'après le staff de Ghost). Vous avez l'erreur 401 car vous n'êtes pas "membre" (et j'ai viré les boutons dans le thème car franchement, rien à faire). Mais le pire c'est que Ghost a besoin pour ça de chercher un script sur unpkg.com...

Est-ce normal ? Non, c'est en violation totale avec la RFC 7235 qui stipule : "the server generating a 401 response MUST send a WWW-Authenticate header field containing at least one challenge applicable to the target resource". En d'autres termes, fix your shit, Ghost.

Que faire (20/04/21) ? Eh bien à ce jour, Ghost continue de se torcher avec l'avis de la communauté qui n'utilise pas leur SaaS Ghost Pro. J'ai donc décidé de tester une modification du code en commentant cette ligne :

            // no code injection for amp context!!!
            if (!_.includes(context, 'amp')) {
                // head.push(getMembersHelper(options.data));
Ghost 4.2.1 - core/frontend/helpers/ghost_head.js

Mais par pitié, au lieu de forcer à utiliser des vieux hacks, faites un putain de toggle comme avant pour avoir le choix...

Enfin (05/05/21) ! Depuis la version 4.4.0, on peut désactiver ce bordel dans l'interface d'administration, ce qui aura sensiblement le même effet (après avoir regardé le code) que la modification manuelle ci-dessus :

Utilisateur de Ghost depuis sa version 0.x, je me sens donc de moins en moins en adéquation avec l'expérience que Ghost que fournit ; d'autant plus quand on regarde les discussions à ce sujet entre utilisateurs et développeurs. J'ai logiquement considéré en conséquence un changement de plateforme : aucun autre CMS dynamique ne me convenait cependant, donc je me suis intéressé à un blog statique généré par Hugo ou Zola par exemple.

J'ai joué avec les deux, et je les trouve excellents dans ce qu'ils font. Malheureusement je n'avais pas envie de me prendre la tête avec l'automatisation du déploiement, d'autant plus que j'utilise parfois un iPad pour écrire des brouillons vite fait et je n'ai pas VSCode dessus pour avoir un workflow suffisamment agréable.

Meet Casper-Wonder

Au 21 avril 2021

J'ai donc écarté l'idée (pour le moment du moins) et j'ai accepté malgré tout de faire la mise à jour vers Ghost 4.x histoire d'être tranquille avec les mises à jour (aussi, il y a des améliorations en performances et le lazy loading natif). Mais je n'allais pas m'avouer 100% vaincu : je me suis décidé à reprendre le thème par défaut et virer tout le bullshit possible.

Ainsi est né mon fork de Casper : Casper-Wonder. J'ai donc créé un repo git avec le code de Casper et j'ai procédé à mes modifications :

  • Dark mode forcé sans injection JS.
  • Suppression à vue de tous les éléments liés aux membres.
  • Quelques retouches CSS mineures.
  • Self-host et mise à jour de jQuery.
  • Des boutons réseaux sociaux autres que Twitter.
  • Un vrai bouton RSS ! Pas le faux avec Feedly.
  • Ajout de highlight.js : de la couleur pour le code, enfin !
  • Intégration avec Commento : custom CSS, lazy loading, compteur.
  • Un bouton scroll-to-top pour revenir en haut facilement.
  • De la recherche côté client grâce à la librairie searchinGhost.

J'ai fait ça en une petite soirée, il n'est pas impossible que je change encore quelques choses. J'ai en profité pour instaurer un workflow qui automatise un peu les mises à jour grâce à GitHub Actions (au passage vous allez voir highlight.js en action sur ce fichier YAML) :

name: Deploy Theme
on:
  push:
    branches:
      - master
      - main
jobs:
  deploy:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v2
      - name: Deploy Ghost Theme
        uses: TryGhost/action-deploy-theme@v1.4.1
        with:
          api-url: ${{ secrets.GHOST_ADMIN_API_URL }}
          api-key: ${{ secrets.GHOST_ADMIN_API_KEY }}
.github/workflows/deploy-theme.yml

Voici comment faire, c'est très simple. Ainsi, à chaque commit sur master, mon site set met à jour sans que je n'aie à upload le theme.zip manuellement.

Bonus, highlight.js (Tomorrow Night) en action sur un code en Go :

package main
import "fmt"

func fib() func() int {
    a, b := 0, 1
    return func() int {
        a, b = b, a+b
        return a
    }
}

func main() {
    f := fib()
    fmt.Println(f(), f(), f(), f(), f())
}

Commento : un système de commentaires self-hosted

J'ai longtemps utilisé Isso sur mes précédents blogs, un serveur de commentaires en Python très simple à intégrer, et surtout léger. Je ne voulais absolument donc pas entendre parler de Discourse qui est lourd, et encore moins de Disqus qui n'est pas respectueux de la vie privée.

Cette fois-ci, j'ai choisi Commento : tout comme Isso il est très léger, écrit en Go, avec une interface de configuration, support d'Akismet, SMTP, etc. Il se déclare fast & bloat-free, et il l'est vraiment, j'adore.

Pour procéder à son installation, j'ai opté pour Docker (ft. docker-compose) comme à mon habitude, le reste étant également dans des conteneurs (la question ne se pose pas). Voici une ébauche du docker-compose que j'utilise :

version: '2.4'
  
networks:
  http_network:
    external: true

  commento-st_network:
    external: false
    internal: true
    ipam:
      config:
        - subnet: 172.29.0.0/16
 
services:
  commento-st:
    image: registry.gitlab.com/commento/commento:v1.8.0
    container_name: commento-st
    runtime: runsc-kvm
    restart: unless-stopped
    user: 1235:1235
    security_opt:
      - no-new-privileges:true
    depends_on:
      - commento-st-db
    env_file: /wherever/commento-st/config.env
    volumes:
      - /wherever/resolv.conf:/etc/resolv.conf:ro
    labels:
      - traefik.enable=true
      - traefik.http.routers.commento-st-secure.entrypoints=https
      - traefik.http.routers.commento-st-secure.rule=Host(`commento.domain.tld`)
      - traefik.http.routers.commento-st-secure.tls=true
      - traefik.http.routers.commento-st-secure.tls.certresolver=http
      - traefik.http.routers.commento-st-secure.middlewares=secure-headers@file,hsts-headers@file
      - traefik.http.routers.commento-st-secure.service=commento-st
      - traefik.http.services.commento-st.loadbalancer.server.port=8080
      - traefik.docker.network=http_network
    networks:
      commento-st_network:
        ipv4_address: 172.29.0.3
      http_network:

  commento-st-db:
    image: postgres:13-alpine
    container_name: commento-st-db
    runtime: runsc-kvm
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    environment:
      - POSTGRES_DB=commento
      - POSTGRES_USER=commento
      - POSTGRES_PASSWORD=supersecretpassword
    volumes:
      - /wherever/commento-st/db:/var/lib/postgresql/data
      - /wherever/resolv.conf:/etc/resolv.conf:ro
    networks:
      commento-st_network:
        ipv4_address: 172.29.0.2

Il fait un peu lourdingue, mais je l'ai pourtant optimisé du mieux que je peux. Subtilités :

  • J'utilise Traefik v2 en tant que reverse proxy, à savoir.
  • Comme j'utilise gVisor, j'ai dû préciser des IP manuellement.
  • Toujours pour gVisor, j'ai aussi dû monter un resolv.conf.
  • J'ai précisé user: 1235:1235 pour que le conteneur soit rootless.

La configuration se passe avec des variables d'environnement dans un fichier config.env (voir ici en détails) :

COMMENTO_ORIGIN=https://commento.domain.tld
COMMENTO_PORT=8080
COMMENTO_POSTGRES=postgres://commento:supersecretpassword@172.29.0.2/commento?sslmode=disable
COMMENTO_SMTP_HOST=
COMMENTO_SMTP_PORT=
COMMENTO_SMTP_USERNAME=
COMMENTO_SMTP_PASSWORD=
COMMENTO_SMTP_FROM_ADDRESS=
COMMENTO_AKISMET_KEY=
COMMENTO_GITHUB_KEY=
COMMENTO_GITHUB_SECRET=
#COMMENTO_FORBID_NEW_OWNERS=true

Je lance, et tout fonctionne. Je crée un compte administrateur puis je décommente la ligne COMMENTO_FORBID_NEW_OWNERS pour verrouiller l'inscription à l'interface. Je paramètre une intégration, ici pour mon blog, et j'ajoute ce code dans post.hbs :

<section class="article-comments gh-canvas">
    <script defer
        src="https://commento.domain.tld/js/commento.js"
        data-css-override="{{asset "built/commento.css"}}" // Si custom CSS
        data-auto-init="false"> // Si lazy loading
    </script>
    <div id="commento"></div>
</section>

Et boom, ça marche. J'ai paramétré une intégration GitHub pour l'instant, mais les commentaires anonymes sont aussi autorisés sous réserve d'une approbation manuelle pour des raisons évidentes.

Interface minimaliste, mais j'aime ça !

Je verrai plus en détails comment je peux modifier le CSS à mon goût, mais pour l'instant ça marche très bien ainsi de ce que j'ai pu tester. Donc si jamais vous voulez commenter mes articles (pour les quelques personnes qui doivent lire...), désormais vous pouvez !

Voilà ce que ça donne après une retouche CSS !

Aussi, j'ai ajouté du lazy loading pour Commento : il ne va donc charger proprement dit que quand vous en aurez besoin. Je me suis aidé de cet article qui explique comment utiliser l'API Intersection Observer.

Oh le joli compteur !

J'ai également ajouté un compteur pour les commentaires. La documentation officielle m'a bien aidé mais est obsolète pour les textes personnalisés, utilisez plutôt window.customCommentsText.

Le tour de la refonte du blog se termine ainsi, c'est déjà pas mal : une mise à jour majeure du CMS, un thème changé, et un système de commentaires. D'ici les jours à venir je testerai sûrement quelques modifications mais je suis plutôt satisfait du résultat actuel.

Fuck Ghost quand même. Bon, en vrai c'est de la méchanceté gratuite : Ghost a le droit de devenir ce qu'il veut, et ça reste le CMS dynamique que je conseille en particulier aux non-techies (mais pas que).

Recherche côté-client (searchinGhost)

Petite nouveauté mise en place le 21/04/2021. Je la documente ici au cas où quelqu'un serait intéressé pour reproduire la même chose.

La librairie searchinGhost permet de communiquer avec l'API v3 de Ghost afin de récupérer le contenu et l'indexer côté client notamment grâce à FlexSearch. Cela permet donc des recherches complètes et très rapides (chose que Ghost ne fournit toujours pas par défaut... un jour qui sait...) :

Faut bien tester avec Webkit...

Je n'ai jamais suivi de formation de développeur web, même si je "comprends" à peu près comment faire, donc j'ai pu m'en sortir en faisant d'abord une iframe crade pour tester, puis proprement avec la modal présente ci-dessus qui devrait assez bien fonctionner.

<input id="search-bar">
<ul id="search-results"></ul>

<script>
    var searchinGhost = new SearchinGhost({
        key: 'CONTENT_API_KEY'
    });
</script>

Côté code, l'implémentation est plutôt simple, c'est ensuite à vous de jouer et bricoler autour comme je l'ai fait. Si vous êtes flemmard ou pas à l'aise avec le web, il existe un wrapper qui fait tout pour vous (avec une iframe, donc un peu crade pour moi, mais ça marche).

La limitation est fixée par localStorage qui n'a pas une taille infinie : une fois qu'elle sera atteinte, je serai contraint d'abandonner l'indexation complète (je pense qu'avec les titres + chapitres c'est déjà pas mal). Pour l'instant, on n'a pas ce problème, donc autant en profiter.

Déclaration d'amour à Metroid

Côté jeux-vidéos, j'ai toujours été un vendu aux licences Nintendo. Metroid n'en fait pas exception, mais c'est une licence que j'avais relativement peu explorée jusqu'à présent. J'ai eu l'occasion de jouer à certains jeux GBA/(S)NES disponibles via le programme ambassadeur de la 3DS (oui...), et Super Metroid quelques années plus tard : je n'étais pas insensible au charme de la série, mais ce n'est qu'en la revisitant en cette période que je me rends compte à quel point les jeux Metroid me correspondent.

Depuis février, je me suis donc décidé : j'allais faire et/ou refaire tous les Metroid par ordre chronologique "canon".

  • Metroid Zero Mission (GBA)
  • Metroid Prime Trilogy (Wii)
  • Metroid II (remakes)
  • Super Metroid (SNES)
  • Metroid Other M (Wii)
  • Metroid Fusion (GBA)

J'exclus les spin-offs, et bien que j'étais tenté d'exclure Other M sur conseils de la communauté Metroid, je me suis décidé à le faire malgré tout.

La trilogie Prime a été une expérience tout simplement incroyable. N'ayant plus de Wii, j'ai opté pour cette solution où je décris ladite expérience :

(Re)découvrez Metroid Prime Trilogy sur PC
Découverte du mod PrimeHack qui permet de jouer à Metroid Prime avec les contrôles clavier/souris, ainsi que des textures HD qui permettent de lui donner une seconde jeunesse visuelle.

J'ai ensuite enchaîné avec Metroid II, plus précisément son remake AM2R (Another Metroid 2 Remake). Ce n'est pas un jeu officiel, mais il est si bien conçu et respectueux du jeu d'origine qu'il est considéré comme un incontournable parmi les jeux Metroid.

Ce jeu est effectivement merveilleux avec une finition à en faire pâlir des jeux officiels. J'ai envie d'en parler plus en détails, mais ça sera dans un article qui sera publié quand j'aurai fait l'autre remake de Metroid II, l'officiel Metroid: Samus Returns.

AM2R est rempli de bonnes idées : tout d'abord la DA est sublime, les OST également, et est assez fidèle au jeu de base en le rendant accessible. Metroid II est un jeu Game Boy à l'origine, et quand je dis Game Boy je parle de la vieille avec ces graphismes :

Bien sûr, le jeu original n'est pas mauvais, il était très bien pour les fans qui voulaient une suite de Metroid I à l'époque. Mais les jeux Game Boy ont mal vieilli contrairement aux jeux SNES/GBA, et sont peu accessibles. De plus, Metroid II n'a pas de map intégrée par exemple, donc la sensation de perte sera totale pour ainsi dire… voire frustrante. Fort heureusement, AM2R est agréable de bout en bout à tel point que c'est même une bonne expérience pour un premier Metroid.

Je vous conseille ce jeu, dont l'auteur DoctorM64 a participé au super jeu Ori and the Will of the Wisps suite au coup de pression de Nintendo qui a interromput le développement d'AM2R. Heureusement, le jeu reçoit des mises à jour communautaires : allez voir ici.

Je reparlerai de Metroid très bientôt de toute façon, concernant AM2R ou d'autres jeux encore. Sachez par ailleurs que je mets à jour régulièrement mon article sur la trilogie comme je suis activement le développement de PrimeHack (et j'ai aidé à corriger un problème sur la dernière version, ça m'a rappelé des cauchemars sur Visual Studio...).

"Spring spleen"

Ce n'est pas forcément lié au pseudo-confinement en place, mais je ressens souvent un spleen saisonnier. Sans aller dans les détails, je suis toujours dans une période où je me pose beaucoup de questions : quel avenir pour ce monde ? quel avenir pour moi dans celui-ci ? pourquoi ? que dois-je faire à court terme ? comment dois-je prévoir le long terme ? (bis repetita)

Je pense que la situation est difficile pour tout le monde, et les mesures sanitaires et plus généralement le contexte de la pandémie y sont pour beaucoup. Cela dit cette situation de lassitude et d'indécision dure déjà depuis quelques années pour moi, si bien que ma perception du temps s'en retrouve déformée et que les années n'ont plus de sens.

Je ne sais pas pourquoi je dis ça ici, mais peut-être que ça fera écho à d'autres personnes et je sais que je ne suis pas le seul.

Pour limiter ce spleen, outre des activités stimulantes mentalement mais physiquement aussi (je vous conseille de garder une forme physique quoiqu'il arrive), j'ai remis au goût du jour un de mes jeux préférés qui n'est autre que The Legend of Zelda: Breath of The Wild. Mes mots ne suffiront pas à décrire l'affection intime que j'entretiens pour ce jeu.

Du coup, par remettre au "goût du jour", j'entends une expérience un peu différente de mes centaines d'heures passées sur ma Switch : BOTW en 4k ! C'est possible ? Avec CEMU bien entendu ! Et le résultat est sublime pour un jeu déjà très beau sur son support original (j'insiste) :

Ah ! On va s'arrêter là, sinon je vais saturer vos connexions.

Outre les paramètres graphiques, j'utilise un outil pour la caméra libre :

etra0/botw-freecam
Freecam for the game Zelda: Breath of the Wild for the Wii U using the Cemu emulator - etra0/botw-freecam

C'est un injecteur écrit en Rust bien pratique. Comme tout injecteur, vérifiez par vous-même le code source avant de l'executer, au moins par bonne pratique ; ici le code est très simple à comprendre.

Je vous laisse ici sur ces images de mon jeu préféré, et à bientôt pour un prochain article (qui sera moins personnel, promis).