Aller au contenu principal

Commande release

La commande tk-ota-push release compile l'application Flutter en mode --release avec la toolchain Shorebird, extrait de manière déterministe le binaire Dart de référence (binaire d'application sur Android, diff_base sur iOS), calcule les empreintes d'intégrité (pubspec_lock_hash, assets_manifest_hash, native_code_hash) et verrouille la Release sur le serveur OTA (POST /api/admin/v1/releases).

Syntaxe générale​

tk-ota-push release [version] [options] [-- <arguments-flutter>]
  • Si [version] est omis, la version complète (x.y.z+build) est lue dans pubspec.yaml.
  • Si [version] est fourni (ex: 1.0.0+3) et diffère de pubspec.yaml, la CLI met automatiquement à jour pubspec.yaml uniquement après avoir vérifié que le serveur OTA est joignable (prepare(version, sync: true)).

Flux d'exécution complet (Publisher.prepare & releaseAndroid / releaseIos)​

Étapes de création d'une release
  1. Configuration des clés : Synchronisation de la clé publique dans shorebird.yaml.
  2. Compilation de l'application : Exécution de flutter build pour générer le binaire complet.
  3. Enregistrement sur le serveur : Téléversement du binaire de référence sur TkushPatch.
  4. Soumission aux magasins : Publication du binaire (.aab ou .ipa) sur Google Play ou l'App Store.

1. Garde-fou pré-compilation contre le Release Overwrite​

Avant même de lancer flutter build, la méthode guardNoOverwrite interroge GET /api/admin/v1/releases/lookup pour chaque plateforme et architecture ciblée. Si la Release est déjà enregistrée sur le serveur, la CLI interrompt immédiatement l'opération avec le message suivant :

HTTP 409 CONFLICT - PROTECTION CONTRE LE 'RELEASE OVERWRITE'
La Release '1.0.0+1' (android/aarch64) est DÉJÀ verrouillée.
SHA-256 de référence : <baseSha256Hash>

Recompiler sous le même numéro changerait les adresses mémoire et ferait
crasher (Segfault) les téléphones qui exécutent déjà cette Release.

- Publier un patch : tk-ota-push patch 1.0.0+1 --platform android
- Nouvelle Release : tk-ota-push release <version avec build incrémenté>

2. Spécificités Android (apk vs aab et multi-ABI)​

Choix du format d'artefact (--artifact)​

  • --artifact apk (défaut) : nettoie build/app/outputs/apk puis exécute flutter build apk --release.
  • --artifact aab : nettoie build/app/outputs/bundle puis exécute flutter build appbundle --release (recommandé pour Google Play Store).

Architecture cible (--arch / -a)​

  • --arch all (défaut) : verrouille successivement les deux ABI Android :
    • aarch64 (correspondant à lib/arm64-v8a/binaire d'application ou base/lib/arm64-v8a/binaire d'application)
    • arm (correspondant à lib/armeabi-v7a/binaire d'application ou base/lib/armeabi-v7a/binaire d'application)
  • --arch aarch64 : verrouille uniquement le binaire 64 bits.
  • --arch arm : verrouille uniquement le binaire 32 bits.

Règle d'or OTA : extraction directe depuis l'archive finale​

Pour éviter toute erreur Update rejected: hash mismatch entre les dossiers intermédiaires de Gradle et le téléphone physique :

  1. La CLI localise l'archive finale signée/alignée dans build/app/outputs/ (_findBuiltPackage).
  2. Elle décompresse en mémoire l'APK ou l'AAB via ZipDecoder (_extractLibAppFromPackage) et extrait lib/<abi>/binaire d'application (ou base/lib/<abi>/binaire d'application) dans build/ota_extracted_libs/<version>/<arch>/binaire d'application.
  3. En cas de repli uniquement, elle recherche dans build/ en donnant priorité absolue aux dossiers stripped_native_libs.

3. Spécificités iOS (ios vs ipa et cache local de référence)​

Choix du format d'artefact (--artifact)​

  • --artifact ios (ou défaut hors ipa) : nettoie build/ios/iphoneos et exécute flutter build ios --release.
  • --artifact ipa : nettoie build/ios/archive et build/ios/ipa, puis exécute flutter build ipa --release en relayant --export-method=<methode> (ad-hoc, app-store, enterprise, development) et --export-options-plist=<chemin>.

