patch Command
The tk-ota-push patch command compiles updated Dart code, verifies native binary integrity against the locked Release, and uploads the new binary (application binary on Android, patch artifact on iOS) to the OTA server (POST /api/admin/v1/patches). The server then computes the binary differential (binary diff + optimized compression) and cryptographically signs it via cryptographic signing engine.
General Syntax
tk-ota-push patch [version] [options] [-- <flutter-arguments>]
Execution Flow and Differential Generation
- Preparation: Verifies project metadata and
shorebird.yamlconfiguration. - Dart Compilation: Builds application snapshots for all target hardware architectures (
aarch64,arm, etc.). - Diff Computation: Computes an optimized compressed diff against the base release.
- Secure Upload: Submits the patch artifact to the server with initial status
STAGED.
1. Platform-Specific Compilation and Linking
On Android (patchAndroid)
- The CLI runs
flutter build apk --release(orappbundlewhen--artifact aabis passed) withSHOREBIRD_PUBLIC_KEY. - It extracts the stripped
application binarybinaries directly from the.apkor.aabarchive for each requested architecture (aarch64andarmby default with--arch all). - It calls
GET /api/admin/v1/releases/lookupto retrieve the reference Release'snativeCodeHashand compares it with thelocalNativeHashcomputed byNativeIntegrityScanner. - It uploads each
application binaryalong with its.symbolsfile (app.android-arm64.symbols/app.android-arm.symbols) toPOST /api/admin/v1/patches.
On iOS (patchIos)
- The CLI checks for
.ota_ios_release_cache/<version>/Appanddiff_base. - It computes the local SHA-256 of
diff_baseand compares it withbaseSha256Hashreturned byGET /api/admin/v1/releases/lookup(platform=ios,arch=aarch64). If they differ (MISMATCH DE HASH iOS), patch generation is immediately aborted. - It runs
flutter build ios --release --no-codesign, locatesapp.dillinside.dart_tool/flutter_build/, and copies it tobuild/app.dill. - It generates the ELF snapshot
build/out.aotusinggen_snapshot_arm64:<gen_snapshot_arm64> --deterministic --snapshot-kind=app-aot-elf --elf=build/out.aot [splitDebugArgs] build/app.dill - It runs
aot-tools.dill linkto link the baseAppbinary and the newout.aotsnapshot intobuild/patch artifact:<dart> <aot-tools.dill> link \
--base=.ota_ios_release_cache/<version>/App \
--patch=build/out.aot \
--analyze-snapshot=<analyze_snapshot_arm64> \
--gen-snapshot=<gen_snapshot_arm64> \
--kernel=build/app.dill \
--output=build/patch artifact - It restores the signed
Runner.appfrom.ota_ios_release_cache/<version>/Runner.app, verifies iOS native integrity (Info.plistandFrameworks/), and uploadspatch artifactto the OTA server.
2. binary diff + optimized compression Differential and signing engine Signature
Upon receiving POST /api/admin/v1/patches, the OTA server (TkushPatch Server):
- Validates the submitted binary size (anti-bomb guard: 60 MB max by default).
- Loads the exact reference binary stored during the Release (
application binaryordiff_base). - Generates the compressed differential patch in
binary diff+optimized compressionformat and verifies that its size does not exceedmaxPatchDiffSizeBytes(15 MB by default). - Computes
targetUncompressedSha256Hex(the SHA-256 hash of the full new binary). - If
auto_approve=true(no--stagedflag) and the tenant does not enforce strict 4-eyes review (enforceFourEyes == false), immediately signs the 64 ASCII bytes of the target SHA-256 via cryptographic signing engine (APPROVED_AND_SIGNED). Otherwise, the patch is saved inSTAGEDstatus.
3. Native Checks That Block a Patch
The OTA engine only replaces Dart code (application binary / App.framework). If you modify native code or OS permissions between the Release and the Patch, NativeIntegrityScanner detects a nativeCodeHash mismatch:
- Android: changes in
classes*.dex(Java/Kotlin),AndroidManifest.xml, or any C/C++ librarylib/<arch>/*.so(excludingapplication binary). - iOS: changes in
Info.plist, the mainRunnerexecutable, or any framework insideFrameworks/(excludingApp.framework).
When a mismatch occurs, the CLI blocks publication with a CliException (exit code 1) and instructs you to publish a new Store Release. Passing --force (-f) overrides this check (as well as the pubspecLockHash check) at your own risk.
pubspec.lock (Pure Dart Dependencies)Updating a 100% pure Dart package changes pubspec.lock but does not alter nativeCodeHash. Therefore, the CLI allows the patch without requiring --force, while the server logs an informational message.
4. Key Options for the patch Command
| Option / Flag | Purpose | Example |
|---|---|---|
-c, --channel <channel> | Target publication channel (stable, staging, beta) | --channel staging |
-m, --message <text> | Business description / changelog displayed to reviewers in the Console | -m "Fix VAT calculation" |
--rollout <0-100> | Initial progressive rollout percentage (Canary) | --rollout 10 |
--countries <codes> | Comma-separated ISO-2 country codes or "all" | --countries "CI,SN,CM" |
--staged | Places the patch in STAGED quarantine (awaiting 4-eyes approval) | --staged |
-f, --force | Ignores native divergence and aligns pubspecLockHash with the Release | --force |
Practical Examples
# 1. Simple patch on the default channel (100% rollout, worldwide)
tk-ota-push patch -m "Fix account balance display"
# 2. Android-only patch for 64-bit architecture on the staging channel
tk-ota-push patch 1.0.0+3 -p android -a aarch64 -c staging -m "64-bit QA test"
# 3. Patch with prod flavor, env file, country targeting, and 25% staged rollout
tk-ota-push patch 2.1.0+105 -p android \
--flavor prod \
-t lib/main_prod.dart \
--dart-define-from-file=env/prod.json \
--artifact aab \
--countries "CI,SN,CM" \
--rollout 25 \
--staged \
-m "Fix wire transfer fee calculation"