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:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user