Installe l'archive self-contained publiée en release du dépôt du code, amd64 ou arm64 selon la machine. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
581 lines
23 KiB
Bash
Executable File
581 lines
23 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Publie une version de MaBibli de bout en bout, en une commande.
|
|
#
|
|
# 1. choisir la version, à partir de celle déjà publiée
|
|
# 2. compiler self-contained POUR CHAQUE ARCHITECTURE, vérifier chaque publish,
|
|
# produire une archive par architecture
|
|
# 3. réécrire manifest.toml (version, url et sha256 de chaque architecture)
|
|
# 4. committer et pousser mabibli_ynh (le serveur lit Gitea, jamais le local)
|
|
# 5. taguer et pousser mabibli (le tag porte le code de la release)
|
|
#
|
|
# La seule chose qui reste manuelle est le dépôt des archives dans la release Gitea :
|
|
# elles sont rappelées en fin de sortie, une par architecture.
|
|
#
|
|
# Le mode `--archive-seule` compile et archive sans toucher ni
|
|
# au manifeste ni à git
|
|
#
|
|
set -euo pipefail
|
|
|
|
#=================================================
|
|
# VALEURS PAR DÉFAUT
|
|
#=================================================
|
|
|
|
racine_paquet="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
manifeste="$racine_paquet/manifest.toml"
|
|
|
|
# ⚠️ Chaque réglage a UNE seule façon de se donner : son option en ligne de commande.
|
|
# ⚠️ LA SEULE LIGNE À CHANGER pour héberger ce projet ailleurs.
|
|
# Tout ce qui suit en dérive : URL de release, URL d'archive, message de fin.
|
|
#
|
|
# Une copie publique du projet porte ici `https://forge.example.org/mabibli`, injoignable
|
|
# par construction (example.org est réservé à la documentation par la RFC 2606). Le
|
|
# contrôle préalable plus bas REFUSE alors de publier, et refuse AVANT de compiler :
|
|
# celui qui reprend le projet doit renseigner cette ligne avant sa première release,
|
|
# plutôt que de découvrir le problème à l'installation, dans ynh_setup_source.
|
|
depot_code="https://git.akbar.nohost.me/mathieu/mabibli"
|
|
|
|
source_dir="$(cd "$racine_paquet/../mabibli" 2>/dev/null && pwd || true)"
|
|
base_url="$depot_code/releases/download"
|
|
url_releases="$depot_code/releases"
|
|
output_dir="$racine_paquet/build/dist"
|
|
|
|
version=""
|
|
increment=""
|
|
sans_confirmation=0
|
|
archive_seule=0
|
|
projet="MaBibli.Api"
|
|
|
|
# ⚠️ Les DEUX architectures se compilent à chaque publication, et il n'y a pas d'option
|
|
# pour n'en faire qu'une. Le manifeste déclare `architectures = ["amd64", "arm64"]` : un
|
|
# serveur arm64 ira chercher `arm64.url`. Publier une seule archive laisserait le
|
|
# manifeste promettre une URL que personne n'a téléversée — et l'échec surviendrait chez
|
|
# celui qui installe, dans `ynh_setup_source`, sans rapport apparent avec la cause.
|
|
#
|
|
# ⚠️ Le RID désigne le SERVEUR, jamais la machine qui compile : le SDK télécharge le
|
|
# runtime pack de la cible, et un hôte arm64 produit un binaire x86-64 sans rien de plus.
|
|
runtimes=("linux-x64" "linux-arm64")
|
|
|
|
# Nom YunoHost de chaque RID .NET — c'est la clé du manifeste (`amd64.url`, `arm64.url`).
|
|
arch_ynh() {
|
|
case "$1" in
|
|
linux-x64) echo "amd64" ;;
|
|
linux-arm64) echo "arm64" ;;
|
|
*) echoerr "RID sans architecture YunoHost connue : $1"; exit 1 ;;
|
|
esac
|
|
}
|
|
|
|
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 (1.0.0 -> 1.0.1)
|
|
--minor Incrémenter le chiffre du milieu (1.0.0 -> 1.1.0)
|
|
--major Incrémenter le premier chiffre (1.0.0 -> 2.0.0)
|
|
--oui, -y Ne rien demander (pour une CI)
|
|
--archive-seule Compiler et archiver SEULEMENT : ni manifeste, ni commit, ni tag.
|
|
Pour REPRENDRE une publication à la main après un échec en cours
|
|
de route ; ne publie rien et ne modifie aucun dépôt. Les contrôles
|
|
restent les mêmes, mais avertissent au lieu de refuser.
|
|
--source-dir CHEMIN Dépôt du code C# (défaut : ../mabibli)
|
|
--depot-code URL Dépôt public du code, d'où dérivent les URL de release
|
|
--base-url URL Racine des URL de release, sans le tag ni le fichier
|
|
--output-dir CHEMIN Où déposer l'archive (défaut : build/dist)
|
|
-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 ;;
|
|
--archive-seule) archive_seule=1; shift ;;
|
|
--source-dir) source_dir="$2"; shift 2 ;;
|
|
--depot-code) depot_code="$2"; base_url="$2/releases/download"; url_releases="$2/releases"; shift 2 ;;
|
|
--base-url) base_url="$2"; shift 2 ;;
|
|
--output-dir) output_dir="$2"; shift 2 ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) echo "Option inconnue : $1" >&2; usage >&2; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
echoerr() { echo "$@" >&2; }
|
|
|
|
# ⚠️ LE SEUL ENDROIT où `--archive-seule` assouplit quoi que ce soit.
|
|
#
|
|
# Les garde-fous ci-dessous protègent la *publication* : ils empêchent qu'une release
|
|
# contienne autre chose que le code que son tag désigne. En `--archive-seule`, rien n'est
|
|
# publié — ni manifeste, ni commit, ni tag — donc rien ne peut mentir, et un refus n'a
|
|
# plus lieu d'être. Il reste un avertissement, car ce qu'il signale est toujours vrai.
|
|
#
|
|
# Chaque garde-fou s'écrit donc UNE fois, sans savoir dans quel mode il tourne. C'est ce
|
|
# qui empêche d'assouplir par inadvertance un contrôle qui devait rester dur.
|
|
# ⚠️ Le CONSTAT et le CONSEIL sont séparés, et ce n'est pas de la cosmétique : le constat
|
|
# reste vrai dans les deux modes, le conseil non. « Choisissez un numéro libre » est le
|
|
# bon conseil pour une publication, et un mauvais conseil en reprise — où le tag visé est
|
|
# justement celui qu'on veut retrouver.
|
|
refuser() { # refuser <conseil> <constat> [<constat>...]
|
|
local conseil="$1"; shift
|
|
if [ "$archive_seule" -eq 1 ]; then
|
|
printf ' ⚠ %s\n' "$@"
|
|
echo " (poursuite : --archive-seule ne publie rien)"
|
|
else
|
|
printf '%s\n' "$@" >&2
|
|
[ -n "$conseil" ] && echoerr "$conseil"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
#=================================================
|
|
# CONTRÔLES PRÉALABLES
|
|
#=================================================
|
|
|
|
if [ -z "$source_dir" ] || [ ! -d "$source_dir" ]; then
|
|
echoerr "Dépôt du code introuvable : '${source_dir:-<vide>}'"
|
|
echoerr "Précisez-le avec --source-dir."
|
|
exit 1
|
|
fi
|
|
source_dir="$(cd "$source_dir" && pwd)"
|
|
|
|
if [ ! -d "$source_dir/$projet" ]; then
|
|
echoerr "'$source_dir' ne contient pas de projet $projet."
|
|
exit 1
|
|
fi
|
|
|
|
# ⚠️ Exigé dans les DEUX modes. `--archive-seule` sert à reprendre une publication à la
|
|
# main, jamais à compiler un arbre quelconque : sans dépôt, il ne saurait ni proposer une
|
|
# version, ni dire si l'archive correspond au code d'un tag. L'exiger ici plutôt qu'à
|
|
# quatre endroits plus bas est ce qui rend le reste du script inconditionnel.
|
|
if [ ! -d "$source_dir/.git" ]; then
|
|
echoerr "'$source_dir' n'est pas un dépôt git."
|
|
echoerr "publier.sh compile ce qu'un commit désigne : sans dépôt, rien ne le désigne."
|
|
exit 1
|
|
fi
|
|
|
|
# ⚠️ Refuser AVANT de compiler, comme les autres garde-fous. Publier avec le placeholder
|
|
# écrirait dans manifest.toml une `amd64.url` que personne ne peut télécharger — et
|
|
# `ynh_setup_source` échouerait à l'installation, loin d'ici et sans rapport apparent.
|
|
if [ "$archive_seule" -eq 0 ] && [[ "$base_url" == *example.org* ]]; then
|
|
echoerr "Le dépôt public n'est pas configuré : $depot_code"
|
|
echoerr "Changez « depot_code » en tête de ce script, ou passez --depot-code URL."
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v dotnet >/dev/null; then
|
|
echoerr "Le SDK .NET est introuvable. Il n'est nécessaire QUE sur cette machine :"
|
|
echoerr "le serveur YunoHost reçoit un publish self-contained et n'a pas de SDK."
|
|
exit 1
|
|
fi
|
|
|
|
#=================================================
|
|
# LA VERSION
|
|
#=================================================
|
|
|
|
# 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 || true)"
|
|
|
|
suivante() { # suivante <version> <patch|minor|major>
|
|
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
|
|
}
|
|
|
|
# ⚠️ Le calcul de la version est le MÊME dans les deux modes, et c'est délibéré : une
|
|
# reprise après échec veut la version qu'on était en train de publier, c'est-à-dire ce
|
|
# que l'incrément propose. Faire retomber `--archive-seule` sur la version du manifeste
|
|
# reproduirait le numéro DÉJÀ publié tant qu'on ne passerait pas `--version`.
|
|
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"
|
|
|
|
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
|
|
|
|
#=================================================
|
|
# CE QUI DOIT ÊTRE VRAI AVANT DE COMPILER
|
|
#=================================================
|
|
|
|
# ⚠️ Recompiler une version dont le tag existe
|
|
# déjà produit une archive qui ne correspond PAS au code que ce tag désigne : la release
|
|
# se met alors à mentir, et l'écran « À propos » avec elle, sans que rien ne le signale.
|
|
if git -C "$source_dir" rev-parse "v$version" >/dev/null 2>&1; then
|
|
commit_tag="$(git -C "$source_dir" rev-list -n1 "v$version")"
|
|
commit_head="$(git -C "$source_dir" rev-parse HEAD)"
|
|
if [ "$commit_tag" != "$commit_head" ]; then
|
|
refuser "Choisissez un numéro libre (--patch)." \
|
|
"Le tag v$version existe et désigne ${commit_tag:0:7} ; HEAD est ${commit_head:0:7}." \
|
|
"L'archive ne serait donc PAS le code que ce tag désigne."
|
|
else
|
|
refuser "Choisissez un numéro libre (--patch)." \
|
|
"Le tag v$version existe déjà (${commit_tag:0:7}) : ce numéro est pris."
|
|
fi
|
|
fi
|
|
|
|
# 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
|
|
git -C "$source_dir" status --short >&2
|
|
refuser "" \
|
|
"Le dépôt du code a des modifications non committées (ci-dessus)." \
|
|
"Le tag désignera HEAD : ce travail ne serait pas dans la release."
|
|
fi
|
|
|
|
echo
|
|
echo "=== À publier ==="
|
|
echo " version : ${precedente:-—} -> $version"
|
|
branche="$(git -C "$source_dir" rev-parse --abbrev-ref HEAD)"
|
|
echo " code : $source_dir ($branche, $(git -C "$source_dir" rev-parse --short HEAD))"
|
|
echo " paquet : $racine_paquet"
|
|
[ "$archive_seule" -eq 1 ] &&
|
|
echo " mode : --archive-seule (ni manifeste, ni commit, ni tag)"
|
|
|
|
if [ "$sans_confirmation" -ne 1 ]; then
|
|
read -rp "Continuer ? [O/n] " reponse
|
|
case "${reponse:-o}" in
|
|
[oO]*) ;;
|
|
*) echo "Abandon."; exit 1 ;;
|
|
esac
|
|
fi
|
|
|
|
#=================================================
|
|
# COMPILATION
|
|
#=================================================
|
|
|
|
# Horodatage du build, injecté dans l'assembly à côté de la version.
|
|
#
|
|
# ⚠️ Il sert de TÉMOIN : l'application ne montre sa version que si cet horodatage est présent.
|
|
# Sans lui, le numéro lu serait le « 1.0.0 » que le SDK pose par défaut, qui se lirait comme une
|
|
# vraie version alors qu'il ne désigne rien — et c'est précisément la valeur qu'on va chercher
|
|
# pour diagnostiquer un appareil dépareillé. Un binaire compilé à la main se déclare donc
|
|
# « version de développement », ce qui est vrai.
|
|
#
|
|
# `SOURCE_DATE_EPOCH` est respecté, comme pour l'archive : sans cela, deux compilations du même
|
|
# commit donneraient deux binaires — donc deux sha256 — pour la seule raison que l'heure a
|
|
# changé, et la reproductibilité visée plus bas ne vaudrait plus rien.
|
|
if [ -n "${SOURCE_DATE_EPOCH:-}" ]; then
|
|
date_build="$(date -u -d "@$SOURCE_DATE_EPOCH" +%Y-%m-%dT%H:%M:%SZ)"
|
|
else
|
|
date_build="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
fi
|
|
|
|
travail="$(mktemp -d)"
|
|
# shellcheck disable=SC2064
|
|
trap "rm -rf '$travail'" EXIT
|
|
|
|
# Une passe complète par architecture : compilation, contrôles, archive. Les trois
|
|
# blocs sont RIGOUREUSEMENT les mêmes d'une architecture à l'autre — c'est ce qui
|
|
# garantit qu'une archive arm64 est vérifiée aussi durement qu'une amd64.
|
|
declare -A sommes=()
|
|
declare -A urls=()
|
|
declare -A archives=()
|
|
|
|
for runtime in "${runtimes[@]}"; do
|
|
|
|
arch="$(arch_ynh "$runtime")"
|
|
publish="$travail/publish-$runtime"
|
|
|
|
echo
|
|
echo "=== Compilation self-contained $runtime ($arch) ==="
|
|
echo " source : $source_dir"
|
|
echo " version: $version"
|
|
echo " build : $date_build"
|
|
|
|
# `--self-contained` embarque le runtime .NET dans le dossier publié : c'est ce qui
|
|
# permet au paquet YunoHost de ne dépendre d'aucun `dotnet-runtime` côté serveur.
|
|
#
|
|
# `MaBibli.Api` référence `MaBibli.Client` : le client Blazor WebAssembly est compilé
|
|
# au passage et atterrit dans `wwwroot/`. Un seul publish suffit donc pour les trois
|
|
# projets.
|
|
#
|
|
# ⚠️ Le client compilé est le MÊME quelle que soit l'architecture — c'est du WebAssembly,
|
|
# zbar compris. Seul l'hôte .NET change. On le recompile quand même par architecture
|
|
# plutôt que de recopier un `wwwroot/` d'une passe à l'autre : le publish est le seul
|
|
# geste dont on sait qu'il produit un ensemble cohérent, et les contrôles ci-dessous
|
|
# portent sur ce qu'on archive vraiment, pas sur ce qu'on croit y avoir mis.
|
|
dotnet publish "$source_dir/$projet" \
|
|
--configuration Release \
|
|
--runtime "$runtime" \
|
|
--self-contained \
|
|
--output "$publish" \
|
|
-p:Version="$version" \
|
|
-p:MaBibliDateBuild="$date_build"
|
|
|
|
#=================================================
|
|
# CONTRÔLES SUR LE RÉSULTAT
|
|
#=================================================
|
|
|
|
echo
|
|
echo "=== Vérification du publish ($arch) ==="
|
|
|
|
if [ ! -x "$publish/$projet" ]; then
|
|
echoerr "Binaire '$projet' absent ou non exécutable dans le publish $runtime."
|
|
exit 1
|
|
fi
|
|
|
|
# Sans `wwwroot`, le service démarrerait et répondrait — en ne servant aucune page.
|
|
# C'est le défaut qui est passé inaperçu pendant trois phases du projet, faute de
|
|
# regarder ailleurs que la racine. On le vérifie donc ici, à la source.
|
|
if [ ! -f "$publish/wwwroot/index.html" ]; then
|
|
echoerr "wwwroot/index.html absent : le client Blazor n'a pas été embarqué ($runtime)."
|
|
exit 1
|
|
fi
|
|
|
|
# `index.html` doit référencer un script réellement présent. Un placeholder non
|
|
# substitué (`blazor.webassembly#[.{fingerprint}].js`) donnerait une page blanche
|
|
# alors que `/` répondrait 200.
|
|
if grep -q '#\[\.{fingerprint}\]' "$publish/wwwroot/index.html"; then
|
|
echoerr "index.html contient des placeholders d'empreinte non substitués ($runtime)."
|
|
echoerr "Voir CLAUDE.md, « Les invariants à ne jamais casser »."
|
|
exit 1
|
|
fi
|
|
|
|
for ressource in $(grep -oE '(src|href)="[^"]+"' "$publish/wwwroot/index.html" \
|
|
| sed -E 's/^(src|href)="//; s/"$//' \
|
|
| grep -vE '^(https?:|data:|#|/$|\.$)'); do
|
|
if [ ! -f "$publish/wwwroot/$ressource" ]; then
|
|
echoerr "index.html référence '$ressource', absent du publish ($runtime)."
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# ⚠️ Le contrôle qui n'existait pas tant qu'il n'y avait qu'une architecture : rien
|
|
# n'empêche un publish de rendre un binaire de l'architecture de la MACHINE plutôt que
|
|
# de la cible. Le symptôme serait un service qui refuse de démarrer sur le serveur, avec
|
|
# un « Exec format error » qui ne désigne pas la publication.
|
|
attendu_elf="$(case "$runtime" in linux-x64) echo "x86-64" ;; linux-arm64) echo "aarch64" ;; esac)"
|
|
if ! file -b "$publish/$projet" | grep -q "$attendu_elf"; then
|
|
echoerr "Le binaire produit n'est pas du $attendu_elf :"
|
|
echoerr " $(file -b "$publish/$projet")"
|
|
exit 1
|
|
fi
|
|
|
|
echo " binaire ($attendu_elf), wwwroot et ressources d'index.html : OK"
|
|
|
|
#=================================================
|
|
# ARCHIVE
|
|
#=================================================
|
|
|
|
archive_nom="mabibli-$version-$runtime.tar.gz"
|
|
mkdir -p "$output_dir"
|
|
archive="$output_dir/$archive_nom"
|
|
|
|
echo
|
|
echo "=== Archive ($arch) ==="
|
|
|
|
# Fichiers à la racine de l'archive, sans dossier intermédiaire : cohérent avec
|
|
# `in_subdir = false` dans manifest.toml.
|
|
#
|
|
# Options de reproductibilité : sans elles, deux compilations identiques donneraient
|
|
# deux sha256 différents (dates, ordre de parcours, uid/gid). En CI comme en local,
|
|
# on veut pouvoir vérifier qu'une archive correspond bien à un commit.
|
|
tar --create --gzip \
|
|
--file="$archive" \
|
|
--directory="$publish" \
|
|
--owner=0 --group=0 --numeric-owner \
|
|
--mtime="@${SOURCE_DATE_EPOCH:-0}" \
|
|
--sort=name \
|
|
.
|
|
|
|
somme="$(sha256sum "$archive" | cut -d' ' -f1)"
|
|
taille="$(du -h "$archive" | cut -f1)"
|
|
taille_octets="$(stat -c%s "$archive")"
|
|
|
|
echo " fichier : $archive"
|
|
echo " taille : $taille ($taille_octets octets)"
|
|
echo " sha256 : $somme"
|
|
|
|
sommes[$arch]="$somme"
|
|
archives[$arch]="$archive"
|
|
urls[$arch]="$base_url/v$version/$archive_nom"
|
|
|
|
# Le publish pèse ~150 Mo : le garder pendant la passe suivante doublerait l'occupation
|
|
# du disque pour rien, l'archive étant déjà produite.
|
|
rm -rf "$publish"
|
|
|
|
done
|
|
|
|
if [ "$archive_seule" -eq 1 ]; then
|
|
echo
|
|
echo "=== Terminé (--archive-seule) ==="
|
|
echo
|
|
for runtime in "${runtimes[@]}"; do
|
|
arch="$(arch_ynh "$runtime")"
|
|
echo " $arch : ${archives[$arch]}"
|
|
echo " sha256 ${sommes[$arch]}"
|
|
done
|
|
cat <<FIN
|
|
|
|
Rien n'a été publié : manifest.toml est inchangé, aucun commit, aucun tag.
|
|
Pour publier réellement cette version, relancez sans --archive-seule.
|
|
|
|
FIN
|
|
exit 0
|
|
fi
|
|
|
|
#=================================================
|
|
# MANIFESTE
|
|
#=================================================
|
|
|
|
echo
|
|
echo "=== Mise à jour de manifest.toml ==="
|
|
|
|
# Le suffixe ~ynhN est conservé s'il existe déjà pour cette version, sinon
|
|
# remis à ~ynh1 : un changement de version applicative repart toujours de 1.
|
|
version_actuelle="$(sed -n 's/^version *= *"\([^"]*\)".*/\1/p' "$manifeste" | head -1)"
|
|
if [[ "$version_actuelle" == "$version~ynh"* ]]; then
|
|
version_paquet="$version_actuelle"
|
|
echo " version : $version_paquet (inchangée)"
|
|
else
|
|
version_paquet="$version~ynh1"
|
|
echo " version : $version_actuelle -> $version_paquet"
|
|
fi
|
|
|
|
# Les architectures sont passées à plat : arch, url, sha256, arch, url, sha256…
|
|
arguments=()
|
|
for runtime in "${runtimes[@]}"; do
|
|
arch="$(arch_ynh "$runtime")"
|
|
arguments+=("$arch" "${urls[$arch]}" "${sommes[$arch]}")
|
|
done
|
|
|
|
python3 - "$manifeste" "$version_paquet" "${arguments[@]}" <<'PY'
|
|
import re, sys
|
|
|
|
chemin, version = sys.argv[1:3]
|
|
reste = sys.argv[3:]
|
|
with open(chemin, encoding="utf-8") as f:
|
|
texte = f.read()
|
|
|
|
remplacements = [(r'^version = ".*"$', f'version = "{version}"')]
|
|
|
|
# ⚠️ Chaque motif DOIT correspondre exactement une fois. Une architecture déclarée au
|
|
# manifeste mais absente d'ici laisserait une URL périmée pointer sur la version
|
|
# précédente : le serveur téléchargerait une archive réelle, dont le sha256 ne
|
|
# correspondrait pas — ou pire, correspondrait, et installerait l'ancienne version.
|
|
for arch, url, somme in zip(reste[0::3], reste[1::3], reste[2::3]):
|
|
remplacements += [
|
|
(rf'^ {arch}\.url = ".*"$', f' {arch}.url = "{url}"'),
|
|
(rf'^ {arch}\.sha256 = ".*"$', f' {arch}.sha256 = "{somme}"'),
|
|
]
|
|
|
|
for motif, valeur in remplacements:
|
|
texte, n = re.subn(motif, valeur, texte, count=1, flags=re.MULTILINE)
|
|
if n != 1:
|
|
sys.exit(f"Motif introuvable dans le manifeste : {motif}")
|
|
|
|
with open(chemin, "w", encoding="utf-8") as f:
|
|
f.write(texte)
|
|
PY
|
|
|
|
for runtime in "${runtimes[@]}"; do
|
|
arch="$(arch_ynh "$runtime")"
|
|
echo " $arch : ${urls[$arch]}"
|
|
echo " sha256 ${sommes[$arch]}"
|
|
done
|
|
|
|
#=================================================
|
|
# 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
|
|
|
|
#=================================================
|
|
# 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 <dépôt> <ref>
|
|
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
|
|
#=================================================
|
|
|
|
cat <<FIN
|
|
|
|
=== Publié : $version ===
|
|
|
|
Il ne reste qu'une chose, dans Gitea : créer la release v$version et y téléverser
|
|
LES DEUX archives. Une seule des deux téléversée, et le manifeste promet à l'autre
|
|
architecture une URL qui répondra 404 à l'installation.
|
|
|
|
Releases : $url_releases
|
|
$(for r in "${runtimes[@]}"; do a="$(arch_ynh "$r")"; echo " $a : ${archives[$a]}"; done)
|
|
|
|
Ensuite, vérifier que le manifeste pointe sur des fichiers réellement téléchargeables
|
|
(sans être authentifié) :
|
|
|
|
$(for r in "${runtimes[@]}"; do a="$(arch_ynh "$r")"; echo " curl -fsSLI \"${urls[$a]}\" | head -1"; done)
|
|
|
|
Puis, sur le serveur :
|
|
|
|
sudo yunohost app upgrade mabibli -u ${depot_code}_ynh
|
|
|
|
FIN
|