diff --git a/build/publier-release.sh b/build/publier-release.sh deleted file mode 100755 index 477d6e0..0000000 --- a/build/publier-release.sh +++ /dev/null @@ -1,291 +0,0 @@ -#!/bin/bash -# -# Produit l'archive de release de MaBibli et met à jour manifest.toml. -# -# Pourquoi ce script vit dans `mabibli_ynh` et non dans `mabibli` : -# sa sortie utile n'est pas l'archive, c'est le couple (url, sha256) que le -# manifeste doit porter. Le garder ici permet de compiler, d'archiver et de mettre à -# jour le manifeste en une commande, puis de committer le tout d'un bloc — le -# manifeste ne peut pas se désynchroniser de l'archive qu'il décrit. -# -# Réutilisable en CI (Gitea Actions) sans modification : aucune interaction, aucune -# variable propre à un environnement d'intégration, tout passe par des options ou des -# variables d'environnement, et le script échoue au premier problème. -# -# Exemple d'usage en CI : -# ./build/publier-release.sh \ -# --source-dir "$GITHUB_WORKSPACE/mabibli" \ -# --version 0.2.0 \ -# --base-url "https://git.akbar.nohost.me/mathieu/mabibli/releases/download" -# -set -euo pipefail - -#================================================= -# VALEURS PAR DÉFAUT -#================================================= - -racine_paquet="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" - -source_dir="${MABIBLI_SOURCE_DIR:-$(cd "$racine_paquet/../mabibli" 2>/dev/null && pwd || true)}" -version="${MABIBLI_VERSION:-}" -base_url="${MABIBLI_BASE_URL:-https://git.akbar.nohost.me/mathieu/mabibli/releases/download}" -output_dir="${MABIBLI_OUTPUT_DIR:-$racine_paquet/build/dist}" -maj_manifeste=1 -runtime="linux-x64" -projet="MaBibli.Api" - -#================================================= -# OPTIONS -#================================================= - -usage() { - cat <<'FIN' -Usage : publier-release.sh [options] - - --source-dir CHEMIN Dépôt du code C# (défaut : ../mabibli) - --version X.Y.Z Version de la release (défaut : déduite de manifest.toml) - --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) - --no-manifest-update Ne pas réécrire manifest.toml - -h, --help Cette aide - -Variables d'environnement équivalentes : MABIBLI_SOURCE_DIR, MABIBLI_VERSION, -MABIBLI_BASE_URL, MABIBLI_OUTPUT_DIR. -FIN -} - -while [ $# -gt 0 ]; do - case "$1" in - --source-dir) source_dir="$2"; shift 2 ;; - --version) version="$2"; shift 2 ;; - --base-url) base_url="$2"; shift 2 ;; - --output-dir) output_dir="$2"; shift 2 ;; - --no-manifest-update) maj_manifeste=0; shift ;; - -h|--help) usage; exit 0 ;; - *) echo "Option inconnue : $1" >&2; usage >&2; exit 2 ;; - esac -done - -manifeste="$racine_paquet/manifest.toml" - -#================================================= -# CONTRÔLES PRÉALABLES -#================================================= - -echoerr() { echo "$@" >&2; } - -if [ -z "$source_dir" ] || [ ! -d "$source_dir" ]; then - echoerr "Dépôt du code introuvable : '${source_dir:-}'" - 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 - -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 par défaut se lit dans le manifeste, en retirant le suffixe ~ynhN : -# le tag et le nom de fichier portent la version applicative, pas celle du paquet. -if [ -z "$version" ]; then - version="$(sed -n 's/^version *= *"\([^"~]*\).*"/\1/p' "$manifeste" | head -1)" - if [ -z "$version" ]; then - echoerr "Impossible de déduire la version depuis $manifeste ; utilisez --version." - exit 1 - fi - echo "Version déduite de manifest.toml : $version" -fi - -#================================================= -# COMPILATION -#================================================= - -# Horodatage du build, injecté dans l'assembly à côté de la version (voir plus bas). -# -# ⚠️ 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 - -publish="$travail/publish" - -echo -echo "=== Compilation self-contained $runtime ===" -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. -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 ===" - -if [ ! -x "$publish/$projet" ]; then - echoerr "Binaire '$projet' absent ou non exécutable dans le publish." - 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é." - 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." - echoerr "Voir CLAUDE.md, « Empreintes WASM désactivées »." - 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." - exit 1 - fi -done - -echo " binaire, 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 ===" - -# 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" - -#================================================= -# MISE À JOUR DU MANIFESTE -#================================================= - -url="$base_url/v$version/$archive_nom" - -if [ "$maj_manifeste" -eq 1 ]; then - 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 - - python3 - "$manifeste" "$version_paquet" "$url" "$somme" <<'PY' -import re, sys - -chemin, version, url, somme = sys.argv[1:5] -with open(chemin, encoding="utf-8") as f: - texte = f.read() - -remplacements = [ - (r'^version = ".*"$', f'version = "{version}"'), - (r'^ amd64\.url = ".*"$', f' amd64.url = "{url}"'), - (r'^ amd64\.sha256 = ".*"$', f' amd64.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 - - echo " url : $url" - echo " sha256 : $somme" -fi - -#================================================= -# SUITE À DONNER -#================================================= - -cat </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="" +output_dir="${MABIBLI_OUTPUT_DIR:-$racine_paquet/build/dist}" + +version="${MABIBLI_VERSION:-}" increment="" sans_confirmation=0 +archive_seule=0 +runtime="linux-x64" +projet="MaBibli.Api" usage() { cat <<'FIN' @@ -32,8 +56,16 @@ Usage : publier.sh [options] --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) + --oui, -y Ne rien demander (pour une CI) + --archive-seule Compiler et archiver SEULEMENT : ni manifeste, ni commit, ni tag. + Pour une CI ou un essai ; ne publie rien et ne modifie aucun dépôt. + --source-dir CHEMIN Dépôt du code C# (défaut : ../mabibli) + --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 + +Variables d'environnement équivalentes : MABIBLI_SOURCE_DIR, MABIBLI_VERSION, +MABIBLI_BASE_URL, MABIBLI_OUTPUT_DIR, MABIBLI_URL_RELEASES. FIN } @@ -42,6 +74,10 @@ while [ $# -gt 0 ]; do --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 ;; + --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 @@ -49,32 +85,48 @@ 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)." +#================================================= +# CONTRÔLES PRÉALABLES +#================================================= + +if [ -z "$source_dir" ] || [ ! -d "$source_dir" ]; then + echoerr "Dépôt du code introuvable : '${source_dir:-}'" + echoerr "Précisez-le avec --source-dir (ou MABIBLI_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 + +if [ "$archive_seule" -eq 0 ] && [ ! -d "$source_dir/.git" ]; then + echoerr "'$source_dir' n'est pas un dépôt git : impossible de taguer la release." + echoerr "Pour compiler sans rien publier : --archive-seule." + 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 PRÉCÉDENTE, DEUX SOURCES QUI DOIVENT S'ACCORDER +# 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_tag="" +[ -d "$source_dir/.git" ] && + 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" + | grep -E '^[0-9]+(\.[0-9]+)*$' | sort -V | tail -1 || true)" suivante() { # suivante IFS=. read -r maj min cor <<<"$1" @@ -85,60 +137,97 @@ suivante() { # suivante 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." +if [ "$archive_seule" -eq 1 ]; then + # Rien n'est publié : la version ne sert qu'à nommer l'archive et à alimenter + # `-p:Version`. Celle du manifeste convient, et on ne propose aucun incrément. + [ -z "$version" ] && version="$version_manifeste" + if [ -z "$version" ]; then + echoerr "Impossible de déduire la version depuis $manifeste ; utilisez --version." + exit 1 + fi +else + if [ -z "$precedente" ]; then + echoerr "Aucune version précédente trouvée (ni tag, ni manifest.toml) ; utilisez --version." 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 + echo "Dernière version publiée : $precedente" + [ "$version_tag" != "$version_manifeste" ] && + echo " ⚠ tag v${version_tag:-—} et manifest.toml ${version_manifeste:-—} divergent" -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 + 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 fi #================================================= # CE QUI DOIT ÊTRE VRAI AVANT DE COMPILER #================================================= +# ⚠️ Le garde-fou qui manquait le 2026-08-21. 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 [ -d "$source_dir/.git" ] && 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 [ "$archive_seule" -eq 1 ]; then + [ "$commit_tag" != "$commit_head" ] && + echo " ⚠ v$version désigne ${commit_tag:0:7}, HEAD est ${commit_head:0:7} : l'archive ne sera PAS le code du tag." + else + echoerr "Le tag v$version existe déjà dans $source_dir (${commit_tag:0:7})." + [ "$commit_tag" != "$commit_head" ] && + echoerr "HEAD est ${commit_head:0:7} : republier sous ce numéro mettrait un autre code dans la release v$version." + echoerr "Choisissez un numéro libre (--patch), ou compilez sans publier (--archive-seule)." + exit 1 + 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 +if [ "$archive_seule" -eq 0 ] && [ -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 " version : ${precedente:-—} -> $version" +if [ -d "$source_dir/.git" ]; then + branche="$(git -C "$source_dir" rev-parse --abbrev-ref HEAD)" + echo " code : $source_dir ($branche, $(git -C "$source_dir" rev-parse --short HEAD))" +else + echo " code : $source_dir" +fi 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 @@ -149,16 +238,187 @@ if [ "$sans_confirmation" -ne 1 ]; then fi #================================================= -# 1. ARCHIVE + MANIFESTE +# COMPILATION #================================================= -"$racine_paquet/build/publier-release.sh" --version "$version" --source-dir "$source_dir" --base-url "$base_url" +# 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 -archive_nom="mabibli-$version-linux-x64.tar.gz" -archive="${MABIBLI_OUTPUT_DIR:-$racine_paquet/build/dist}/$archive_nom" +travail="$(mktemp -d)" +# shellcheck disable=SC2064 +trap "rm -rf '$travail'" EXIT + +publish="$travail/publish" + +echo +echo "=== Compilation self-contained $runtime ===" +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. +dotnet publish "$source_dir/$projet" \ + --configuration Release \ + --runtime "$runtime" \ + --self-contained \ + --output "$publish" \ + -p:Version="$version" \ + -p:MaBibliDateBuild="$date_build" #================================================= -# 2. LE PAQUET (le serveur lit Gitea, jamais la copie locale) +# CONTRÔLES SUR LE RÉSULTAT +#================================================= + +echo +echo "=== Vérification du publish ===" + +if [ ! -x "$publish/$projet" ]; then + echoerr "Binaire '$projet' absent ou non exécutable dans le publish." + 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é." + 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." + echoerr "Voir CLAUDE.md, « Empreintes WASM désactivées »." + 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." + exit 1 + fi +done + +echo " binaire, 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 ===" + +# 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" + +url_archive="$base_url/v$version/$archive_nom" + +if [ "$archive_seule" -eq 1 ]; then + cat < $version_paquet" +fi + +python3 - "$manifeste" "$version_paquet" "$url_archive" "$somme" <<'PY' +import re, sys + +chemin, version, url, somme = sys.argv[1:5] +with open(chemin, encoding="utf-8") as f: + texte = f.read() + +remplacements = [ + (r'^version = ".*"$', f'version = "{version}"'), + (r'^ amd64\.url = ".*"$', f' amd64.url = "{url}"'), + (r'^ amd64\.sha256 = ".*"$', f' amd64.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 + +echo " url : $url_archive" +echo " sha256 : $somme" + +#================================================= +# LE PAQUET (le serveur lit Gitea, jamais la copie locale) #================================================= echo @@ -172,7 +432,7 @@ fi git -C "$racine_paquet" push #================================================= -# 3. LE CODE : tag et push +# LE CODE : tag et push #================================================= echo @@ -199,8 +459,6 @@ verifier_pousse "$racine_paquet" "refs/heads/$(git -C "$racine_paquet" rev-parse # CE QUI RESTE À FAIRE À LA MAIN #================================================= -url_archive="$base_url/v$version/$archive_nom" - cat <