diff --git a/A_FAIRE.md b/A_FAIRE.md deleted file mode 100644 index 02c71b8..0000000 --- a/A_FAIRE.md +++ /dev/null @@ -1,84 +0,0 @@ -# État du paquet - -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. - ---- - -## Publier une nouvelle version - -📖 La marche à suivre est dans [`PUBLICATION.md`](PUBLICATION.md) — première mise en -production, montée de version, retour arrière, désinstallation. - -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. - -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`. - ---- - -# Contraintes permanentes — ce ne sont pas des tâches - -## Le dépôt du code doit rester public - -`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 - -MaBibli s'installe sur `mabibli.mondomaine.tld`, **pas** sur `mondomaine.tld/mabibli`. - -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`. - -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. - -## `install_dir` finit par appartenir à l'application, pas à root - -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`. - -⚠️ 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 - -`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 - -`build/dist/` est ignoré. L'archive vit dans la release Gitea ; committée, elle -ajouterait 69 Mo d'historique **par version**, définitivement. diff --git a/PUBLICATION.md b/PUBLICATION.md deleted file mode 100644 index 2ee499a..0000000 --- a/PUBLICATION.md +++ /dev/null @@ -1,262 +0,0 @@ -# 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 -u https://git.akbar.nohost.me/mathieu/mabibli_ynh --debug -``` - -⚠️ **L'option `-u` n'est pas facultative ici.** MaBibli n'est pas dans le catalogue -officiel : sans elle, YunoHost ne sait pas où retrouver le paquet et refuse d'emblée — -« mabibli is not in the catalog (anymore?) », puis « No apps can be upgraded ». Rien -n'est cassé pour autant, la commande n'a simplement pas commencé. C'est la **même URL** -qu'à l'installation, celle du dépôt `_ynh`, jamais celle de l'archive. - -(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 79ae86f..5ff51bd 100644 --- a/README.md +++ b/README.md @@ -4,74 +4,32 @@ Paquet d'installation de [MaBibli](https://git.akbar.nohost.me/mathieu/mabibli), gestion de bibliothèque personnelle (catalogue, prêts, scan ISBN). Ce dépôt **ne contient aucun code C#**. Il porte uniquement de quoi installer sur YunoHost une -archive déjà compilée : le manifeste, les configurations nginx et systemd, et les scripts -d'installation. +archive déjà compilée : le manifeste, les configurations nginx et systemd, les scripts +d'installation, et les scripts de publication. -## Contenu +## 📖 Toute la documentation est dans le dépôt du code -| Fichier | Rôle | -|---|---| -| `manifest.toml` | Identité, version, URL de l'archive et son `sha256`, ressources (utilisateur système, répertoires, port, permissions) | -| `conf/systemd.service` | Unité du service — écoute sur `127.0.0.1`, base dans le répertoire de données, durcissement | -| `conf/nginx.conf` | Reverse proxy et intégration SSOwat | -| `scripts/_common.sh` | Variables partagées et sauvegarde/restauration cohérente de la base SQLite | -| `scripts/install` `remove` `upgrade` `backup` `restore` | Cycle de vie de l'application | -| `build/publier-release.sh` | Compile, archive, calcule le `sha256` et met à jour `manifest.toml` | +Elle a été regroupée dans **[le README de `mabibli`](https://git.akbar.nohost.me/mathieu/mabibli#readme)**, +qui est le point d'entrée unique du projet : contenu et points de conception du paquet, +publication d'une version, mise en production pas à pas, montée de version, retour arrière, +désinstallation, et contraintes permanentes. + +Il n'y a qu'une seule chaîne de publication et un seul projet : deux jeux de documentation +finissaient par diverger, et l'on ne savait plus lequel faisait autorité. + +Ne restent ici que les deux fichiers que **YunoHost lit lui-même** et qui doivent donc vivre +à côté du manifeste : + +- `doc/DESCRIPTION.md` — présentation affichée dans le catalogue d'applications +- `doc/ADMIN.md` — authentification, emplacement des données, sauvegarde ; affichée dans + l'interface d'administration une fois l'application installée ## Publier une nouvelle version -Le serveur ne compile jamais : il télécharge une archive déjà produite. La chaîne est donc -« compiler ici, déposer une release, mettre à jour le manifeste ». - ```bash # Depuis ce dépôt, avec le dépôt du code à côté (../mabibli) -./build/publier-release.sh --version 0.1.1 +./build/publier.sh ``` -Le script : - -1. lance `dotnet publish MaBibli.Api -c Release -r linux-x64 --self-contained` ; -2. vérifie que le binaire est là, que `wwwroot/` a bien été embarqué, et que **toutes** les - ressources référencées par `index.html` existent réellement dans le publish ; -3. produit `build/dist/mabibli--linux-x64.tar.gz`, de façon reproductible ; -4. en calcule le `sha256` ; -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 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`, -`MABIBLI_OUTPUT_DIR`), il ne pose aucune question et s'arrête à la première erreur. Il est donc -utilisable tel quel dans Gitea Actions le jour où un runner sera disponible. - -## Points de conception - -**Le service n'écoute que sur `127.0.0.1`.** L'application déduit l'identité de l'en-tête -`YNH_USER` injecté par SSOwat ; cet en-tête n'est digne de confiance que si nginx est le seul -chemin d'accès. Un service exposé sur le réseau permettrait de forger `YNH_USER` et de -contourner le portail. La contrainte est écrite dans `conf/systemd.service` (`ASPNETCORE_URLS`) -et le port n'est pas ouvert au pare-feu. - -**Aucune compilation sur le serveur.** Le publish est *self-contained* : il embarque son propre -runtime .NET, donc aucun paquet `dotnet-runtime` n'est nécessaire. `ynh_setup_source` vérifie le -`sha256` de l'archive avant de la déployer. - -**Les données survivent aux mises à jour.** La base SQLite vit dans le répertoire de données, pas -à côté du binaire ; `upgrade` ne remplace que le répertoire d'installation. - -**La sauvegarde passe par l'API de sauvegarde en ligne de SQLite.** La base est en mode WAL : -copier le seul fichier `.db` d'une base active peut ne rien sauvegarder du tout. Voir -`doc/ADMIN.md`. - -## Documentation - -- `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 +Le détail — ce que le script vérifie, ce qui reste manuel, et la marche à suivre côté serveur — +est dans le README de `mabibli`. diff --git a/build/publier.sh b/build/publier.sh new file mode 100755 index 0000000..4404e1d --- /dev/null +++ b/build/publier.sh @@ -0,0 +1,223 @@ +#!/bin/bash +# +# Publie une version de MaBibli de bout en bout, en une commande. +# +# Enchaîne ce qui était jusqu'ici tapé à la main, toujours dans le même ordre : +# +# 1. produire l'archive et mettre à jour manifest.toml (publier-release.sh) +# 2. committer et pousser mabibli_ynh (le serveur lit Gitea, pas le local) +# 3. taguer et pousser mabibli (le tag porte le code de la release) +# +# La version se choisit ici, à partir de celle déjà publiée : rien à aller chercher. +# La seule chose qui reste manuelle est le dépôt de l'archive dans la release Gitea — +# l'URL est rappelée en fin de sortie. +# +set -euo pipefail + +racine_paquet="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +manifeste="$racine_paquet/manifest.toml" + +source_dir="${MABIBLI_SOURCE_DIR:-$(cd "$racine_paquet/../mabibli" 2>/dev/null && pwd || true)}" +base_url="${MABIBLI_BASE_URL:-https://git.akbar.nohost.me/mathieu/mabibli/releases/download}" +url_releases="${MABIBLI_URL_RELEASES:-https://git.akbar.nohost.me/mathieu/mabibli/releases}" +version="" +increment="" +sans_confirmation=0 + +usage() { + cat <<'FIN' +Usage : publier.sh [options] + + --version X.Y.Z Version à publier (sinon : demandée, à partir de la précédente) + --patch Incrémenter le dernier chiffre (0.3.0 -> 0.3.1) + --minor Incrémenter le chiffre du milieu (0.3.0 -> 0.4.0) + --major Incrémenter le premier chiffre (0.3.0 -> 1.0.0) + --oui Ne rien demander (pour une CI) + -h, --help Cette aide +FIN +} + +while [ $# -gt 0 ]; do + case "$1" in + --version) version="$2"; shift 2 ;; + --patch|--minor|--major) increment="${1#--}"; shift ;; + --oui|-y) sans_confirmation=1; shift ;; + -h|--help) usage; exit 0 ;; + *) echo "Option inconnue : $1" >&2; usage >&2; exit 2 ;; + esac +done + +echoerr() { echo "$@" >&2; } + +if [ -z "$source_dir" ] || [ ! -d "$source_dir/.git" ]; then + echoerr "Dépôt du code introuvable : '${source_dir:-}' (MABIBLI_SOURCE_DIR)." + exit 1 +fi + +#================================================= +# LA VERSION PRÉCÉDENTE, DEUX SOURCES QUI DOIVENT S'ACCORDER +#================================================= + +# Le tag fait foi : c'est lui qui désigne le code publié. Le manifeste ne porte que la +# dernière version *empaquetée*, qui peut être en retard si une publication a échoué en +# cours de route. On prend le plus élevé des deux pour ne jamais proposer un numéro déjà pris. +version_tag="$(git -C "$source_dir" tag --list 'v[0-9]*' --sort=-v:refname | head -1 | sed 's/^v//')" +version_manifeste="$(sed -n 's/^version *= *"\([^"~]*\).*"/\1/p' "$manifeste" | head -1)" + +precedente="$(printf '%s\n%s\n' "$version_tag" "$version_manifeste" \ + | grep -E '^[0-9]+(\.[0-9]+)*$' | sort -V | tail -1)" + +if [ -z "$precedente" ]; then + echoerr "Aucune version précédente trouvée (ni tag, ni manifest.toml) ; utilisez --version." + exit 1 +fi + +echo "Dernière version publiée : $precedente" +[ "$version_tag" != "$version_manifeste" ] && + echo " ⚠ tag v${version_tag:-—} et manifest.toml ${version_manifeste:-—} divergent" + +suivante() { # suivante + IFS=. read -r maj min cor <<<"$1" + case "$2" in + major) echo "$((maj + 1)).0.0" ;; + minor) echo "$maj.$((min + 1)).0" ;; + patch) echo "$maj.$min.$((cor + 1))" ;; + esac +} + +if [ -z "$version" ] && [ -n "$increment" ]; then + version="$(suivante "$precedente" "$increment")" +fi + +if [ -z "$version" ]; then + if [ "$sans_confirmation" -eq 1 ] || [ ! -t 0 ]; then + echoerr "Pas de terminal pour demander la version ; utilisez --version ou --patch/--minor/--major." + exit 1 + fi + echo + echo " 1) $(suivante "$precedente" patch) (correction)" + echo " 2) $(suivante "$precedente" minor) (nouveautés)" + echo " 3) $(suivante "$precedente" major) (rupture)" + echo " 4) autre — à saisir" + echo + read -rp "Version à publier [1] ? " choix + case "${choix:-1}" in + 1) version="$(suivante "$precedente" patch)" ;; + 2) version="$(suivante "$precedente" minor)" ;; + 3) version="$(suivante "$precedente" major)" ;; + 4) read -rp "Numéro de version : " version ;; + *) version="$choix" ;; # un numéro tapé directement passe aussi + esac +fi + +if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echoerr "Version invalide : '$version' (attendu X.Y.Z)." + exit 1 +fi + +if git -C "$source_dir" rev-parse "v$version" >/dev/null 2>&1; then + echoerr "Le tag v$version existe déjà dans $source_dir." + exit 1 +fi + +#================================================= +# CE QUI DOIT ÊTRE VRAI AVANT DE COMPILER +#================================================= + +# Le tag va désigner le HEAD du dépôt du code : un travail non committé ne serait donc +# pas dans la release, alors même que l'archive, elle, le contiendrait. +if [ -n "$(git -C "$source_dir" status --porcelain)" ]; then + echoerr "Le dépôt du code a des modifications non committées :" + git -C "$source_dir" status --short >&2 + exit 1 +fi + +branche="$(git -C "$source_dir" rev-parse --abbrev-ref HEAD)" + +echo +echo "=== À publier ===" +echo " version : $precedente -> $version" +echo " code : $source_dir ($branche, $(git -C "$source_dir" rev-parse --short HEAD))" +echo " paquet : $racine_paquet" + +if [ "$sans_confirmation" -ne 1 ]; then + read -rp "Continuer ? [O/n] " reponse + case "${reponse:-o}" in + [oO]*) ;; + *) echo "Abandon."; exit 1 ;; + esac +fi + +#================================================= +# 1. ARCHIVE + MANIFESTE +#================================================= + +"$racine_paquet/build/publier-release.sh" --version "$version" --source-dir "$source_dir" --base-url "$base_url" + +archive_nom="mabibli-$version-linux-x64.tar.gz" +archive="${MABIBLI_OUTPUT_DIR:-$racine_paquet/build/dist}/$archive_nom" + +#================================================= +# 2. LE PAQUET (le serveur lit Gitea, jamais la copie locale) +#================================================= + +echo +echo "=== Paquet : commit et push ===" +git -C "$racine_paquet" add -A +if git -C "$racine_paquet" diff --cached --quiet; then + echo " rien à committer (manifeste déjà à jour)" +else + git -C "$racine_paquet" commit -m "Publier la version $version" +fi +git -C "$racine_paquet" push + +#================================================= +# 3. LE CODE : tag et push +#================================================= + +echo +echo "=== Code : tag v$version et push ===" +git -C "$source_dir" tag "v$version" +git -C "$source_dir" push origin "$branche" --tags + +# Un push qui échoue à demi ne se voit pas dans la sortie de git : on redemande au distant. +verifier_pousse() { # verifier_pousse + if git -C "$1" ls-remote --exit-code origin "$2" >/dev/null 2>&1; then + echo " ✓ $2 présent sur origin" + else + echoerr " ✗ $2 ABSENT sur origin — le push n'a pas abouti." + exit 1 + fi +} + +echo +echo "=== Vérification côté distant ===" +verifier_pousse "$source_dir" "refs/tags/v$version" +verifier_pousse "$racine_paquet" "refs/heads/$(git -C "$racine_paquet" rev-parse --abbrev-ref HEAD)" + +#================================================= +# CE QUI RESTE À FAIRE À LA MAIN +#================================================= + +url_archive="$base_url/v$version/$archive_nom" + +cat <