Résolution du bundle .app et création du cache .ota_ios_release_cache​

  1. En mode ipa, la CLI cherche en priorité le bundle .app contenant Frameworks/App.framework/App dans build/ios/archive/ (archive .xcarchive), puis dans build/ios/iphoneos/.
  2. Elle crée le répertoire local .ota_ios_release_cache/<version>/ à la racine du projet Flutter et y sauvegarde :
    • Runner.app (copie intégrale signée)
    • App (binaire Mach-O Frameworks/App.framework/App)
    • release_version.txt
    • supplement/ (fichiers App.*.link, App.*_table.json, App.dd_resolution.tsv)
  3. Elle extrait le snapshot de base via aot-tools.dill dump_blobs :
    <dart> <aot-tools.dill> dump_blobs \
    --analyze-snapshot=<analyze_snapshot_arm64> \
    --snapshot=.ota_ios_release_cache/<version>/App \
    --output=.ota_ios_release_cache/<version>/diff_base
  4. Le fichier diff_base est envoyé au serveur OTA avec platform=ios et arch=aarch64.
Conservation du dossier .ota_ios_release_cache/<version>

Sur iOS, l'assemblage d'un patch (aot_tools link) nécessite le binaire App et le fichier diff_base générés lors de la Release. Conservez ou mettez en cache .ota_ios_release_cache/<version> dans votre pipeline CI/CD pour pouvoir publier des patchs iOS ultérieurement.

4. Scellement des 3 empreintes d'intégrité​

Lors de l'appel multipart POST /api/admin/v1/releases, la CLI transmet trois condensats SHA-256 :

  • pubspec_lock_hash : SHA-256 de pubspec.lock.
  • assets_manifest_hash : SHA-256 canonique de la déclaration flutter: assets: et du contenu de chaque fichier d'asset (computeAssetsManifestHash()).
  • native_code_hash : SHA-256 calculé par NativeIntegrityScanner sur classes*.dex, AndroidManifest.xml et lib/<arch>/*.so (hors binaire d'application) sur Android, ou Info.plist, exécutable principal et Frameworks/ (hors App.framework) sur iOS.

Exemples de commandes release​

# Release Zero-Config (Android + iOS, version lue dans pubspec.yaml, obfuscation active)
tk-ota-push release

# Release Android AppBundle (.aab) pour Google Play avec Flavor prod
tk-ota-push release 2.1.0+105 -p android \
--flavor prod \
-t lib/main_prod.dart \
--dart-define-from-file=env/prod.json \
--artifact aab

# Release iOS (.ipa) pour App Store / TestFlight avec Flavor prod
tk-ota-push release 2.1.0+105 -p ios \
--flavor prod \
-t lib/main_prod.dart \
--dart-define-from-file=env/prod.json \
--artifact ipa \
--export-method app-store

Exemple de reçu de verrouillage Release​

  ┌──────────────────────────────────────────────────────────────────────────
│ 📦 REÇU DE VERROUILLAGE RELEASE (COFFRE-FORT OTA)
├──────────────────────────────────────────────────────────────────────────
│ ID Release (UUID) : a42e271a-31ab-4476-ae69-24e5d2b8448f
│ Application / Tenant: sara-money-demo (sara-money)
│ Version & Cible : v1.0.0+3 • android (aarch64)
│ Statut : LIVE_ON_STORE
│ Binaire Référence : binaire d'application (Scellé)
│ SHA-256 Binaire : 6b6929c8ff345cf008496328d7bff081a8ccfb89498bf9916ed668be3c3dedcf
│ Garde-Fou Natif : 42d889f8f4b6bf9d3abb767d... (pubspec.lock)
│ Garde-Fou Assets : 99e887f4f4b6bf9d3abb767d... (AssetManifest)
│ Symboles (.symbols) : Archivés (app.android-arm64.symbols • SHA-256 : abcdef123456...)
│ Horodatage (UTC) : 2026-11-03T11:05:55.847643091Z
└──────────────────────────────────────────────────────────────────────────