diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index d4090147c..099f4acc2 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -1,4 +1,4 @@ -name: Workflow for master/development branches +name: Workflow for kmp-impl branch on: pull_request: diff --git a/.github/workflows/upload-demo-app-on-firebase.yaml b/.github/workflows/upload-demo-app-on-firebase.yaml new file mode 100644 index 000000000..0405c0a11 --- /dev/null +++ b/.github/workflows/upload-demo-app-on-firebase.yaml @@ -0,0 +1,40 @@ +name: Upload Demo App on Firebase + +on: + workflow_dispatch: + inputs: + label: + description: 'Run tests and upload demo app on Firebase' + required: true + default: 'firebase-test-on' + type: string + tester_groups: + description: 'Comma-separated list of tester groups' + required: true + default: 'mifos-mobile-testers' + type: string + + pull_request: + types: [ synchronize, opened, reopened, edited, closed, labeled ] + +jobs: + upload_demo_app_on_firebase: + runs-on: macos-latest + if: github.event.label.name == github.event.inputs.label + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: ☁️ Deploy Android App on Firebase + uses: openMF/kmp-android-firebase-publish-action@v1.0.0 + with: + release_type: 'demo' + android_package_name: 'androidApp' + keystore_file: ${{ secrets.ORIGINAL_KEYSTORE_FILE }} + keystore_password: ${{ secrets.ORIGINAL_KEYSTORE_FILE_PASSWORD }} + keystore_alias: ${{ secrets.ORIGINAL_KEYSTORE_ALIAS }} + keystore_alias_password: ${{ secrets.ORIGINAL_KEYSTORE_ALIAS_PASSWORD }} + google_services: ${{ secrets.GOOGLESERVICES }} + firebase_creds: ${{ secrets.FIREBASECREDS }} + tester_groups: ${{ inputs.tester_groups }} \ No newline at end of file diff --git a/androidApp/src/demo/ic_launcher-playstore.png b/androidApp/src/demo/ic_launcher-playstore.png new file mode 100644 index 000000000..b5ba435e3 Binary files /dev/null and b/androidApp/src/demo/ic_launcher-playstore.png differ diff --git a/androidApp/src/demo/res/mipmap-anydpi-v26/ic_launcher.xml b/androidApp/src/demo/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 000000000..036d09bc5 --- /dev/null +++ b/androidApp/src/demo/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/androidApp/src/demo/res/mipmap-anydpi-v26/ic_launcher_round.xml b/androidApp/src/demo/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 000000000..036d09bc5 --- /dev/null +++ b/androidApp/src/demo/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/androidApp/src/demo/res/mipmap-hdpi/ic_launcher.webp b/androidApp/src/demo/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 000000000..b66cf7614 Binary files /dev/null and b/androidApp/src/demo/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/androidApp/src/demo/res/mipmap-hdpi/ic_launcher_foreground.webp b/androidApp/src/demo/res/mipmap-hdpi/ic_launcher_foreground.webp new file mode 100644 index 000000000..e8f26bb75 Binary files /dev/null and b/androidApp/src/demo/res/mipmap-hdpi/ic_launcher_foreground.webp differ diff --git a/androidApp/src/demo/res/mipmap-hdpi/ic_launcher_round.webp b/androidApp/src/demo/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 000000000..3187a2f6c Binary files /dev/null and b/androidApp/src/demo/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/androidApp/src/demo/res/mipmap-mdpi/ic_launcher.webp b/androidApp/src/demo/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 000000000..475e2b0fe Binary files /dev/null and b/androidApp/src/demo/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/androidApp/src/demo/res/mipmap-mdpi/ic_launcher_foreground.webp b/androidApp/src/demo/res/mipmap-mdpi/ic_launcher_foreground.webp new file mode 100644 index 000000000..6d780ac33 Binary files /dev/null and b/androidApp/src/demo/res/mipmap-mdpi/ic_launcher_foreground.webp differ diff --git a/androidApp/src/demo/res/mipmap-mdpi/ic_launcher_round.webp b/androidApp/src/demo/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 000000000..03176eda3 Binary files /dev/null and b/androidApp/src/demo/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/androidApp/src/demo/res/mipmap-xhdpi/ic_launcher.webp b/androidApp/src/demo/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 000000000..0be89c424 Binary files /dev/null and b/androidApp/src/demo/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/androidApp/src/demo/res/mipmap-xhdpi/ic_launcher_foreground.webp b/androidApp/src/demo/res/mipmap-xhdpi/ic_launcher_foreground.webp new file mode 100644 index 000000000..92b3594dd Binary files /dev/null and b/androidApp/src/demo/res/mipmap-xhdpi/ic_launcher_foreground.webp differ diff --git a/androidApp/src/demo/res/mipmap-xhdpi/ic_launcher_round.webp b/androidApp/src/demo/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 000000000..c4b16e0e1 Binary files /dev/null and b/androidApp/src/demo/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/androidApp/src/demo/res/mipmap-xxhdpi/ic_launcher.webp b/androidApp/src/demo/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 000000000..dcf0f71e5 Binary files /dev/null and b/androidApp/src/demo/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/androidApp/src/demo/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/androidApp/src/demo/res/mipmap-xxhdpi/ic_launcher_foreground.webp new file mode 100644 index 000000000..9d598bb75 Binary files /dev/null and b/androidApp/src/demo/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ diff --git a/androidApp/src/demo/res/mipmap-xxhdpi/ic_launcher_round.webp b/androidApp/src/demo/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 000000000..9f31e244d Binary files /dev/null and b/androidApp/src/demo/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/androidApp/src/demo/res/mipmap-xxxhdpi/ic_launcher.webp b/androidApp/src/demo/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 000000000..bd6e6ee15 Binary files /dev/null and b/androidApp/src/demo/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/androidApp/src/demo/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/androidApp/src/demo/res/mipmap-xxxhdpi/ic_launcher_foreground.webp new file mode 100644 index 000000000..40de1e04c Binary files /dev/null and b/androidApp/src/demo/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ diff --git a/androidApp/src/demo/res/mipmap-xxxhdpi/ic_launcher_round.webp b/androidApp/src/demo/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 000000000..af2248418 Binary files /dev/null and b/androidApp/src/demo/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/androidApp/src/demo/res/values/ic_launcher_background.xml b/androidApp/src/demo/res/values/ic_launcher_background.xml new file mode 100644 index 000000000..87a07fb9f --- /dev/null +++ b/androidApp/src/demo/res/values/ic_launcher_background.xml @@ -0,0 +1,13 @@ + + + + #120A48 + \ No newline at end of file diff --git a/fastlane/FastFile b/fastlane/FastFile index 3b56927fc..7b3a4dc1b 100644 --- a/fastlane/FastFile +++ b/fastlane/FastFile @@ -16,7 +16,7 @@ platform :android do options[:keyPassword] ||= "mifos1234" # Generate version - generateVersion = generateFirebaseVersion() + generateVersion = generateVersion() buildAndSignApp( taskName: "assemble", @@ -28,8 +28,8 @@ platform :android do ) end - desc "Bundle Play Store release" - lane :bundlePlayStoreRelease do |options| + desc "Bundle Release APK" + lane :bundleReleaseApks do |options| options[:storeFile] ||= "release_keystore.keystore" options[:storePassword] ||= "mifos1234" options[:keyAlias] ||= "mifos-mobile" @@ -38,39 +38,90 @@ platform :android do # Generate version generateVersion = generateVersion() - # Generate Release Note - releaseNotes = generateFullReleaseNote() + buildAndSignApp( + taskName: "assemble", + buildType: "Release", + storeFile: options[:storeFile], + storePassword: options[:storePassword], + keyAlias: options[:keyAlias], + keyPassword: options[:keyPassword], + ) + end - # Write the generated release notes to default.txt - buildConfigPath = "metadata/android/en-US/changelogs/default.txt" + desc "Publish Release Artifacts to Firebase App Distribution" + lane :deployReleaseApkOnFirebase do |options| + options[:appId] ||= "1:728434912738:android:d853a78f14af0c381a1dbb" + options[:apkFile] ||= "androidApp/build/outputs/apk/prod/release/androidApp-prod-release.apk" + options[:serviceCredsFile] ||= "secrets/firebaseAppDistributionServiceCredentialsFile.json" + options[:groups] ||= "mifos-mobile-testers" - # Create directories if they don't exist - require 'fileutils' - FileUtils.mkdir_p(File.dirname(buildConfigPath)) + options[:storeFile] ||= "release_keystore.keystore" + options[:storePassword] ||= "mifos1234" + options[:keyAlias] ||= "mifos-mobile" + options[:keyPassword] ||= "mifos1234" - File.write(buildConfigPath, releaseNotes) + # Generate version + generateVersion = generateVersion( + platform: "firebase", + appId: options[:appId], + serviceCredsFile: options[:serviceCredsFile] + ) + + # Generate Release Note + releaseNotes = generateFullReleaseNote() buildAndSignApp( - taskName: "bundle", + taskName: "assembleProd", buildType: "Release", storeFile: options[:storeFile], storePassword: options[:storePassword], keyAlias: options[:keyAlias], keyPassword: options[:keyPassword], ) + + firebase_app_distribution( + app: options[:appId], + android_artifact_type: "APK", + android_artifact_path: options[:apkFile], + service_credentials_file: options[:serviceCredsFile], + groups: options[:groups], + release_notes: "#{releaseNotes}", + ) end - desc "Publish Release Play Store artifacts to Firebase App Distribution" - lane :deploy_on_firebase do |options| + desc "Publish Demo Artifacts to Firebase App Distribution" + lane :deployDemoApkOnFirebase do |options| + options[:appId] ||= "1:728434912738:android:7845cce9777d9cf11a1dbb" options[:apkFile] ||= "androidApp/build/outputs/apk/demo/release/androidApp-demo-release.apk" options[:serviceCredsFile] ||= "secrets/firebaseAppDistributionServiceCredentialsFile.json" options[:groups] ||= "mifos-mobile-testers" + options[:storeFile] ||= "release_keystore.keystore" + options[:storePassword] ||= "mifos1234" + options[:keyAlias] ||= "mifos-mobile" + options[:keyPassword] ||= "mifos1234" + + # Generate version with app ID + generateVersion = generateVersion( + platform: "firebase", + appId: options[:appId], + serviceCredsFile: options[:serviceCredsFile] + ) + # Generate Release Note - releaseNotes = generateFullReleaseNote() + releaseNotes = generateReleaseNote() + + buildAndSignApp( + taskName: "assembleDemo", + buildType: "Release", + storeFile: options[:storeFile], + storePassword: options[:storePassword], + keyAlias: options[:keyAlias], + keyPassword: options[:keyPassword], + ) firebase_app_distribution( - app: "1:728434912738:android:d853a78f14af0c381a1dbb", + app: options[:appId], android_artifact_type: "APK", android_artifact_path: options[:apkFile], service_credentials_file: options[:serviceCredsFile], @@ -80,8 +131,39 @@ platform :android do end desc "Deploy internal tracks to Google Play" - lane :deploy_internal do |options| + lane :deployInternal do |options| options[:aabFile] ||= "androidApp/build/outputs/bundle/prodRelease/androidApp-prod-release.aab" + options[:storeFile] ||= "release_keystore.keystore" + options[:storePassword] ||= "mifos1234" + options[:keyAlias] ||= "mifos-mobile" + options[:keyPassword] ||= "mifos1234" + + # Generate version + generateVersion = generateVersion( + platform: "playstore" + ) + + # Generate Release Note + releaseNotes = generateFullReleaseNote() + + # Write the generated release notes to default.txt + buildConfigPath = "metadata/android/en-US/changelogs/default.txt" + + # Create directories if they don't exist + require 'fileutils' + FileUtils.mkdir_p(File.dirname(buildConfigPath)) + + File.write(buildConfigPath, releaseNotes) + + buildAndSignApp( + taskName: "bundleProd", + buildType: "Release", + storeFile: options[:storeFile], + storePassword: options[:storePassword], + keyAlias: options[:keyAlias], + keyPassword: options[:keyPassword], + ) + upload_to_play_store( track: 'internal', aab: options[:aabFile], @@ -92,7 +174,7 @@ platform :android do end desc "Promote internal tracks to beta on Google Play" - lane :promote_to_beta do + lane :promoteToBeta do upload_to_play_store( track: 'internal', track_promote_to: 'beta', @@ -143,83 +225,75 @@ platform :android do ) end - desc "Generate Play Store Version" - lane :generateVersion do - # Get current version codes from both production and beta - prod_codes = google_play_track_version_codes( - track: 'production', - ) - beta_codes = google_play_track_version_codes( - track: 'beta', - ) + desc "Generate Version for different platforms" + lane :generateVersion do |options| + # Default to 'git' if no platform specified + platform = (options[:platform] || 'git').downcase - # Find highest version code - latest_code = (prod_codes + beta_codes).max || 1 - new_version_code = latest_code + 1 - - # Generate version file + # Generate version file for all platforms gradle(tasks: ["versionFile"]) - # Set version from file - ENV['VERSION'] = File.read("../version.txt").strip - - # Set it as environment variable or use directly - ENV['VERSION_CODE'] = new_version_code.to_s - - UI.success("Set VERSION=#{ENV['VERSION']} VERSION_CODE=#{ENV['VERSION_CODE']}") - end + # Set version from file with fallback + ENV['VERSION'] = File.read("../version.txt").strip rescue "1.0.0" - desc "Generate Firebase Version" - lane :generateFirebaseVersion do |options| - options[:serviceCredsFile] ||= "secrets/firebaseAppDistributionServiceCredentialsFile.json" - - begin - # Get latest release from Firebase App Distribution - latest_release = firebase_app_distribution_get_latest_release( - app: "1:728434912738:android:d853a78f14af0c381a1dbb", - service_credentials_file: options[:serviceCredsFile] + case platform + when 'playstore' + # Get current version codes from both production and beta + prod_codes = google_play_track_version_codes( + track: 'production', + ) + beta_codes = google_play_track_version_codes( + track: 'beta', ) - # Extract the build version from latest release - latest_build_version = if latest_release - latest_release[:buildVersion].to_i - else - 1 # Default to 0 if no releases found - end - - # Increment the build version - new_build_version = latest_build_version + 1 + # Find highest version code + latest_code = (prod_codes + beta_codes).max || 1 + ENV['VERSION_CODE'] = (latest_code + 1).to_s - # Generate version file - gradle(task: "versionFile") + when 'firebase' + service_creds = options[:serviceCredsFile] || "secrets/firebaseAppDistributionServiceCredentialsFile.json" + app_id = options[:appId] || "1:728434912738:android:d853a78f14af0c381a1dbb" - # Set version from file - version = File.read("../version.txt").strip rescue "1.0.0" # Default version if file not found + begin + # Get latest release from Firebase App Distribution + latest_release = firebase_app_distribution_get_latest_release( + app: app_id, + service_credentials_file: service_creds + ) + + # Extract and increment the build version + latest_build_version = latest_release ? latest_release[:buildVersion].to_i : 0 + ENV['VERSION_CODE'] = (latest_build_version + 1).to_s + + rescue => e + UI.error("Error generating Firebase version: #{e.message}") + UI.error(e.backtrace.join("\n")) + raise e + end - # Set environment variables - ENV['VERSION'] = version - ENV['VERSION_CODE'] = new_build_version.to_s + when 'git' + # Calculate version code from git history + commit_count = `git rev-list --count HEAD`.to_i + tag_count = `git tag | grep -v beta | wc -l`.to_i + ENV['VERSION_CODE'] = (commit_count << 1).to_s - # Output the results - UI.success("Latest Firebase build version: #{latest_build_version}") - UI.success("New build version: #{new_build_version}") - UI.success("Set VERSION=#{ENV['VERSION']} VERSION_CODE=#{ENV['VERSION_CODE']}") + else + UI.user_error!("Unsupported platform: #{platform}. Supported platforms are: playstore, firebase, git") + end - # Return the values for potential further use - { - version: ENV['VERSION'], - version_code: ENV['VERSION_CODE'] - } + # Output the results + UI.success("Generated version for #{platform}") + UI.success("Set VERSION=#{ENV['VERSION']} VERSION_CODE=#{ENV['VERSION_CODE']}") - rescue => e - UI.error("Error generating Firebase version: #{e.message}") - UI.error(e.backtrace.join("\n")) - raise e - end + # Return the values for potential further use + { + version: ENV['VERSION'], + version_code: ENV['VERSION_CODE'] + } end desc "Generate release notes" - lane :generateReleaseNotes do |options| + lane :generateReleaseNote do |options| releaseNotes = changelog_from_git_commits( commits_count: 1, ) @@ -247,7 +321,7 @@ platform :android do end # Get the tag from options or find the latest tag - from_tag = options[:from_tag] + from_tag = options[:fromTag] if from_tag UI.message "Using specified tag: #{from_tag}" # Verify the tag exists @@ -410,7 +484,7 @@ platform :ios do increment_version() build_ios() - releaseNotes = generateReleaseNotes() + releaseNotes = generateReleaseNote() release = firebase_app_distribution( app: "1:728434912738:ios:86a7badfaed88b841a1dbb", service_credentials_file: options[:serviceCredsFile], @@ -421,7 +495,7 @@ platform :ios do end desc "Generate release notes" - lane :generateReleaseNotes do + lane :generateReleaseNote do branchName = `git rev-parse --abbrev-ref HEAD`.chomp() releaseNotes = changelog_from_git_commits( commits_count: 1, diff --git a/fastlane/README.md b/fastlane/README.md index 0c2644ecf..ad2f4e1bb 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -31,34 +31,42 @@ Assemble debug APKs. Assemble Release APK -### android bundlePlayStoreRelease +### android bundleReleaseApks ```sh -[bundle exec] fastlane android bundlePlayStoreRelease +[bundle exec] fastlane android bundleReleaseApks ``` -Bundle Play Store release +Bundle Release APK -### android deploy_on_firebase +### android deployReleaseApkOnFirebase ```sh -[bundle exec] fastlane android deploy_on_firebase +[bundle exec] fastlane android deployReleaseApkOnFirebase ``` -Publish Release Play Store artifacts to Firebase App Distribution +Publish Release Artifacts to Firebase App Distribution -### android deploy_internal +### android deployDemoApkOnFirebase ```sh -[bundle exec] fastlane android deploy_internal +[bundle exec] fastlane android deployDemoApkOnFirebase +``` + +Publish Demo Artifacts to Firebase App Distribution + +### android deployOnInternal + +```sh +[bundle exec] fastlane android deployOnInternal ``` Deploy internal tracks to Google Play -### android promote_to_beta +### android promoteToBeta ```sh -[bundle exec] fastlane android promote_to_beta +[bundle exec] fastlane android promoteToBeta ``` Promote internal tracks to beta on Google Play @@ -77,20 +85,12 @@ Promote beta tracks to production on Google Play [bundle exec] fastlane android generateVersion ``` -Generate Play Store Version - -### android generateFirebaseVersion - -```sh -[bundle exec] fastlane android generateFirebaseVersion -``` - -Generate Firebase Version +Generate Version for different platforms -### android generateReleaseNotes +### android generateReleaseNote ```sh -[bundle exec] fastlane android generateReleaseNotes +[bundle exec] fastlane android generateReleaseNote ``` Generate release notes @@ -132,10 +132,10 @@ Build iOS application Upload iOS application to Firebase App Distribution -### ios generateReleaseNotes +### ios generateReleaseNote ```sh -[bundle exec] fastlane ios generateReleaseNotes +[bundle exec] fastlane ios generateReleaseNote ``` Generate release notes