Last Updated: 2026-02-13
Reusable Workflows: openMF/mifos-x-actionhub@v1.0.8
Custom Actions: 13 total (4 Android, 4 iOS, 2 macOS, 1 Desktop, 1 Web, 1 Static Analysis)
This project uses reusable workflows from openMF/mifos-x-actionhub repository. All workflows are version-pinned to @v1.0.8 for stability.
Architecture:
Local Workflows (.github/workflows/)
↓
Reusable Workflows (mifos-x-actionhub/.github/workflows/)
↓
Custom Actions (mifos-x-actionhub-*/)
↓
Fastlane Lanes (fastlane/Fastfile)
Purpose: Production deployment across all 5 platforms
Trigger:
- Manual dispatch (
workflow_dispatch) - Inputs:
release_type(internal/beta), platform toggles, package names
Reusable Workflow:
uses: openMF/mifos-x-actionhub/.github/workflows/multi-platform-build-and-publish.yaml@v1.0.8Jobs (10):
| # | Job Name | Platform | Custom Action | Purpose |
|---|---|---|---|---|
| 1 | publish_android_on_firebase |
Android | android-firebase-publish@v1.0.0 |
Deploy APK to Firebase |
| 2 | publish_android_on_playstore |
Android | publish-android-playstore-beta@v1.0.0 |
Deploy AAB to Play Store (internal/beta) |
| 3 | publish_ios_app_to_firebase |
iOS | publish-ios-firebase@v1.0.3 |
Deploy IPA to Firebase |
| 4 | publish_ios_app_to_testflight |
iOS | publish-ios-testflight@v1.0.1 |
Upload to TestFlight |
| 5 | publish_ios_app_to_appstore |
iOS | publish-ios-appstore@v1.0.1 |
Submit to App Store |
| 6 | publish_macos_app_to_testflight |
macOS | publish-macos-testflight-kmp@v1.0.0 |
macOS TestFlight |
| 7 | publish_macos_app_to_appstore |
macOS | publish-macos-appstore-kmp@v1.0.0 |
macOS App Store |
| 8 | publish_desktop_external |
Desktop | publish-desktop-app-kmp@v1.0.1 |
Matrix: Windows/macOS/Linux |
| 9 | publish_web |
Web | web-publish-kmp@v1.0.1 |
Deploy to GitHub Pages |
| 10 | github_release |
All | N/A | Create pre-release with artifacts |
Workflow Inputs:
inputs:
release_type: # 'internal' or 'beta'
target_branch: # default: 'dev'
# Package names
android_package_name: # e.g., 'cmp-android'
ios_package_name: # e.g., 'cmp-ios'
desktop_package_name: # e.g., 'cmp-desktop'
web_package_name: # e.g., 'cmp-web'
# Distribution toggles
distribute_ios_firebase: # boolean
distribute_ios_testflight: # boolean
distribute_ios_appstore: # boolean
# Configuration
use_cocoapods: # boolean (iOS dependency manager)
shared_module: # 'cmp-shared'
metadata_path: # './fastlane/metadata'
xcode-version: # '15.2'
java-version: # '17'Required Secrets: 30+ (see Secrets section)
Purpose: PR validation with static analysis and debug builds
Trigger:
- Pull request events (opened, synchronize, reopened)
Reusable Workflow:
uses: openMF/mifos-x-actionhub/.github/workflows/pr-check.yaml@v1.0.8Jobs (5):
| # | Job Name | Custom Action | Purpose |
|---|---|---|---|
| 1 | checks |
static-analysis-check@v1.0.1 |
Spotless, Detekt, Dependency Guard |
| 2 | build_android_app |
build-android-app@v1.0.2 |
Build debug Android APK |
| 3 | build_desktop_app |
build-desktop-app-kmp@v1.0.1 |
Matrix: ubuntu/macos/windows |
| 4 | build_web_app |
build-web-app-kmp@v1.0.1 |
Build web app |
| 5 | build_ios_app |
build-ios-app@v1.0.3 |
Build unsigned iOS IPA (optional) |
Workflow Inputs:
inputs:
build_ios: # boolean (default: true)
android_package_name: # e.g., 'cmp-android'
desktop_package_name: # e.g., 'cmp-desktop'
web_package_name: # e.g., 'cmp-web'
ios_package_name: # e.g., 'cmp-ios'
use_cocoapods: # boolean
shared_module: # 'cmp-shared'Required Secrets: None (debug builds only)
Purpose: Promote Play Store beta track → production
Trigger:
- Manual dispatch
- Release published
Reusable Workflow:
uses: openMF/mifos-x-actionhub/.github/workflows/promote-to-production.yaml@v1.0.8Jobs (1):
| Job Name | Custom Action | Purpose |
|---|---|---|
play_promote_production |
publish-android-playstore-production@v1.0.0 |
Promote beta → production |
Required Secrets: PLAYSTORECREDS
Purpose: Build Android APK or AAB (debug or release)
Inputs:
android_package_name(required): Module name (e.g.,cmp-android)build_type(required):DebugorReleasekey_store(optional): Base64-encoded keystoregoogle_services(optional): Base64-encoded google-services.jsonkey_store_password,key_store_alias,key_store_alias_password(optional)java-version(default:17)
What it does:
- Sets up Java 17 (Zulu distribution)
- Caches Gradle dependencies
- Version Generation (if Release):
- Tries:
./gradlew versionFile→ readsversion.txt - Fallback: Git-based version (latest tag + commit count)
- Calculates
VERSION_CODEfrom commit count
- Tries:
- Inflates keystore and google-services.json from base64
- Runs:
./gradlew :{package_name}:assembleRelease(orassembleDebug) - Uploads APK as artifact
set +e swallows versionFile errors. See BUGS_AND_ISSUES.md.
Output Artifacts:
android-app(all APKs from**/build/outputs/apk/**/*.apk)
Purpose: Deploy Android APK to Firebase App Distribution
Inputs:
android_package_name(required)release_type(optional):prod(default) ordemokeystore_file,keystore_password,keystore_alias,keystore_alias_passwordgoogle_services(required): Base64-encoded google-services.jsonfirebase_creds(required): Base64-encoded Firebase service account JSONtester_groups(required): Firebase tester group name
What it does:
- Installs Fastlane + plugins (
firebase_app_distribution,increment_build_number) - Inflates secrets to:
{package_name}/google-services.jsonkeystores/release_keystore.keystoresecrets/firebaseAppDistributionServiceCredentialsFile.json
- Calls Fastlane lane:
- Prod:
bundle exec fastlane android deployReleaseApkOnFirebase - Demo:
bundle exec fastlane android deployDemoApkOnFirebase
- Prod:
- Cleans up secrets
tester_groups input is IGNORED. Fastlane lane doesn't use it.
- Workaround: Set
ENV['FIREBASE_GROUPS']in workflow environment - See BUGS_AND_ISSUES.md
Output Artifacts:
firebase-app(all APKs)
Purpose: Deploy AAB to Play Store (internal track, optionally promote to beta)
Inputs:
android_package_name(required)release_type(required):internalorbeta- Keystore parameters (same as above)
google_services(required)playstore_creds(required): Base64-encoded Play Store service account JSON
What it does:
- Inflates secrets to:
{package_name}/google-services.jsonkeystores/release_keystore.keystoresecrets/playStorePublishServiceCredentialsFile.json
- Calls Fastlane lane:
bundle exec fastlane android deployInternal- Uploads AAB to internal track
- If
release_type == 'beta':bundle exec fastlane android promoteToBeta- Promotes internal → beta
- Cleans up secrets
Fastlane Lanes Used:
deployInternal(line 108): Builds AAB, uploads to internal trackpromoteToBeta(line 139): Promotes internal → beta
Output Artifacts:
play-store-app(AAB from**/build/outputs/bundle/**/*.aab)
Purpose: Promote Play Store beta track → production
Inputs:
playstore_creds(required): Base64-encoded service account JSON
What it does:
- Installs Fastlane
- Inflates Play Store credentials to
secrets/playStorePublishServiceCredentialsFile.json - Calls:
bundle exec fastlane android promote_to_production - Cleans up secrets
Fastlane Lane Used:
promote_to_production(line 151): Promotes beta → production
No Output Artifacts
Purpose: Build iOS IPA (debug unsigned or release signed)
Inputs:
ios_package_name(required): iOS module name (e.g.,cmp-ios)build_type(required):DebugorReleaseuse_cocoapods(default:false): Install CocoaPods dependenciesshared_module(required): Shared module path- For Release builds:
appstore_key_id,appstore_issuer_id(App Store Connect API)appstore_auth_key(Base64-encoded .p8 file)match_password: Fastlane Match passphrasematch_ssh_private_key(Base64-encoded SSH key for Match repo)
What it does:
- Sets up Ruby + Fastlane
- Sets up Xcode (default: 15.2)
- If Release:
- Writes App Store Connect API key to
secrets/AuthKey.p8 - Configures SSH for Fastlane Match:
secrets/match_ci_key+~/.ssh/config - Calls:
bundle exec fastlane ios build_signed_ios
- Writes App Store Connect API key to
- If Debug:
- Calls:
bundle exec fastlane ios build_ios(no code signing)
- Calls:
- Cleans up secrets
Fastlane Lanes Used:
build_ios(line 436): Debug build, skip codesigningbuild_signed_ios(line 456): Release build with Match certificates
Output Artifacts:
ios-app(IPA from**/build/**/*.ipa)
Purpose: Deploy iOS IPA to Firebase App Distribution
Inputs:
ios_package_name(required)use_cocoapods(default:false)shared_module(required)- App Store Connect API parameters (same as build-ios-app)
firebase_creds(required): Base64-encoded service account JSONtester_groups(required): Firebase tester group
What it does:
- Installs Fastlane +
firebase_app_distribution,increment_build_numberplugins - Writes secrets:
secrets/AuthKey.p8secrets/match_ci_keysecrets/firebaseAppDistributionServiceCredentialsFile.json- SSH config for Match
- Calls:
bundle exec fastlane ios deploy_on_firebase- Auto-increments build number from latest Firebase release
- Builds signed IPA
- Uploads to Firebase
- Cleans up secrets
tester_groups input is ignored.
- Workaround: Set
ENV['FIREBASE_GROUPS'] - See BUGS_AND_ISSUES.md
Fastlane Lane Used:
deploy_on_firebase(line 508): Increment version, build, upload
Output Artifacts:
firebase-app-ios(IPA)
Purpose: Upload iOS build to TestFlight
Inputs:
- Same as publish-ios-on-firebase (no Firebase creds needed)
What it does:
- Writes secrets (App Store Connect API key, Match SSH key)
- Calls:
bundle exec fastlane ios beta- Gets version from Gradle (sanitized for App Store:
YYYY.M.{commitCount}) - Increments build number from latest TestFlight build
- Builds signed IPA with appstore provisioning profile
- Uploads to TestFlight with comprehensive metadata
- Gets version from Gradle (sanitized for App Store:
- Cleans up secrets
Fastlane Lane Used:
beta(line 532): Version, build, upload to TestFlight
Version Sanitization:
- Gradle:
2026.1.1-beta.0.9+abc123→ App Store:2026.1.9 - See Version Handling Guide
Output Artifacts:
testflight-app(IPA)
Purpose: Submit iOS app to App Store for review
Inputs:
- Same as TestFlight action
What it does:
- Writes secrets
- Calls:
bundle exec fastlane ios release- Gets sanitized version from Gradle
- Increments build number from TestFlight
- Updates Info.plist with privacy strings
- Builds signed IPA
- Generates release notes from conventional commits
- Uploads to App Store with metadata
- Submits for review
- Cleans up secrets
Fastlane Lane Used:
release(line 635): Version, build, upload, submit for review
Output Artifacts:
appstore-app(IPA)
Purpose: Deploy macOS app to TestFlight
Inputs:
desktop_package_name(required)- App Store Connect API parameters
mac_signing_certificate(Base64-encoded .p12)mac_signing_certificate_passwordmac_installer_certificate(Base64-encoded .p12)mac_installer_certificate_passwordmac_provisioning_profile_base64(Base64-encoded .provisionprofile)bundle_identifier(required)
What it does:
- Creates temporary keychain
- Imports signing certificates (.p12 files)
- Writes provisioning profile to
~/Library/MobileDevice/Provisioning Profiles/ - Calls:
bundle exec fastlane mac desktop_testflight - Cleans up keychain and secrets
Note: macOS uses manual certificate management (not Fastlane Match)
Purpose: Deploy macOS app to App Store
Inputs:
- Same as macOS TestFlight action
What it does:
- Same as TestFlight, but calls:
bundle exec fastlane mac desktop_release
Note: Production macOS deployment
Purpose: Build Desktop apps for Windows, macOS, Linux
Matrix Strategy:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}Inputs:
desktop_package_name(required)- Windows, macOS, Linux signing parameters (9 total secrets)
java-version(default:17)
What it does:
- Sets up Java 17
- Sets up Gradle
- Runs:
./gradlew :${{ desktop_package_name }}:packageReleaseDistributionForCurrentOS - Uploads platform-specific artifacts:
- Windows:
*.exe,*.msi(lines 72-91) - macOS:
*.dmg(lines 97-107) - Linux:
*.deb(lines 102-107)
- Windows:
Compose Desktop Gradle Task:
packageReleaseDistributionForCurrentOS(Compose Desktop plugin)
Output Artifacts:
desktop-app-windows,desktop-app-macos,desktop-app-linux
Purpose: Deploy web app to GitHub Pages
Inputs:
web_package_name(required)java-version(default:17)
What it does:
- Sets up Java 17
- Runs:
./gradlew :${{ web_package_name }}:jsBrowserDistribution - Deploys to GitHub Pages:
- Uses
peaceiris/actions-gh-pages@v4 - Publishes
build/dist/js/productionExecutable/togh-pagesbranch
- Uses
Kotlin/JS Gradle Task:
jsBrowserDistribution(Kotlin/JS plugin)
Outputs:
page_url: GitHub Pages URL
Output Artifacts:
web-app(JavaScript distribution)
Purpose: Run code quality checks
Inputs:
java-version(default:17)
What it does:
- Sets up Java 17
- Sets up Gradle
- Runs checks sequentially:
./gradlew check -p build-logic # Build logic checks ./gradlew spotlessCheck # Code formatting ./gradlew detekt # Kotlin linting ./gradlew dependencyGuard # Dependency validation
- Uploads Detekt reports as artifacts
Tools:
- Spotless: Enforces code formatting (Kotlin, KTS files)
- Detekt: Kotlin static analysis and linting
- Dependency Guard: Validates dependency changes
Output Artifacts:
detekt-reports(Detekt HTML/XML reports)
| Category | Count | Secrets |
|---|---|---|
| Android | 8 | ORIGINAL_KEYSTORE_FILE, ORIGINAL_KEYSTORE_FILE_PASSWORD, ORIGINAL_KEYSTORE_ALIAS, ORIGINAL_KEYSTORE_ALIAS_PASSWORD, UPLOAD_KEYSTORE_FILE, UPLOAD_KEYSTORE_FILE_PASSWORD, UPLOAD_KEYSTORE_ALIAS, UPLOAD_KEYSTORE_ALIAS_PASSWORD |
| Firebase | 3 | FIREBASECREDS, GOOGLESERVICES |
| Play Store | 1 | PLAYSTORECREDS |
| iOS | 5 | APPSTORE_KEY_ID, APPSTORE_ISSUER_ID, APPSTORE_AUTH_KEY, MATCH_PASSWORD, MATCH_SSH_PRIVATE_KEY |
| Desktop | 9 | WINDOWS_SIGNING_KEY, WINDOWS_SIGNING_PASSWORD, WINDOWS_SIGNING_CERTIFICATE, MACOS_SIGNING_KEY, MACOS_SIGNING_PASSWORD, MACOS_SIGNING_CERTIFICATE, LINUX_SIGNING_KEY, LINUX_SIGNING_PASSWORD, LINUX_SIGNING_CERTIFICATE |
| Shared | 1 | GITHUB_TOKEN (auto-provided) |
Total: 30+ secrets
Use keystore-manager.sh to encode secrets:
File in secrets/ |
GitHub Secret Name | Used By |
|---|---|---|
firebaseAppDistributionServiceCredentialsFile.json |
FIREBASECREDS |
Android/iOS Firebase publish |
google-services.json |
GOOGLESERVICES |
Android build/deploy |
playStorePublishServiceCredentialsFile.json |
PLAYSTORECREDS |
Play Store publish/promote |
Auth_key.p8 |
APPSTORE_AUTH_KEY |
iOS build/deploy |
match_ci_key |
MATCH_SSH_PRIVATE_KEY |
iOS build/deploy (Match access) |
Commands:
# Encode all secrets for GitHub Actions
./keystore-manager.sh encode-secrets
# Add secrets to GitHub repository (requires gh CLI)
./keystore-manager.sh add
# View current secrets
./keystore-manager.sh viewSee Secrets Management Guide for complete reference.
Cause: Missing GitHub secret
Fix:
# Check what secrets are configured
gh secret list
# Add missing secret
./keystore-manager.sh add # Interactive modeCause: tester_groups input is ignored (known bug)
Fix: Set environment variable in workflow:
env:
FIREBASE_GROUPS: "my-tester-group"Possible causes:
- Match SSH key not configured
- Match password incorrect
- Match repository empty
Fix:
# Run iOS setup wizard
./scripts/setup_ios_complete.sh
# Verify Match configuration
./scripts/verify_ios_deployment.shCause: Version generation from Gradle → Firebase → App Store requires sanitization
Fix: Fastlane automatically sanitizes versions. See Version Handling Guide.
Cause: Beta track empty (known issue - no pre-flight validation)
Fix:
- Check Play Console for beta releases
- Deploy to beta first:
release_type: betain multi-platform workflow - Wait for beta review to complete
- Then promote to production
Cause: Platform-specific signing issues or missing dependencies
Check:
- Signing certificates are valid for target platform
- Compose Desktop version supports target OS
- Required native dependencies installed on runner
Cause: GITHUB_TOKEN lacks Pages write permissions
Fix:
- Go to Settings → Actions → General
- Workflow permissions → "Read and write permissions"
- Re-run workflow
-
Check Action Logs:
- GitHub Actions → Failed workflow → Expand failed step
- Look for Fastlane errors, Gradle failures, or secret inflation issues
-
Validate Locally:
# Test Fastlane lanes locally bundle exec fastlane android deployInternal --verbose bundle exec fastlane ios beta --verbose
-
Dry Run:
- Use
skip_submission: truein Fastlane lanes to test build without uploading
- Use
-
Secret Validation:
# Verify secrets are properly encoded ./keystore-manager.sh view # Re-encode if corrupted ./keystore-manager.sh encode-secrets
Need more help?