diff --git a/A_FAIRE.md b/A_FAIRE.md index 0db1d31..02c71b8 100644 --- a/A_FAIRE.md +++ b/A_FAIRE.md @@ -1,147 +1,82 @@ -# À faire avant la première installation +# État du paquet -État au 2026-08-18. Ce qui est fait est conservé en bas de page : le savoir *pourquoi* -c'était à faire reste utile à la prochaine version. +Mis à jour le 2026-08-18, après une installation, une sauvegarde et une mise à jour +réussies sur le serveur réel. + +**Aucune tâche bloquante.** Ce fichier garde les contraintes permanentes et le +journal de ce qui a été réglé — le *pourquoi* resservira à la prochaine version. --- -## 1. Rendre le dépôt du code accessible sans authentification — BLOQUANT +## Publier une nouvelle version -`ynh_setup_source` télécharge l'archive **sans jeton**. Tant que le dépôt `mabibli` -est privé, l'installation échoue au téléchargement. +📖 La marche à suivre est dans [`PUBLICATION.md`](PUBLICATION.md) — première mise en +production, montée de version, retour arrière, désinstallation. -### Le symptôme, et pourquoi il induit en erreur +En résumé : `./build/publier-release.sh --version X.Y.Z`, tag et release sur le dépôt +du code, téléversement de l'archive, puis commit **et push** du manifeste. -Constaté le 2026-08-18, sans authentification : - -``` -/mathieu/mabibli → 404 -/mathieu/mabibli/releases → 404 -/api/v1/repos/mathieu/mabibli → 404 -``` - -Gitea répond **404 et non 403** à un visiteur anonyme sur un dépôt privé, pour ne pas -révéler son existence. Le symptôme est donc **exactement le même** que « la release -n'a pas été créée » ou « le fichier n'a pas été téléversé ». Ne pas chercher du côté -de la release avant d'avoir réglé la visibilité : les deux causes sont indiscernables. - -Que le dépôt existe bien est vérifiable autrement, par SSH (clé, donc authentifié) : - -``` -git ls-remote --tags origin -``` - -### Ce qu'il faut faire - -Dans Gitea, sur le dépôt `mathieu/mabibli` : *Settings → Danger Zone → Make -repository public*. Le code est déjà sous AGPL-3.0-or-later, la publication est -cohérente avec la licence. - -Si le dépôt doit rester privé, il faut héberger l'archive ailleurs, sur une URL -publique, et faire pointer `--base-url` dessus. Un jeton dans l'URL du manifeste -serait à écarter : le manifeste est lu par YunoHost et lisible sur le serveur. +Le suffixe `~ynhN` se bump à la main quand seul le paquet change (conf, scripts) sans +nouvelle archive — c'est ce qui a été fait pour `~ynh2` et `~ynh3`. --- -## 2. Créer la release et y téléverser l'archive +# Contraintes permanentes — ce ne sont pas des tâches -À faire (ou à vérifier — voir point 1, tant que le dépôt est privé on ne peut pas -savoir si c'est déjà fait). +## Le dépôt du code doit rester public -Le tag est déjà poussé : `v0.1.0` → `922fd02`. Reste à créer la release -correspondante dans l'interface Gitea et à y téléverser : - -``` -build/dist/mabibli-0.1.0-linux-x64.tar.gz -``` - -⚠️ **L'archive à téléverser est celle produite le 2026-08-18**, `sha256` -`e3316bd05cd71234403695bab405f48009d8d6186b0074235396d622c8c0caef`. Une archive plus -ancienne traînant sur le disque ne correspondrait plus au manifeste, et -l'installation s'interromprait à la vérification d'intégrité — comportement voulu, -mais autant ne pas s'y heurter. - -⚠️ Elle fait **69 Mo** : c'est normal, le publish est *self-contained* et embarque le -runtime .NET. C'est précisément ce qui évite d'installer `dotnet-runtime` sur le -serveur YunoHost. - -### Comment vérifier - -``` -curl -fsSLI "https://git.akbar.nohost.me/mathieu/mabibli/releases/download/v0.1.0/mabibli-0.1.0-linux-x64.tar.gz" | head -1 -``` - -Doit répondre `HTTP/2 200`, **sans être connecté**. Et le `sha256` du manifeste doit -correspondre à l'archive réellement déposée : - -``` -sha256sum build/dist/mabibli-0.1.0-linux-x64.tar.gz -grep sha256 manifest.toml -``` - ---- - -## 3. Committer et pousser le paquet - -Le manifeste corrigé n'est pas encore poussé. `yunohost app install ` -lit le manifeste **depuis Gitea**, pas depuis la copie locale : sans ce push, -l'installation repartira sur l'ancienne URL fictive. - -``` -git add -A && git commit && git push -``` - ---- - -# Contrainte permanente — ce n'est pas une tâche +`ynh_setup_source` télécharge **sans jeton**. Sur un dépôt privé, Gitea répond +**404 et non 403** à un anonyme : le symptôme est identique à « la release n'existe +pas », ce qui envoie chercher au mauvais endroit. Ne pas mettre de jeton dans l'URL +du manifeste — il serait lisible sur le serveur. ## L'application exige un domaine entier, pas un sous-chemin -**Contrainte technique, pas un choix.** MaBibli doit être installée sur -`mabibli.mondomaine.tld`, et **non** sur `mondomaine.tld/mabibli`. +MaBibli s'installe sur `mabibli.mondomaine.tld`, **pas** sur `mondomaine.tld/mabibli`. -### Pourquoi +Deux éléments sont figés **à la compilation** du client Blazor WebAssembly : la balise +`` de `index.html`, et les empreintes d'intégrité de +`service-worker-assets.js`. Les réécrire sur le serveur casserait le service worker, +donc le mode hors-ligne — et rien n'est recompilé sur le serveur, c'est tout l'intérêt +du self-contained. Le paquet déclare donc l'application en `full_domain`. -Deux éléments sont figés **à la compilation** du client Blazor WebAssembly : +Lever cette contrainte demanderait une archive **par chemin d'installation**, ou une +compilation sur le serveur. Les deux annulent le bénéfice du self-contained. -- la balise `` de `index.html`, qui détermine la racine de toutes les - URL de l'application ; -- les empreintes d'intégrité inscrites dans `service-worker-assets.js`, qui - garantissent que le service worker sert bien les fichiers attendus. +## `install_dir` finit par appartenir à l'application, pas à root -Réécrire ces valeurs sur le serveur au moment de l'installation casserait les -empreintes, donc le service worker, donc le fonctionnement hors-ligne. Et comme -rien n'est recompilé côté serveur — c'est tout l'intérêt du déploiement -self-contained —, il n'existe pas de solution propre pour un sous-chemin. +Le manifeste demande `owner = "root:rwx"` pour que le service ne puisse pas réécrire +ses binaires, mais le helper `_ynh_apply_default_permissions` repasse derrière avec un +`chown -R mabibli:mabibli`. L'intention tient quand même, portée par +`ProtectSystem=strict` dans l'unité systemd : tout est en lecture seule sauf +`ReadWritePaths=`, qui ne liste que le `data_dir`. -Le paquet déclare donc l'application en `full_domain` auprès de YunoHost, qui -demandera un domaine dédié à l'installation. - -### Si tu voulais lever cette contrainte plus tard - -Il faudrait produire une archive **par chemin d'installation**, ou recompiler sur le -serveur. Les deux annulent le bénéfice du self-contained. À moins d'un besoin réel, -un sous-domaine reste la bonne réponse. +⚠️ Ne pas retirer `ProtectSystem=strict` en croyant que la propriété des fichiers +protège encore. --- # Fait +## Les deux pièges systemd — 2026-08-18 + +Découverts à la première installation réelle, chacun a coûté un cycle complet. Ni +l'un ni l'autre ne peut sortir d'un lancement du binaire à la main : ils tiennent au +gestionnaire de services. + +| Piège | Symptôme | Correction | +|---|---|---| +| `Environment=` découpe sur les espaces | `ArgumentException … at index 0` | guillemeter toute la ligne | +| `ProtectHome=yes` masque `/home` | `SQLite Error 14: unable to open database file` | `ProtectHome=tmpfs` + `BindPaths=` | + +Détail et mesures dans les commentaires de `conf/systemd.service`, à ne pas retirer. + ## L'URL de démonstration a été remplacée — 2026-08-18 -Le `manifest.toml` portait une URL **fictive** (`gitea.example.org`) : le paquet ne -pouvait pas connaître le vrai domaine Gitea au moment de sa création. Constaté en -conditions réelles — l'installation s'arrête net au `Prefetching asset main`. - -Corrigé par `./build/publier-release.sh`, qui recompile, vérifie le publish, produit -l'archive, calcule son `sha256` et l'inscrit dans le manifeste avec la bonne URL. -`https://git.akbar.nohost.me/mathieu/mabibli/releases/download` est désormais la -valeur **par défaut** de `--base-url` dans le script : l'option n'est plus à passer. - -Les occurrences hors manifeste, que le script ne touche pas, ont été traitées à la -main : `README.md` et `conf/systemd.service` (directive `Documentation=`). Le -contrôle reste `grep -rn "gitea.example.org" .` — il ne doit plus rien remonter que -ce fichier-ci. +`manifest.toml` portait `gitea.example.org` : l'installation s'arrêtait net au +`Prefetching asset main`. Corrigé partout (`manifest.toml`, `README.md`, +`conf/systemd.service`, et la valeur par défaut de `--base-url`). Contrôle : +`grep -rn "gitea.example.org" .` ne doit plus rien remonter que ce fichier-ci. ## L'archive ne va plus dans git — 2026-08-18 diff --git a/PUBLICATION.md b/PUBLICATION.md new file mode 100644 index 0000000..e71de5f --- /dev/null +++ b/PUBLICATION.md @@ -0,0 +1,256 @@ +# Mettre MaBibli en production, et la mettre à jour + +Deux marches à suivre, éprouvées sur un serveur réel le 2026-08-18 : la **première mise +en production**, puis la **montée de version**. Les contraintes qui les gouvernent sont +expliquées dans `A_FAIRE.md` — ici, ce sont les gestes. + +Principe qui explique toute la chaîne : **le serveur ne compile jamais**. Il télécharge +une archive déjà produite, vérifie son empreinte, et la déploie. Le SDK .NET n'est +nécessaire que sur la machine de développement. + +``` +machine de dev Gitea serveur YunoHost +────────────── ───── ──────────────── +publier-release.sh + → archive .tar.gz ────────► release du dépôt `mabibli` + → manifest.toml ────────► dépôt `mabibli_ynh` ──────► yunohost app install + (url + sha256) (vérifie le sha256) +``` + +--- + +# A. Première mise en production + +## A.1 Préalables, une seule fois + +- Le dépôt du code (`mabibli`) est **public** sur Gitea. `ynh_setup_source` télécharge + sans jeton ; sur un dépôt privé Gitea répond 404 — indiscernable d'une release + absente. +- Le domaine dédié existe côté YunoHost. MaBibli s'installe sur un **domaine entier** + (`mabibli.mondomaine.tld`), jamais sur un sous-chemin : + +```bash +sudo yunohost domain add mabibli.mondomaine.tld +``` + +- Le SDK .NET est installé sur la machine de développement (`dotnet --version`). + +## A.2 Produire l'archive + +Depuis `mabibli_ynh`, avec le dépôt du code à côté (`../mabibli`) : + +```bash +./build/publier-release.sh +``` + +Le script compile en `Release` self-contained, **vérifie le publish** (binaire présent, +`wwwroot/` embarqué, toutes les ressources d'`index.html` réellement sur disque, aucun +placeholder d'empreinte non substitué), produit l'archive de façon reproductible, en +calcule le `sha256`, et réécrit `version`, `amd64.url` et `amd64.sha256` dans +`manifest.toml`. + +À contrôler dans sa sortie : la ligne `binaire, wwwroot et ressources d'index.html : OK`, +et le `sha256` affiché — c'est celui que le manifeste porte désormais. + +## A.3 Déposer la release + +```bash +cd ../mabibli && git tag v0.1.0 && git push origin main --tags +``` + +Puis, **dans l'interface Gitea** : créer la release `v0.1.0` sur le dépôt `mabibli`, et y +téléverser `build/dist/mabibli-0.1.0-linux-x64.tar.gz`. + +Vérification, à faire **sans être authentifié** (autre navigateur, ou `curl` comme +ci-dessous) : + +```bash +curl -fsSLI "https://git.akbar.nohost.me/mathieu/mabibli/releases/download/v0.1.0/mabibli-0.1.0-linux-x64.tar.gz" | head -1 +``` + +Attendu : `HTTP/2 200`. Un 404 signifie soit que la release n'est pas déposée, soit que +le dépôt est privé — les deux se ressemblent, commencer par vérifier la visibilité. + +## A.4 Pousser le paquet + +⚠️ **L'étape la plus facile à oublier.** `yunohost app install ` lit le manifeste +**depuis Gitea**, jamais la copie locale : un manifeste corrigé mais non poussé n'existe +pas pour le serveur. + +```bash +cd ../mabibli_ynh && git add -A && git commit -m "Publier la version 0.1.0" && git push +``` + +## A.5 Installer + +```bash +sudo yunohost app install https://git.akbar.nohost.me/mathieu/mabibli_ynh --debug +``` + +YunoHost demande le domaine (celui créé en A.1) et le groupe autorisé (`all_users`). + +**Ce qu'il faut voir passer :** `Prefetching asset main` sur la bonne URL, puis +l'installation des fichiers, nginx, systemd, et enfin `The service mabibli has correctly +executed the action start` — le script attend la ligne `Application started` du journal, +ce qui fait échouer franchement l'installation si les migrations EF Core ne passent pas. + +## A.6 Vérifier + +```bash +sudo ss -tlnp | grep -i mabibli +``` + +Doit montrer **`127.0.0.1:` uniquement**. C'est une frontière de sécurité : sur +`0.0.0.0`, n'importe qui sur le réseau pourrait forger l'en-tête `YNH_USER` et se faire +passer pour un membre du foyer. + +```bash +sudo ls -l /home/yunohost.app/mabibli +``` + +`mabibli.db` doit exister : les migrations se sont appliquées seules au premier +démarrage. (Pas de `-wal` ni `-shm` au repos, c'est normal — SQLite fait un checkpoint à +la fermeture de la dernière connexion.) + +Enfin, dans un navigateur sur `https://mabibli.mondomaine.tld` : le portail authentifie, +et l'application affiche **ton** nom d'utilisateur YunoHost — pas « anonyme ». C'est le +seul contrôle qui éprouve réellement l'intégration SSO. + +--- + +# B. Monter de version — exemple : 0.1.0 → 0.1.1 + +## B.1 Ce qui distingue une version applicative d'une révision de paquet + +| Ce qui change | Version | Nouvelle archive ? | +|---|---|---| +| Le code C# | `0.1.0` → `0.1.1~ynh1` | **oui** | +| Seulement le paquet (conf systemd/nginx, scripts) | `0.1.0~ynh1` → `0.1.0~ynh2` | non | + +Le second cas est le plus simple : bump du suffixe `~ynhN` **à la main** dans +`manifest.toml`, commit, push, puis directement l'étape B.5. L'archive et son `sha256` +ne bougent pas. (C'est ce qui a été fait pour `~ynh2` et `~ynh3`.) + +La suite décrit le premier cas. + +## B.2 Compiler et vérifier la nouvelle version + +Le code est prêt et committé dans `mabibli`. Depuis `mabibli_ynh` : + +```bash +./build/publier-release.sh --version 0.1.1 +``` + +⚠️ **`--version` est obligatoire ici.** Sans lui, le script déduit la version de +`manifest.toml` et reproduirait 0.1.0. Il remet aussi le suffixe à `~ynh1` : une +nouvelle version applicative repart toujours de 1. + +Note le `sha256` affiché — il ne sera plus jamais le même, même à code identique si les +dépendances bougent. + +## B.3 Tag et release + +```bash +cd ../mabibli && git tag v0.1.1 && git push origin main --tags +``` + +Créer la release `v0.1.1` dans Gitea, y téléverser +`build/dist/mabibli-0.1.1-linux-x64.tar.gz`, puis vérifier sans authentification : + +```bash +curl -fsSLI "https://git.akbar.nohost.me/mathieu/mabibli/releases/download/v0.1.1/mabibli-0.1.1-linux-x64.tar.gz" | head -1 +``` + +## B.4 Pousser le paquet + +```bash +cd ../mabibli_ynh && git add -A && git commit -m "Publier la version 0.1.1" && git push +``` + +## B.5 Sauvegarder, puis mettre à jour + +YunoHost prend **lui-même** une sauvegarde de sécurité avant la mise à jour +(`mabibli-pre-upgrade1`), mais elle **ne contient pas le répertoire de données** +(`BACKUP_CORE_ONLY`) : elle sert à restaurer l'application, pas la bibliothèque. Prendre +une sauvegarde complète reste donc utile avant une version qui touche au schéma : + +```bash +sudo yunohost backup create --apps mabibli +``` + +```bash +sudo yunohost app upgrade mabibli --debug +``` + +(Ajouter `--force` seulement pour réappliquer une version identique, par exemple en +mise au point du paquet.) + +Le script arrête le service **avant** de remplacer les binaires — deux processus sur la +même base SQLite pendant une migration est exactement ce qu'il faut éviter — puis +attend `Application started`, avec un délai de 120 s : une migration sur base remplie +prend plus de temps que la création d'un schéma vide. + +## B.6 Vérifier après mise à jour + +```bash +sudo systemctl status mabibli --no-pager && sudo journalctl -u mabibli -n 30 --no-pager +``` + +Puis, dans le navigateur, le contrôle qui compte vraiment : **les livres sont toujours +là**. C'est ce qui valide que la base vit bien dans le répertoire de données et non à +côté du binaire — `upgrade` remplace intégralement `/var/www/mabibli`. + +Vider le cache du navigateur n'est pas nécessaire : le service worker compare les +empreintes et propose « Mettre à jour ». Sur mobile, un onglet resté ouvert peut +retarder la bascule — c'est précisément ce que le bandeau de mise à jour sert à +débloquer. + +## B.7 Si la mise à jour échoue + +YunoHost restaure automatiquement la sauvegarde de sécurité quand le script échoue. Si +le service démarre mais que l'application se comporte mal, revenir en arrière à la main : + +```bash +sudo yunohost backup list +``` + +```bash +sudo yunohost app remove mabibli --purge +``` + +```bash +sudo yunohost backup restore --apps mabibli +``` + +⚠️ `--purge` efface le répertoire de données. Ne le faire qu'avec une archive contenant +la bibliothèque sous la main — celle de B.5, pas `mabibli-pre-upgrade1`. + +--- + +# C. Désinstaller + +```bash +sudo yunohost app remove mabibli +``` + +Retire le service, la conf nginx, la permission SSO, l'utilisateur système, le port et +`/var/www/mabibli`. **`/home/yunohost.app/mabibli` survit**, donc la bibliothèque aussi : +une désinstallation faite trop vite ne doit pas être irréversible. + +⚠️ Corollaire à connaître en phase d'essai : réinstaller après un `remove` sans purge +**retrouve l'ancienne base**. Ce n'est pas une installation vierge, même si tout le +reste est neuf. + +Pour tout effacer, données comprises : + +```bash +sudo yunohost app remove mabibli --purge +``` + +Contrôle qu'il ne reste rien : + +```bash +systemctl status mabibli; sudo ls -d /var/www/mabibli /home/yunohost.app/mabibli 2>&1; getent passwd mabibli +``` + +Les trois doivent être négatifs. Le domaine, lui, reste déclaré dans YunoHost. diff --git a/README.md b/README.md index 557d151..79ae86f 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Le serveur ne compile jamais : il télécharge une archive déjà produite. La c ```bash # Depuis ce dépôt, avec le dépôt du code à côté (../mabibli) -./build/publier-release.sh --version 0.2.0 --base-url https://gitea.exemple.org/mathieu/mabibli/releases/download +./build/publier-release.sh --version 0.1.1 ``` Le script : @@ -38,7 +38,12 @@ Le script : 5. réécrit `version`, `amd64.url` et `amd64.sha256` dans `manifest.toml`. Restent à faire à la main : créer le tag et la release sur Gitea, y téléverser l'archive, puis -committer `manifest.toml`. +committer et **pousser** `manifest.toml` — YunoHost lit le manifeste depuis Gitea, pas la copie +locale. + +📖 **La marche à suivre complète est dans [`PUBLICATION.md`](PUBLICATION.md)** : première mise en +production, montée de version (avec les contrôles à chaque étape), retour arrière, et +désinstallation. Le script ne dépend d'aucun environnement de CI : tout passe par des options ou des variables d'environnement (`MABIBLI_SOURCE_DIR`, `MABIBLI_VERSION`, `MABIBLI_BASE_URL`, @@ -68,3 +73,5 @@ copier le seul fichier `.db` d'une base active peut ne rien sauvegarder du tout. - `doc/DESCRIPTION.md` — présentation affichée dans le catalogue YunoHost - `doc/ADMIN.md` — authentification, emplacement des données, sauvegarde, contraintes +- `PUBLICATION.md` — mise en production et montée de version, pas à pas +- `A_FAIRE.md` — contraintes permanentes et journal des points réglés diff --git a/build/publier-release.sh b/build/publier-release.sh index 7e280d4..ffcc567 100755 --- a/build/publier-release.sh +++ b/build/publier-release.sh @@ -16,7 +16,7 @@ # ./build/publier-release.sh \ # --source-dir "$GITHUB_WORKSPACE/mabibli" \ # --version 0.2.0 \ -# --base-url "https://gitea.exemple.org/mathieu/mabibli/releases/download" +# --base-url "https://git.akbar.nohost.me/mathieu/mabibli/releases/download" # set -euo pipefail