Skip to main content

release Command

The tk-ota-push release command builds the Flutter application in --release mode using the Shorebird toolchain, deterministically extracts the reference Dart binary (application binary on Android, diff_base on iOS), computes integrity hashes (pubspec_lock_hash, assets_manifest_hash, native_code_hash), and locks the Release on the OTA server (POST /api/admin/v1/releases).

General Syntax​

tk-ota-push release [version] [options] [-- <flutter-arguments>]
  • If [version] is omitted, the full version string (x.y.z+build) is read from pubspec.yaml.
  • If [version] is provided (e.g., 1.0.0+3) and differs from pubspec.yaml, the CLI updates pubspec.yaml only after verifying that the OTA server is reachable (prepare(version, sync: true)).

Full Execution Flow (Publisher.prepare & releaseAndroid / releaseIos)​

Release Creation Steps
  1. Key Setup: Synchronizes the public signing key in shorebird.yaml.
  2. App Compilation: Executes flutter build to produce the complete base application package.
  3. Server Registration: Uploads base reference binaries to TkushPatch.
  4. Store Submission: Submits the binary (.aab or .ipa) to Google Play or Apple App Store.

1. Pre-Build Guard Against Release Overwrite​

Before invoking flutter build, the guardNoOverwrite method queries GET /api/admin/v1/releases/lookup for every targeted platform and architecture. If the Release is already locked on the server, the CLI aborts immediately with the following message:

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. Android Specifics (apk vs aab and Multi-ABI)​

Selecting the Artifact Format (--artifact)​

  • --artifact apk (default): cleans build/app/outputs/apk and runs flutter build apk --release.
  • --artifact aab: cleans build/app/outputs/bundle and runs flutter build appbundle --release (recommended for Google Play Store).

Target Architecture (--arch / -a)​

  • --arch all (default): locks both Android ABIs sequentially:
    • aarch64 (mapped to lib/arm64-v8a/application binary or base/lib/arm64-v8a/application binary)
    • arm (mapped to lib/armeabi-v7a/application binary or base/lib/armeabi-v7a/application binary)
  • --arch aarch64: locks only the 64-bit binary.
  • --arch arm: locks only the 32-bit binary.

Golden OTA Rule: Direct Extraction from the Final Archive​

To eliminate any Update rejected: hash mismatch error between intermediate Gradle folders and physical devices:

  1. The CLI locates the final aligned package inside build/app/outputs/ (_findBuiltPackage).
  2. It decodes the APK or AAB in memory using ZipDecoder (_extractLibAppFromPackage) and extracts lib/<abi>/application binary (or base/lib/<abi>/application binary) to build/ota_extracted_libs/<version>/<arch>/application binary.
  3. Only as a fallback does it search build/, strictly prioritizing stripped_native_libs directories.

3. iOS Specifics (ios vs ipa and Local Reference Cache)​

Selecting the Artifact Format (--artifact)​

  • --artifact ios (or default when not ipa): cleans build/ios/iphoneos and runs flutter build ios --release.
  • --artifact ipa: cleans build/ios/archive and build/ios/ipa, then runs flutter build ipa --release while forwarding --export-method=<method> (ad-hoc, app-store, enterprise, development) and --export-options-plist=<path>.

Resolving the .app Bundle and Creating .ota_ios_release_cache​

  1. In ipa mode, the CLI first searches for the .app bundle containing Frameworks/App.framework/App inside build/ios/archive/ (.xcarchive), then falls back to build/ios/iphoneos/.
  2. It creates .ota_ios_release_cache/<version>/ at the Flutter project root and stores:
    • Runner.app (full signed copy)
    • App (Frameworks/App.framework/App Mach-O binary)
    • release_version.txt
    • supplement/ (App.*.link, App.*_table.json, App.dd_resolution.tsv files)
  3. It extracts the base snapshot using 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. The resulting diff_base file is uploaded to the OTA server with platform=ios and arch=aarch64.
Preserving .ota_ios_release_cache/<version>

On iOS, linking a patch (aot_tools link) requires both the App binary and the diff_base file generated during the Release. Keep or cache .ota_ios_release_cache/<version> in your CI/CD pipeline so you can publish iOS patches later.

4. Sealing the 3 Integrity Hashes​

During the multipart POST /api/admin/v1/releases call, the CLI sends three SHA-256 digests:

  • pubspec_lock_hash: SHA-256 of pubspec.lock.
  • assets_manifest_hash: canonical SHA-256 of the flutter: assets: section and the contents of every referenced asset file (computeAssetsManifestHash()).
  • native_code_hash: SHA-256 computed by NativeIntegrityScanner across classes*.dex, AndroidManifest.xml, and lib/<arch>/*.so (excluding application binary) on Android, or Info.plist, the main executable, and Frameworks/ (excluding App.framework) on iOS.

release Command Examples​

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

# Android AppBundle (.aab) Release for Google Play with prod flavor
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

# iOS (.ipa) Release for App Store / TestFlight with prod flavor
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

Example Release Lock Receipt​

  ┌──────────────────────────────────────────────────────────────────────────
│ 📦 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 : application binary (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
└──────────────────────────────────────────────────────────────────────────