guest@6admin.io:~$

FluoBoard : ma veilleuse de CI depuis 2019, enfin open source

Troisième épisode de la série FluoBoard, après la carte d’origine et sa réécriture . Cette fois, ce que j’en ai fait pour moi. En 2019, j’avais déjà ma propre carte de dev et de tests, celle qui me servait à bricoler en marge du firmware de veilleuse et c’est elle que j’ai détournée: le système hérité affichait la disponibilité des salles, j’en ai fait ma veilleuse de CI. En 2021, la société où je bossais alors m’en a offert cinq ou six de plus, histoire que je puisse continuer à m’amuser. Elles sont restées sur mon bureau, pour mon seul usage: un petit serveur Python faisait le pont entre mes pipelines de CI au travail et les rubans LED, traduisant l’état de chaque build en couleur. Le concept a donc sept ans. Ce qui est nouveau en 2026, c’est que la reconstruction est open source et que le protocole d’origine a survécu intact: la board poll toujours GET /lights/<mac> et lit un seul chiffre ASCII entre <...>.

Autant l’avouer tout de suite: si j’ai conservé ce protocole à un chiffre au lieu de repartir d’une simple machine à état, c’est par fidélité à ce montage d’origine, pas par pertinence technique. En toute honnêteté, une machine à état aurait mieux fait le travail.

Le même chiffre, un nouveau maître

Les codes de 2019 décrivaient l’occupation des salles (<1> lime libre, <4> rouge booked-and-busy). Le serveur de 2026 (server/pipeline_server.py, 367 lignes) remape les chiffres sur l’état CI:

CODE_ERROR   = "0"   # error / unknown / MAC not in config
CODE_SUCCESS = "1"   # success
CODE_FAILED  = "4"   # failed / timed out
CODE_RUNNING = "6"   # running / pending / queued / created
CODE_CANCEL  = "8"   # canceled / skipped / manual
CODE_NONE    = "9"   # no pipeline found

Même canal à un chiffre, nouveau maître: le serveur poll les API de GitHub Actions et GitLab CI toutes les 30 secondes et répond au poll de la board avec le chiffre dérivé. Le ruban devient vert au succès, rouge à l’échec, orange clignotant pendant qu’une pipeline tourne. La board est une veilleuse de CI; les salles de réunion tiraient leur occupation du même chiffre des années plus tôt.

Deux comportements du serveur valent d’être volés pour n’importe quel poller: un StatusCache qui garde le dernier état connu quand l’API de CI flanche (un 500 ne doit pas faire clignoter le mur en arc-en-ciel) et des tokens qui vivent uniquement en variables d’environnement (GITHUB_TOKEN, GITLAB_TOKEN, GITLAB_TOKEN_<HOST> pour les instances self-hosted). Le tout en stdlib Python pure: pas de framework, pas de dépendances, pas de pip sur la machine qui sert le mur du bureau.

De la CI qui compile du firmware

La CI du repo elle-même (.github/workflows/ci.yml, ajoutée en juillet 2026) traite le firmware comme du logiciel, ce qu’il est:

  • une matrice de quatre profiles de compilation (default, prod-minimal, prod-robuste, dev-verbose) pour que chaque combinaison de config build toujours;
  • une garde d’overflow flash: FLASH_MAX=28672 et le build échoue si un profile la dépasse. Les régressions firmware sont silencieuses jusqu’à ce que l’upload échoue sur le matériel; la garde les rend bruyantes au moment de la PR;
  • report-size-deltas qui commente les deltas flash/RAM sur chaque PR, pour voir le coût de votre fonctionnalité avant de la merger;
  • un lint syntaxique de pipeline_server.py lui-même, parce que le serveur fait partie du même repo.

Le flash reste manuel et local exprès: make flash compile avec une définition de board custom (fluo:avr:fluoeth), détecte le port, uploade. La CI ne touche jamais au matériel. Une veilleuse de bureau qui se brique parce qu’une pipeline s’est emballée n’est pas un affichage d’état, c’est une responsabilité. L’examen de la CI: ce que vous pouvez flasher. L’examen du mur: ce que le serveur sert vraiment.

La war story GRB, deuxième partie

L’épisode 2021 racontait douze constantes de couleur commentées // red, six d’entre elles, bleus inclus. La réécriture de 2025 a mis la vérité dans un seul bloc de commentaire et le README a maintenant une section qui se lit comme une cicatrice: “Do not ‘correct’ these values or change NEO_RGB to NEO_GRB”. Le ruban est câblé physiquement en GRB; le flag du driver NEO_RGB envoie les octets dans l’ordre G-R-B; chaque appel Color() suit la convention G-R-B. Le premier visiteur qui “corrige” ça livre une LED verte en rouge. Le bloc de commentaire existe parce que le fix a été tenté et que la tentative a été annulée.

Sept ans, un chiffre

  • Les protocoles survivent au code. Le contrat à un chiffre de 2019 a traversé trois générations de firmware et deux serveurs; le code autour non et il n’avait pas besoin de.
  • Le poller doit être ennuyeux. Stdlib, tokens en env, cache du dernier état connu. Un affichage d’état qui crashe sur un blip d’API n’affiche rien.
  • La CI firmware est d’abord un problème de taille. La garde d’overflow flash et les deltas de taille en PR sont les deux checks qui attrapent les vraies régressions; le reste est de l’assurance build.
  • Écrivez vos mensonges matériels. Le bloc GRB est la documentation d’une décision que quelqu’un essaiera certainement d’annuler. Documentez-la comme si ça allait arriver, parce que ça arrivera.

[edit: 2026] L’implémentation Ansible de tout ceci: les rôles gitlab-runner et github-runner .