build: reproductibilité des jniLibs — manifest sha256 + script + garde release

Les 23 .so de app/src/main/jniLibs/ (474 Mo, 5 chaînes de build : kazeia-engine,
ExecuTorch, QNN SDK 2.42, Genie, TTS pipeline) sont des artefacts gitignorés
posés à la main — « ça ne buildait que sur cette machine ». On ne les versionne
pas (l'historique gonflerait de ~500 Mo par update engine) ; on versionne leur
DÉFINITION :

- scripts/jnilibs.MANIFEST.sha256 : état attendu (sha256 + provenances).
- scripts/jnilibs.sh : verify (CI/Gradle) | sync-engine (rafraîchit le
  sous-ensemble kazeia-engine depuis /opt/Kazeia-engine/dist) | update-manifest
  (re-fige, à committer) | export/import (tarball pour autre machine).
- Garde Gradle : preReleaseBuild dépend de verifyJniLibs → un build RELEASE avec
  des libs manquantes/modifiées/non déclarées ÉCHOUE. Debug reste libre.

Validé : release passe avec libs conformes ; échoue sur lib altérée (MODIFIÉ
libomp.so) ; verify OK après restauration. Ménage au passage : 3 .bak morts
(~293 Mo) sortis de jniLibs vers _jnilibs_backup_baks/.

Nouvelle machine : git clone + ./scripts/jnilibs.sh import <tarball> (export
déposé sur box.kazeia.com privé) → build garanti conforme.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Kazeia Team 2026-06-11 09:31:00 +02:00
parent e5b48e4e0f
commit 9c6f4e8838
3 changed files with 124 additions and 0 deletions

View File

@ -108,6 +108,16 @@ android {
}
}
// Reproductibilité des jniLibs : les .so (gitignorés, 5 chaînes de build) doivent
// correspondre au manifest versionné. Un build RELEASE avec des libs divergentes
// échoue ; le debug reste libre (itération engine). Cf scripts/jnilibs.sh.
val verifyJniLibs = tasks.register<Exec>("verifyJniLibs") {
commandLine("bash", rootProject.file("scripts/jnilibs.sh").absolutePath, "verify")
}
tasks.matching { it.name == "preReleaseBuild" }.configureEach {
dependsOn(verifyJniLibs)
}
dependencies {
// Tests unitaires JVM (src/test/) — logique pure : CrisisGuard, VectorIndex, Chunker.
// Lancer : ./gradlew :app:testDebugUnitTest

View File

@ -0,0 +1,29 @@
# jniLibs MANIFEST — état attendu de app/src/main/jniLibs/arm64-v8a/
# Régénéré par scripts/jnilibs.sh update-manifest le 2026-06-11.
# Provenances : kazeia-engine dist (libllama/libggml*/libkazeia_*),
# ExecuTorch build local (libexecutorch*, libqnn_executorch_backend, libfbjni),
# QNN SDK 2.42 (libQnn*), Genie (libGenie), TTS pipeline (libtts_pipeline), libomp.
# Les binaires ne sont PAS dans git ; ce manifest fait foi (verify en preBuild).
dd0506873aeb45e103ef0023678a7f14938a0f147f2348d1efc366f1dce40863 libexecutorch_jni.so
0b6f86e114f0cf9f2940194797f06ded576e6b3272a5ffbb2c4b8cdc02850ef8 libexecutorch.so
34a0af206f660461e3da687f64172830cc61a32a93732fd7e984c43996d6fd91 libfbjni.so
d49b53137f478b7b4ccbd58b9166d83e832c299b681609c70628291c61ae7204 libGenie.so
616fa830f6fd3396ea7f0ca92f348c2146efb0b1154ab6ed2e6709655905ead3 libggml-base.so
66ea0c948385d8060a7296285d7a85738c21318a88ef767986444a3d9683aeea libggml-cpu.so
7aeb209ec5c1da8857779b8f3b4c3e7519ed69c6211491948d3c87349dbced57 libggml.so
daf3695e82debf425e4a53b73e96fcb9e9e01ab2fe8513bf6db8f53b09450a34 libkazeia_engine.so
fdd730af3daf132d0819b3fc99abd8aae79800aab7a9aad371e117c16bd3a3aa libkazeia_stt.so
114795bf0256c380dc87971598707e9e0b745a7fc868f3d1fb460e94cecdd69c libkazeia_tts.so
349ba592462113c6e871b9c3ee508727a9e5f154a33905f946bda97535eff586 libllama-common.so
2ca77a28421fc0c680e0a5ddf0208a9471268370893f8dd4eba2a10a8aacb4ac libllama.so
a207169a2696e8f0f7639090c2882152c734368b5696246a514d63b3ce0769c1 libomp.so
d1e8fc79e47fe04ed00aa9bbcc5ccb84d187f0160b1d77ae5bd012aedea9c938 libqnn_executorch_backend.so
414794639e2897120143cff80aaf73e830e305357b6c1aa8343de380c89b8c36 libQnnGpuNetRunExtensions.so
26f13b297d375e9629eb997b7adb05a4ce9e1c17d306baaefe905563fee43eb8 libQnnGpuProfilingReader.so
b5b52b66f3fc5567429024409dcedb006ca6a9115f271689810589b7d79af430 libQnnGpu.so
2daafb376fe6904aa325e29810480e3d76dc392991d1367ae6ea3508db2b60ac libQnnHtpPrepare.so
10eb1923b34ac9a726e4a446b395d5eee2ea5388487917778aaf954e8bfb2d92 libQnnHtp.so
7ee72b438c97c13ceb96d18fe255d1f156afbdc4c43669752f349dd5a56e62a3 libQnnHtpV79Skel.so
a08fb34747345125fd3cef3fc10046c663465dd60fddbac9cc37436b3d2e68a9 libQnnHtpV79Stub.so
a69cfc4350c16b226bcaa19c657234820bb93e3ff6116cf3845ae77557f1f7c3 libQnnSystem.so
d35a02f70cb63ec60c8ae78011f5e93aecbb31396d5395a4a82cb1bc71803956 libtts_pipeline.so

View File

@ -0,0 +1,85 @@
#!/usr/bin/env bash
# Gestion reproductible des jniLibs de l'app Kazeia.
#
# Les .so de app/src/main/jniLibs/arm64-v8a/ sont des ARTEFACTS (gitignorés),
# venant de 5 chaînes de build différentes (kazeia-engine, ExecuTorch, QNN SDK,
# Genie, TTS pipeline). La SOURCE DE VÉRITÉ versionnée est le manifest sha256 :
# kazeia-android/scripts/jnilibs.MANIFEST.sha256
#
# Usage :
# ./jnilibs.sh verify # compare jniLibs au manifest (CI/Gradle l'appelle)
# ./jnilibs.sh sync-engine # rafraîchit le sous-ensemble kazeia-engine depuis /opt/Kazeia-engine/dist
# ./jnilibs.sh update-manifest # re-fige le manifest sur l'état courant (à committer !)
# ./jnilibs.sh export # tarball des jniLibs (transfert vers une autre machine)
# ./jnilibs.sh import <tar.gz> # restaure les jniLibs depuis un export, puis verify
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
LIBS="$HERE/../app/src/main/jniLibs/arm64-v8a"
MANIFEST="$HERE/jnilibs.MANIFEST.sha256"
ENGINE_DIST="/opt/Kazeia-engine/dist"
verify() {
[ -f "$MANIFEST" ] || { echo "ERREUR: manifest absent ($MANIFEST)"; exit 1; }
local fail=0
# 1) tous les fichiers du manifest présents et conformes
while read -r sum path; do
[ -z "$sum" ] && continue; case "$sum" in \#*) continue;; esac
local f="$LIBS/$path"
if [ ! -f "$f" ]; then echo " MANQUANT $path"; fail=1
elif [ "$(sha256sum "$f" | cut -d' ' -f1)" != "$sum" ]; then echo " MODIFIÉ $path"; fail=1
fi
done < "$MANIFEST"
# 2) pas de .so non déclaré (un intrus = build non reproductible ailleurs)
for f in "$LIBS"/*.so; do
local b; b="$(basename "$f")"
grep -q " $b\$" "$MANIFEST" || { echo " NON DÉCLARÉ $b (update-manifest ?)"; fail=1; }
done
if [ $fail -eq 0 ]; then echo "jniLibs OK ($(grep -cv '^#' "$MANIFEST" | cat) libs conformes au manifest)";
else echo ""; echo "jniLibs NON conformes. Corriger puis './jnilibs.sh update-manifest' si le changement est voulu."; exit 1; fi
}
sync_engine() {
# Sous-ensemble possédé par kazeia-engine (build CPU-only, cf RAG_INTEGRATION.md engine)
[ -d "$ENGINE_DIST" ] || { echo "ERREUR: $ENGINE_DIST absent (machine sans le dépôt engine — utiliser import)"; exit 1; }
for f in lib-cpu/libllama.so lib-cpu/libggml.so lib-cpu/libggml-base.so lib-cpu/libggml-cpu.so \
b-jni-cpu/libkazeia_engine.so b-jni-cpu/libkazeia_tts.so; do
cp -v "$ENGINE_DIST/$f" "$LIBS/" || true
done
echo "→ vérifier l'app sur device, puis './jnilibs.sh update-manifest' + commit."
}
update_manifest() {
{
echo "# jniLibs MANIFEST — état attendu de app/src/main/jniLibs/arm64-v8a/"
echo "# Régénéré par scripts/jnilibs.sh update-manifest le $(date +%F)."
echo "# Provenances : kazeia-engine dist (libllama/libggml*/libkazeia_*),"
echo "# ExecuTorch build local (libexecutorch*, libqnn_executorch_backend, libfbjni),"
echo "# QNN SDK 2.42 (libQnn*), Genie (libGenie), TTS pipeline (libtts_pipeline), libomp."
echo "# Les binaires ne sont PAS dans git ; ce manifest fait foi (verify en preBuild)."
( cd "$LIBS" && sha256sum *.so | sort -k2 )
} > "$MANIFEST"
echo "manifest re-figé : $MANIFEST ($(grep -cv '^#' "$MANIFEST") libs) — À COMMITTER."
}
export_tar() {
local out="/opt/Kazeia/_archives/kazeia-jnilibs-$(date +%Y%m%d).tar.gz"
mkdir -p /opt/Kazeia/_archives
tar czf "$out" -C "$LIBS/.." arm64-v8a
echo "export : $out ($(du -h "$out" | cut -f1)) — à déposer sur box.kazeia.com (dossier privé) pour les autres machines."
}
import_tar() {
[ -f "${1:-}" ] || { echo "usage: jnilibs.sh import <tar.gz>"; exit 1; }
tar xzf "$1" -C "$LIBS/.."
verify
}
case "${1:-}" in
verify) verify;;
sync-engine) sync_engine;;
update-manifest) update_manifest;;
export) export_tar;;
import) import_tar "${2:-}";;
*) grep '^#' "$0" | head -14; exit 1;;
esac