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 frompubspec.yaml. - If
[version]is provided (e.g.,1.0.0+3) and differs frompubspec.yaml, the CLI updatespubspec.yamlonly after verifying that the OTA server is reachable (prepare(version, sync: true)).
Full Execution Flow (Publisher.prepare & releaseAndroid / releaseIos)
- Key Setup: Synchronizes the public signing key in
shorebird.yaml. - App Compilation: Executes
flutter buildto produce the complete base application package. - Server Registration: Uploads base reference binaries to TkushPatch.
- Store Submission: Submits the binary (
.aabor.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): cleansbuild/app/outputs/apkand runsflutter build apk --release.--artifact aab: cleansbuild/app/outputs/bundleand runsflutter build appbundle --release(recommended for Google Play Store).
Target Architecture (--arch / -a)
--arch all(default): locks both Android ABIs sequentially:aarch64(mapped tolib/arm64-v8a/application binaryorbase/lib/arm64-v8a/application binary)arm(mapped tolib/armeabi-v7a/application binaryorbase/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:
- The CLI locates the final aligned package inside
build/app/outputs/(_findBuiltPackage). - It decodes the APK or AAB in memory using
ZipDecoder(_extractLibAppFromPackage) and extractslib/<abi>/application binary(orbase/lib/<abi>/application binary) tobuild/ota_extracted_libs/<version>/<arch>/application binary. - Only as a fallback does it search
build/, strictly prioritizingstripped_native_libsdirectories.
3. iOS Specifics (ios vs ipa and Local Reference Cache)
Selecting the Artifact Format (--artifact)
--artifact ios(or default when notipa): cleansbuild/ios/iphoneosand runsflutter build ios --release.--artifact ipa: cleansbuild/ios/archiveandbuild/ios/ipa, then runsflutter build ipa --releasewhile 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
- In
ipamode, the CLI first searches for the.appbundle containingFrameworks/App.framework/Appinsidebuild/ios/archive/(.xcarchive), then falls back tobuild/ios/iphoneos/. - It creates
.ota_ios_release_cache/<version>/at the Flutter project root and stores:Runner.app(full signed copy)App(Frameworks/App.framework/AppMach-O binary)release_version.txtsupplement/(App.*.link,App.*_table.json,App.dd_resolution.tsvfiles)
- 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 - The resulting
diff_basefile is uploaded to the OTA server withplatform=iosandarch=aarch64.
.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 ofpubspec.lock.assets_manifest_hash: canonical SHA-256 of theflutter: assets:section and the contents of every referenced asset file (computeAssetsManifestHash()).native_code_hash: SHA-256 computed byNativeIntegrityScanneracrossclasses*.dex,AndroidManifest.xml, andlib/<arch>/*.so(excludingapplication binary) on Android, orInfo.plist, the main executable, andFrameworks/(excludingApp.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
└──────────────────────────────────────────────────────────────────────────