Documenter l'installation YunoHost éprouvée en production

Section « Installation YunoHost » : la chaîne réelle, les pièges de
publication (dépôt privé qui répond 404, manifeste lu depuis Gitea), les
deux pièges systemd avec leur mesure, l'écart entre le propriétaire
d'install_dir déclaré et celui appliqué, et ce qui a été vérifié en
exécution — installation, WAL, sauvegarde, mise à jour, désinstallation.

L'étape 9 des prochaines étapes est faite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mathieu
2026-08-18 21:05:42 +02:00
co-authored by Claude Opus 5
parent 922fd024e7
commit bfbd90fabd
+91 -1
View File
@@ -838,6 +838,96 @@ extérieur ou par Échap. Le calque est focalisé à l'ouverture (`FocusAsync`)
**Le substitut à initiale n'est jamais cliquable** : le bouton déclencheur n'est rendu que
lorsqu'une URL de couverture existe. Sans couverture, il n'y a rien à agrandir.
## Installation YunoHost — éprouvée en production le 2026-08-18
Le paquet vit dans le dépôt `mabibli_ynh` (voir « Deux dépôts distincts »). Installé,
mis à jour et sauvegardé sur un vrai serveur ; ce qui suit est ce que l'exercice a
appris, pas ce qu'on en attendait.
### La chaîne, telle qu'elle tourne
```
publier-release.sh → archive tar.gz (69 Mo, self-contained) → release Gitea
→ manifest.toml (amd64.url + amd64.sha256)
→ yunohost app install <url du dépôt _ynh>
```
Le script est la seule source des trois valeurs qui doivent rester cohérentes :
version, URL, sha256. ⚠️ Il ne réécrit **que** `manifest.toml` — les autres mentions
de l'URL (`README.md`, `Documentation=` de l'unité systemd) sont à traiter à la main.
⚠️ **Le dépôt du code doit être public.** `ynh_setup_source` télécharge sans jeton.
Gitea répond **404 et non 403** à un anonyme sur un dépôt privé : le symptôme est
rigoureusement identique à « la release n'existe pas », ce qui envoie chercher au
mauvais endroit. Contrôle qui tranche, hors session authentifiée :
`curl -fsSLI "<url du manifeste>"`.
⚠️ **YunoHost lit le manifeste depuis Gitea**, jamais la copie locale. Une correction
non poussée est une correction qui n'existe pas — constaté deux fois.
### Deux pièges systemd, tous deux invisibles hors d'un vrai serveur
Ni l'un ni l'autre ne peut sortir d'un `dotnet run` ou d'un lancement du publish à la
main : ils tiennent au gestionnaire de services, pas à l'application. C'est ce qui
justifie de tester l'installation réelle plutôt que le seul binaire.
| Piège | Symptôme | Correction |
|---|---|---|
| `Environment=` **découpe sur les espaces** | `ArgumentException: Format of the initialization string … at index 0` | guillemeter **toute** la ligne : `Environment="ConnectionStrings__MaBibli=Data Source=…"` |
| `ProtectHome=yes` masque `/home` | `SQLite Error 14: unable to open database file` | `ProtectHome=tmpfs` + `BindPaths=__DATA_DIR__` |
Le premier ne définissait pas une variable mais **deux** : `…__MaBibli=Data` et un
`Source=…` parasite. Le second est contre-intuitif parce que le `data_dir` de
YunoHost vit sous `/home/yunohost.app/<app>` : `ReadWritePaths=` **ne perce pas**
`ProtectHome` — mesuré sur une unité de test, `yes` + `ReadWritePaths` échoue,
`tmpfs` + `BindPaths` réussit. `tmpfs` garde l'essentiel du bénéfice : les répertoires
personnels du serveur restent invisibles au service.
### `install_dir` n'appartient pas à root, contrairement au manifeste
Le manifeste déclare `owner = "root:rwx"` pour que le service ne puisse pas réécrire
ses propres binaires. En pratique le helper `_ynh_apply_default_permissions` repasse
derrière avec `chown -R <app>:<app>`. Observé : `/var/www/mabibli` appartient à
`mabibli`.
**L'intention tient quand même**, mais par un seul mécanisme au lieu de deux :
`ProtectSystem=strict` met tout le système en lecture seule dans le namespace du
service, `ReadWritePaths=` ne listant que le `data_dir`. ⚠️ Ne pas retirer
`ProtectSystem=strict` en croyant que la propriété des fichiers protège encore.
### Ce qui a été vérifié en exécution
- **Installation** : archive téléchargée, sha256 contrôlé, migrations EF Core
appliquées au premier démarrage (`Application started` en 5 s, timeout de 60 s
largement suffisant), base créée dans `data_dir`.
- **Écoute sur `127.0.0.1` uniquement** — la contrainte dure de « Intégration SSO »
est tenue en production, vérifiée par `ss -tlnp`.
- **Mode WAL confirmé sur le serveur** (`PRAGMA journal_mode``wal`) : la
justification du `.backup` de `scripts/backup` n'est pas théorique. Les fichiers
`-wal`/`-shm` n'existent pas au repos (SQLite fait un checkpoint à la fermeture de
la dernière connexion) — leur absence dans un listing ne veut pas dire que le mode
a changé.
- **Sauvegarde** : l'archive contient bien `mabibli-instantane.db` **à côté** de la
base vivante et de ses `-wal`/`-shm`. C'est la restauration qui écarte ces derniers.
- **Mise à jour** : la sauvegarde de sécurité pré-upgrade **saute `data_dir`**
(`BACKUP_CORE_ONLY`), comme prévu, et la bibliothèque survit au remplacement
intégral d'`install_dir`.
- **Désinstallation** : `remove` et `remove --purge` fonctionnent tous deux. Sans
`--purge`, `data_dir` survit — donc **réinstaller après un `remove` retrouve
l'ancienne base**. Ce n'est pas une installation vierge, même si tout le reste est
neuf : piège à connaître en phase d'essai.
**La marche à suivre — première mise en production, montée de version, retour arrière,
désinstallation — est écrite pas à pas dans `mabibli_ynh/PUBLICATION.md`.** Elle n'est
pas recopiée ici : ce sont des gestes, pas des décisions.
### Ce que l'installation impose et qui ne se contourne pas
**Un domaine entier** (`mabibli.mondomaine.tld`), pas un sous-chemin : le
`<base href="/">` et les empreintes de `service-worker-assets.js` sont figés à la
compilation, et rien n'est recompilé sur le serveur. Le manifeste déclare donc
l'application en `full_domain`.
## Historique du projet (pourquoi ces choix)
L'utilisateur a testé deux solutions existantes avant de se lancer dans un projet custom :
@@ -857,7 +947,7 @@ Conclusion : aucune des deux solutions existantes ne coche toutes les cases (pr
6. Gestion des prêts
7. Intégration scan caméra (**ZXing.Net** + interop caméra minimal)
8. ~~Cache hors-ligne pour la consultation~~ — fait le 2026-08-18 (voir « Stratégie hors-ligne »)
9. Packaging YunoHost (`manifest.toml`, `conf/systemd.service`, `conf/nginx.conf`, `scripts/install`) en s'inspirant de radarr_ynh
9. ~~Packaging YunoHost~~ — fait le 2026-08-18, installé et mis à jour sur un serveur réel (voir « Installation YunoHost »)
## Questions ouvertes