A high-end digital atelier for professional prayer management.
Release builds require Android keystore credentials. Never hardcode these in
key.properties and never commit that file to source control.
build.gradle.kts reads credentials from environment variables first (CI/CD),
then falls back to android/key.properties (local developer machines).
A missing or empty KEYSTORE_PASSWORD causes an immediate GradleException
so the build fails with a clear message rather than silently producing an
unsigned APK.
| Env var | key.properties key |
Description |
|---|---|---|
KEYSTORE_PASSWORD |
storePassword |
Password for the .keystore file itself |
KEY_PASSWORD |
keyPassword |
Password for the specific key entry inside the store |
KEY_ALIAS |
keyAlias |
Alias chosen when the keystore was created |
KEYSTORE_PATH |
storeFile |
Absolute or project-relative path to the .keystore |
Store the keystore as a base64-encoded repository secret (KEYSTORE_BASE64),
then decode and inject it at build time:
- name: Decode keystore
run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > android/app/release.keystore
- name: Build release APK
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEYSTORE_PATH: android/app/release.keystore
run: flutter build apk --release --obfuscate --split-debug-info=build/android/symbols
- name: Build release AAB
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEYSTORE_PATH: android/app/release.keystore
run: flutter build appbundle --release --obfuscate --split-debug-info=build/android/symbolsUse the Android code signing section in the workflow editor. Codemagic
uploads the keystore and injects CM_KEYSTORE_PASSWORD, CM_KEY_PASSWORD,
CM_KEY_ALIAS, and CM_KEYSTORE_PATH automatically. Map them to the names
build.gradle.kts expects by adding to your codemagic.yaml environment:
environment:
vars:
KEYSTORE_PASSWORD: $CM_KEYSTORE_PASSWORD
KEY_PASSWORD: $CM_KEY_PASSWORD
KEY_ALIAS: $CM_KEY_ALIAS
KEYSTORE_PATH: $CM_KEYSTORE_PATHAdd a Android Sign step to your workflow. Bitrise sets BITRISEIO_ANDROID_KEYSTORE_PASSWORD,
BITRISEIO_ANDROID_KEYSTORE_ALIAS, and BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD.
Map them in the Env Vars tab:
KEYSTORE_PASSWORD → $BITRISEIO_ANDROID_KEYSTORE_PASSWORD
KEY_PASSWORD → $BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD
KEY_ALIAS → $BITRISEIO_ANDROID_KEYSTORE_ALIAS
KEYSTORE_PATH → (path output by the Keystore & Sign step)
Copy android/key.properties.example to android/key.properties and fill in
your local keystore details. This file is gitignored and will never be committed.
cp android/key.properties.example android/key.properties
# Edit android/key.properties with your actual valuesEvery production IPA must be built with code obfuscation enabled. This makes reverse-engineering significantly harder and is required before App Store submission.
flutter build ipa --release \
--obfuscate \
--split-debug-info=build/ios/symbolsWhy these flags matter
--obfuscaterenames Dart symbols so the compiled binary is resistant to reverse-engineering.
--split-debug-infowrites the symbol map tobuild/ios/symbols/.Upload
build/ios/symbols/to Crashlytics or Sentry before distributing the IPA. Without this directory, all production crash stack traces will show obfuscated names and be completely unreadable.
- name: Build IPA
run: |
flutter build ipa --release \
--obfuscate \
--split-debug-info=build/ios/symbols
- name: Upload debug symbols to Crashlytics
run: |
# Use the Firebase CLI or the Crashlytics Gradle plugin equivalent
# for iOS: upload-symbols -gsp GoogleService-Info.plist \
# -p ios build/ios/symbolsAdd to your codemagic.yaml build arguments:
scripts:
- name: Build IPA
script: |
flutter build ipa --release \
--obfuscate \
--split-debug-info=build/ios/symbolsUpload the generated build/ios/symbols/ directory as a build artifact and
pipe it into the Crashlytics or Sentry symbol upload step.
Add a Flutter Build step with these additional arguments:
--obfuscate --split-debug-info=build/ios/symbols
Then add a Script step after it to upload build/ios/symbols/ to your
crash reporting service.
If you ever build through Xcode directly (not recommended for release), pass
the same flags via the OTHER_DART_FLAGS build setting in the Runner scheme.
Prefer the flutter build ipa command above to guarantee obfuscation is never
accidentally skipped.
The app performs SHA-256 public-key pinning on all API connections via
lib/core/config/certificate_pins.dart. Before releasing to the stores:
-
Extract the real pin for each domain:
openssl s_client -connect api.minaret.app:443 </dev/null 2>/dev/null \ | openssl x509 -pubkey -noout \ | openssl pkey -pubin -outform der \ | openssl dgst -sha256 -binary \ | base64
-
Replace the
PLACEHOLDERvalues inpinnedDomainsinside that file. -
A debug-build
AssertionErrorwill fire if any placeholder remains.