CLI

sankofa release

Build and publish a base release — the signed store binary plus the OTA baseline that patches attach to. Full flag reference and examples for iOS and Android, Flutter and React Native.

sankofa release builds your app and publishes a base release: the signed store binary you submit to the App Store / Play Store, plus the OTA baseline that sankofa patch later ships code updates against. You run it once per app version.

bash
sankofa release [platform] [options]

platform is ios or android. Omit it and the CLI prompts. The stack (Flutter vs. React Native) is auto-detected from pubspec.yaml / package.json, and the CLI runs the matching pipeline.

What it produces

StackiOSAndroid
FlutterSigned .ipa (App Store / TestFlight) + iOS OTA baseline.aab (Play Store) or --apk + OTA baseline (libapp.so)
React NativeSigned .ipa + OTA archive + optional simulator preview.aab/.apk + OTA archive + optional APK preview

For Flutter, the store binary is built with the Sankofa Flutter engine and the OTA wiring baked in. This is why you must publish through sankofa release rather than a raw flutter build — only the CLI registers the server-side baseline, and only the Sankofa-engine binary can accept patches later.

Common options

These apply to both Flutter and React Native.

--publishflag
Publish immediately without the interactive confirmation prompt. Required for non-interactive / CI runs.
--env <environment>live | test
Target environment. Prompts if omitted; resolves from SANKOFA_ENVIRONMENT / config otherwise.
--rollout <percent>0–100default 100
Initial rollout percentage. Deterministic per device — a device is either in or out of the band.
--mandatoryflag
Mark the release as mandatory (force-update). Existing devices must update before they can run.
--description <desc>string
Human-readable release note stored with the release row.
--project <path>path
Path to the app directory. Defaults to auto-detecting the project root from the current directory.
--dry-runflag
Build the app and capture the local patch-safety baseline, but do NOT contact the server or upload anything. Great for verifying the build + baseline before going live.

Flutter releases

Android

bash
# Play Store bundle (default)
sankofa release android

# Sideload-installable APK instead of an AAB
sankofa release android --apk

# Non-interactive with a staged rollout + note
sankofa release android --publish --rollout 25 --description "1.4.0 GA"

# Build + verify locally, upload nothing
sankofa release android --dry-run

sankofa release android builds the AAB (or APK), extracts the AOT baseline (libapp.so), verifies the embedded engine is a genuine Sankofa build, captures the auto-diff base for future patches, and registers the baseline on the server. The AAB it prints is your deployable — upload that to the Play Console, not the output of a raw flutter build.

iOS

bash
# Signed .ipa for App Store Connect / TestFlight
sankofa release ios

# Build the .xcarchive only (sign + export later in Xcode)
sankofa release ios --no-codesign

# Non-interactive
sankofa release ios --publish --rollout 50 --description "1.4.0"

sankofa release ios builds a signed .ipa (your store artifact — Sankofa never stores it), captures the auto-diff base, and registers an iOS OTA baseline on the server. iOS OTA runs through the engine's bytecode interpreter, so the baseline is a small signed envelope that the real code (shipped later by sankofa patch ios) attaches to.

Flavored apps

Apps with Gradle product flavors and a per-flavor entry point need both --flavor and --target, or the build picks the wrong main().

bash
sankofa release android --flavor staging    -t lib/main_staging.dart
sankofa release android --flavor production -t lib/main_production.dart
sankofa release ios      --flavor staging    -t lib/main_staging.dart

If you have no lib/main.dart and pass neither flag, the CLI stops with a hint showing the exact command to use.

Flutter-specific options

--apkflag
Android: produce a sideload-installable APK instead of the default AAB. Mutually exclusive with --appbundle.
--appbundleflagdefault on
Android: produce a Play Store AAB. This is the default.
--flavor <name>string
Product flavor to build (e.g. staging, production). Required for flavored apps.
-t, --target <file>path
App entry-point file (e.g. lib/main_staging.dart). Required for flavored apps without a lib/main.dart.
--no-codesignflag
iOS: build the .xcarchive without code-signing so you can sign + export later in Xcode.
--preview-artifactflag
Android: also upload the release APK so 'sankofa preview --from-server --label <v>' can install this exact build on a device. No-op on iOS (would require a second simulator-only build).
--dart-define <KEY=VALUE>repeatable
Extra dart-define baked into the build. Repeat for multiple. Use SANKOFA_SKIP_ENGINE_CHECK=1 here to bypass the bundled-engine compatibility check when your engine is forked but its Dart version string is unstamped.

React Native releases

React Native uses the same command surface. release bundles the JavaScript + assets, builds a native preview artifact, and (unless you opt out) the signed store binary.

bash
sankofa release ios
sankofa release android --publish --rollout 50 --description "v1.2.0"

# OTA archive only, skip the multi-minute signed-binary build
sankofa release android --skip-distribution

# Choose the Android format
sankofa release android --apk
sankofa release android --appbundle

React Native-specific options

--entry-file <file>path
JS entry file. Auto-detected (index.js / index.ts) when omitted.
--output-dir <dir>pathdefault ./build
Directory for built artifacts.
--no-native-artifactflag
Skip building/uploading the native preview artifact. Only use this when you intentionally do not need 'sankofa preview'.
--skip-distributionflag
Skip building the signed store binary (OTA-only release). By default 'release' also builds the submittable binary.
--ios-export-method <method>stringdefault app-store
iOS export method: app-store, ad-hoc, development, or enterprise.
--ios-team-id <id>string
Apple Developer Team ID for signing. Auto-detected from the archive when omitted.
--ios-export-options <path>path
Path to a custom ExportOptions.plist. Overrides --ios-export-method / --ios-team-id.
--apk / --appbundleflagdefault appbundle
Android output format. (--android-format aab|apk is a deprecated alias.)

After a successful release

The command prints the release label, platform, target version, rollout %, release ID, and the on-disk path + SHA-256 of every artifact it produced. Two follow-ups are the norm:

  1. Submit the store binary

    Upload the printed .ipa to App Store Connect (Transporter, Xcode Organizer, or xcrun altool) or the .aab to the Play Console. Sankofa does not submit it for you.

  2. Ship code updates with patch

    Once the base release is live, every subsequent code change ships over-the-air with sankofa patch — no new store submission.

  • sankofa patch — ship a code-only update against this release.
  • sankofa dist <ios|android> — build only the signed store binary, no OTA.
  • sankofa submit <ios|android> — upload a signed binary to App Store Connect / Play Console.
  • sankofa status — list all releases and patches for the current project.
  • sankofa preview — run a build for QA before you submit.
Edit this page on GitHub