Distribuer l'outil comme un binaire Go autonome

serve.py supposait Python installé et une commande tapée dans un terminal
depuis le dossier du dépôt. L'objectif ici : poser l'exécutable et son
planning dans un dossier synchronisé, et les retrouver sur n'importe quelle
machine sans rien installer.

Le binaire embarque l'interface, expose les mêmes routes sur le même format,
et cherche son projets.json à côté de lui. Compilé avec CGO_ENABLED=0, il ne
dépend d'aucune bibliothèque système, pas même de la glibc ; build.sh produit
les cinq plateformes depuis une seule machine.

Le front est inchangé : les modules ES sont embarqués et servis tels quels,
et --dev les sert depuis le disque pour développer sans recompiler.

Deux ajustements dictés par le double-clic, où aucun drapeau ne peut être
passé : le port se cherche à partir de 8000 s'il est occupé, et le fichier de
données est résolu depuis l'exécutable et non depuis le répertoire courant.

La décision 3 est rectifiée au passage : elle affirmait à tort que la File
System Access API ne fonctionne pas depuis file://. Vérification faite, c'est
CORS sur les modules ES qui ferme cette voie, pas la sécurité de l'API.

serve.py est supprimé, le binaire l'ayant remplacé en usage réel.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-01 16:48:59 +02:00
parent 6b15972e3b
commit 558d42f4cb
9 changed files with 940 additions and 259 deletions

View File

@@ -44,11 +44,20 @@ décision 4).
La première approche envisagée était d'écrire directement dans le fichier depuis le navigateur, via
`showOpenFilePicker` / `createWritable`, pour se passer de tout back-end.
Sauf que cette API exige un **contexte sécurisé** : elle ne fonctionne pas depuis `file://`. Il
fallait donc de toute façon lancer un serveur statique. Le « zéro back-end » était perdu d'avance,
tout en payant trois prix : limitation aux navigateurs Chromium, re-autorisation du sélecteur de
fichier à chaque session, et maintien d'un chemin de repli export/import pour Firefox et Safari,
doublé d'un miroir `localStorage`.
> **Rectification (1ᵉʳ août 2026).** Le paragraphe suivant affirmait que l'API « ne fonctionne pas
> depuis `file://` ». C'est faux, et la vérification sur Chromium 150 le montre : `file://` **est**
> un contexte sécurisé (`isSecureContext === true`), `showSaveFilePicker` y ouvre bien son
> dialogue, et `localStorage` comme IndexedDB y persistent d'une session à l'autre — un handle
> peut donc y être mémorisé. Ce qui bloque réellement en `file://`, c'est autre chose : les
> **modules ES** et `fetch` y sont interdits par CORS (origine `null`), donc c'est la décision 10
> qui ferme cette voie, pas la sécurité de l'API. La conclusion ci-dessous — lancer un serveur —
> reste la bonne, mais pour ce motif-là. Voir décision 22.
Cette API exige un **contexte sécurisé**, et l'on avait cru qu'elle ne fonctionnait pas depuis
`file://`. Il fallait donc de toute façon lancer un serveur statique. Le « zéro back-end » était
perdu d'avance, tout en payant trois prix : limitation aux navigateurs Chromium, re-autorisation du
sélecteur de fichier à chaque session, et maintien d'un chemin de repli export/import pour Firefox
et Safari, doublé d'un miroir `localStorage`.
Puisqu'une commande doit être lancée dans tous les cas, autant qu'elle serve à quelque chose. Un
serveur de la bibliothèque standard, `GET` et `PUT` sur `/api/data`, tient en une centaine de lignes
@@ -132,6 +141,13 @@ compilation obsolète.
Node n'est utilisé que pour lancer les tests, via son lanceur intégré (`node --test`), sans aucune
dépendance.
**Portée après la décision 22.** Le serveur se compile désormais, mais le front, lui, ne se
compile toujours pas : aucun bundler, aucune transformation, aucun `node_modules`. Les fichiers de
`js/` sont embarqués tels quels dans le binaire et servis tels quels au navigateur — c'est bien la
ligne qu'on lit dans l'éditeur qui s'exécute, et `--dev` les sert directement depuis le disque.
Ce qui était visé ici est donc intact ; ce qui change, c'est seulement la façon dont le serveur est
distribué.
## 11. Un en-tête à trois bandes, la semaine pour grain le plus fin
L'en-tête affichait une seule rangée de graduations dont le contenu changeait selon le
@@ -439,3 +455,48 @@ même si l'on ferme le panneau sans rien saisir. Il porte alors le nom
« Nouveau projet », se voit sur la frise, et se supprime d'un clic depuis son
propre panneau — ce qui reste moins coûteux que d'imposer un dialogue à tout le
monde à chaque création.
## 22. Un binaire Go plutôt qu'un script Python
`serve.py` supposait Python installé et une commande tapée dans un terminal, depuis le dossier du
dépôt. C'est une friction quotidienne pour un outil qu'on veut ouvrir d'un geste, et un obstacle
net sous Windows, où Python est absent par défaut.
L'objectif visé : **poser l'outil et son planning dans un dossier synchronisé, et les retrouver
sur n'importe quelle machine**. Trois voies menaient là.
**Un `index.html` autonome en `file://`.** Séduisante — zéro exécutable, longévité maximale — mais
elle impose de renoncer aux modules ES, bloqués par CORS depuis `file://` (voir la rectification en
décision 3), donc de concaténer les sources en un fichier unique. Et surtout elle enferme dans
Chromium : Firefox et Safari n'implémentent pas la File System Access API.
**Tauri.** Accès direct au disque, sans dialogue ni permission, et le code du navigateur reste
intact. Mais un webview système à installer sous Linux (WebKitGTK), un rendu à vérifier sur trois
moteurs différents, une chaîne Rust et une intégration continue multi-OS pour produire les
binaires. C'est contradictoire avec la décision 10 : une chaîne Tauri de 2026 ne se recompilera pas
aussi sûrement dans dix ans qu'un fichier HTML s'ouvrira.
**Un binaire Go qui contient le serveur et l'interface**, retenu. Il conserve exactement
l'architecture éprouvée — mêmes routes, même fichier JSON, mêmes sauvegardes horodatées, front
inchangé — en supprimant la seule vraie friction, l'installation. `CGO_ENABLED=0` produit un
exécutable statique de 6 Mo qui ne dépend de rien, pas même de la glibc, et les cinq plateformes se
compilent depuis une seule machine sans intégration continue.
Face à `file://`, il garde **tous** les navigateurs et les modules ES. Face à Tauri, il n'exige
aucun webview et divise l'outillage par dix. Ce qu'il concède : l'interface s'ouvre dans un onglet
plutôt que dans une fenêtre d'application, et il faut un binaire par plateforme.
Deux choix de détail méritent d'être notés.
Le port n'est plus fixe : lancé par un double-clic, l'utilisateur n'a aucun moyen de passer
`--port` si 8000 est occupé. Le serveur essaie donc les dix premiers ports libres à partir de 8000.
Un `--port` explicite, lui, n'est jamais contourné — c'est un choix de l'utilisateur.
Le fichier de données est cherché **à côté de l'exécutable**, pas dans le répertoire courant, qui
est imprévisible lors d'un double-clic. Le mode `--dev` fait exception : il sert l'interface depuis
le disque et lit `data/projets.json`, pour qu'un rafraîchissement suffise à voir ses modifications
sans recompiler.
`serve.py` a été supprimé une fois le binaire validé en usage réel. Il n'y avait rien à migrer : le
format du fichier et les routes sont identiques, et son historique reste dans git si le besoin de
le relire se présentait.