ci: Configured GitHub Workflow For Play Store & FAD Publishing (#2738)

This commit is contained in:
Sk Niyaj Ali 2025-01-12 01:28:54 +05:30 committed by GitHub
parent d3d673ffda
commit 466b24514c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
47 changed files with 833 additions and 593 deletions

View File

@ -1 +1 @@
### We're moving towards to use [Jira](https://mifosforge.jira.com/jira/software/c/projects/MM/issues/) for issue tracking, and Join our [slack](https://join.slack.com/t/mifos/shared_invite/zt-2wvi9t82t-DuSBdqdQVOY9fsqsLjkKPA) channel `mifos-mobile` to discuss all things about Mifos Mobile development. and do not cross post your messages in multiple channels. ask your question in the appropriate channel.
#### We are transitioning to [Jira](https://mifosforge.jira.com/jira/software/c/projects/MM/issues/) for issue tracking. Additionally, we encourage you to join our Slack channel #mifos-mobile to discuss all aspects of Mifos Mobile development. Please ensure that your messages are not cross-posted in multiple channels. Kindly direct your questions to the most appropriate channel for efficient communication.

View File

@ -1,56 +0,0 @@
name: 'Create Release Notes'
description: 'Creates the current releases release notes'
inputs:
tag-name:
description: 'Name of the tag that will be used for this release'
required: true
gh-token:
description: 'The GitHub token used to get details from the API'
required: true
runs:
using: 'composite'
steps:
- name: Get Previous Release Tag
uses: actions/github-script@v7
id: latest-release-tag
with:
github-token: ${{ inputs.gh-token }}
result-encoding: string
script: |
const { data } = await github.rest.repos.getLatestRelease({
owner: context.repo.owner,
repo: context.repo.repo,
})
return data.tag_name
- name: Get Generated Release Notes
uses: actions/github-script@v7
id: generate-notes
with:
github-token: ${{ inputs.gh-token }}
result-encoding: string
script: |
const { data } = await github.rest.repos.generateReleaseNotes({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: '${{ inputs.tag-name }}',
target_commitish: 'development',
previous_tag_name: '${{ steps.latest-release-tag.outputs.result }}',
})
return data.body.replaceAll('`', '\'').replaceAll('"', '\'')
- name: Generate Release Notes
id: version-generator
shell: bash
run: |
mkdir -p ./androidApp/build/outputs/
echo "Previous Release Tag:"
echo "${{ steps.latest-release-tag.outputs.result }}"
echo "Full Changelog:"
CHANGELOG="${{ steps.generate-notes.outputs.result }}"
echo -e "$CHANGELOG"
printf "$CHANGELOG" > ./androidApp/build/outputs/changelogGithub
echo "Beta Changelog:"
git log --format="* %s" HEAD^..HEAD
git log --format="* %s" HEAD^..HEAD > ./androidApp/build/outputs/changelogBeta

View File

@ -1,54 +0,0 @@
name: 'Create Release Numbers'
description: 'Creates the current release number based on Gradle or Git history'
outputs:
version-code:
description: 'The numeric app version'
value: ${{ steps.version-generator.outputs.version-code }}
version:
description: 'The app version'
value: ${{ steps.version-generator.outputs.version }}
runs:
using: 'composite'
steps:
- name: Set Build Number and Version
id: version-generator
shell: bash
run: |
# Try to get version from Gradle
./gradlew versionFile
GRADLE_VERSION=$(cat version.txt)
if [ "$GRADLE_VERSION" = "unspecified" ] || [ -z "$GRADLE_VERSION" ]; then
echo "Gradle version is unspecified or empty. Generating version from Git."
# Get the latest tag
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
# Extract major, minor, patch from the tag
IFS='.' read -r MAJOR MINOR PATCH <<< "${LATEST_TAG#v}"
# Count commits since the last tag
COMMITS_SINCE_TAG=$(git rev-list ${LATEST_TAG}..HEAD --count)
# Calculate new patch version
NEW_PATCH=$((PATCH + COMMITS_SINCE_TAG))
# Generate version name
VERSION="${MAJOR}.${MINOR}.${NEW_PATCH}"
else
echo "Using Gradle-generated version."
VERSION=$GRADLE_VERSION
fi
# Calculate version code
COMMITS=$(git rev-list --count HEAD)
TAGS=$(git tag | grep -v beta | wc -l)
VC=$((((COMMITS+TAGS) * 3) << 1))
echo "Version: $VERSION"
echo "Number of Commits: $COMMITS"
echo "Number of Tags: $TAGS"
echo "Version Code: $VC"
echo "version-code=$VC" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT

View File

@ -1,40 +0,0 @@
name: 'Inflate Secrets'
description: 'Inflates the secret values into the appropriate files'
inputs:
keystore:
description: 'The keystore to inflate'
required: true
google-services:
description: 'The google-services.json to inflate'
required: true
playstore-creds:
description: 'The playstore credentials to inflate'
required: true
runs:
using: 'composite'
steps:
- name: Mock debug google-services.json
shell: bash
run: |
cp .github/mock-google-services.json androidApp/google-services.json
- name: Inflate release_keystore.keystore
shell: bash
env:
KEYSTORE: ${{ inputs.keystore }}
run: |
echo $KEYSTORE | base64 --decode > androidApp/release_keystore.keystore
- name: Inflate google-services.json
shell: bash
env:
GOOGLE_SERVICES: ${{ inputs.google-services }}
run: |
echo $GOOGLE_SERVICES > androidApp/google-services.json
- name: Inflate playStorePublishServiceCredentialsFile.json
shell: bash
env:
CREDS: ${{ inputs.playstore-creds }}
run: |
echo $CREDS > androidApp/playStorePublishServiceCredentialsFile.json

View File

@ -1,55 +0,0 @@
{
"project_info": {
"project_number": "project_number",
"firebase_url": "firebase_url",
"project_id": "project_id",
"storage_bucket": "storage_bucket"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "mobilesdk_app_id",
"android_client_info": {
"package_name": "org.mifos.mobile"
}
},
"oauth_client": [
{
"client_id": "client_id",
"client_type": 1,
"android_info": {
"package_name": "org.mifos.mobile",
"certificate_hash": "2f8ce9c728acf6b9c50750b328742d5391c0b303"
}
},
{
"client_id": "client_id",
"client_type": 3
}
],
"api_key": [
{
"current_key": "current_key"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 2,
"other_platform_oauth_client": [
{
"client_id": "client_id",
"client_type": 3
}
]
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}

96
.github/workflows/android-release.yml vendored Normal file
View File

@ -0,0 +1,96 @@
# GitHub Actions Workflow for Kotlin Android Application Deployment
#
# OVERVIEW:
# This workflow supports building and publishing applications across multiple platforms:
# - Android (APK/AAB)
#
# PREREQUISITES:
# Ensure your project is configured with:
# - Gradle build system
# - Kotlin Multiplatform Project with Android, iOS, Desktop, and Web modules
# - Fastlane for deployment automation
# - Separate modules/package names for each platform
#
# REQUIRED SECRETS:
# Configure the following secrets in GitHub repository settings:
# - ORIGINAL_KEYSTORE_FILE: Base64 encoded Android release keystore
# - ORIGINAL_KEYSTORE_FILE_PASSWORD: Keystore password
# - ORIGINAL_KEYSTORE_ALIAS: Keystore alias
# - ORIGINAL_KEYSTORE_ALIAS_PASSWORD: Keystore alias password
# - UPLOAD_KEYSTORE_FILE: Base64 encoded Android release keystore
# - UPLOAD_KEYSTORE_FILE_PASSWORD: Keystore password
# - UPLOAD_KEYSTORE_ALIAS: Keystore alias
# - UPLOAD_KEYSTORE_ALIAS_PASSWORD: Keystore alias password
# - GOOGLESERVICES: Google Services configuration JSON
# - PLAYSTORECREDS: Play Store service account credentials
# - FIREBASECREDS: Firebase distribution credentials
# WORKFLOW INPUTS:
# - release_type: 'internal' (default) or 'beta'
# - target_branch: Branch to use for release (default: 'dev')
# - android_package_name: Name of Android module
# USAGE:
# 1. Ensure all required secrets are configured
# 2. Customize package names in workflow inputs
# 3. Toggle platform-specific publishing flags
# 4. Trigger workflow manually or via GitHub Actions UI
# https://github.com/openMF/mifos-mobile-github-actions/blob/main/.github/workflows/android-build-and-publish.yaml
# ##############################################################################
# DON'T EDIT THIS FILE UNLESS NECESSARY #
# ##############################################################################
name: Android Build and Publish
on:
workflow_dispatch:
inputs:
release_type:
type: choice
options:
- internal
- beta
default: internal
description: Release Type
target_branch:
type: string
default: 'development'
description: 'Target branch for release'
permissions:
contents: write
id-token: write
pages: write
concurrency:
group: "reusable"
cancel-in-progress: false
jobs:
android_build_and_publish:
name: Android Build and Publish
uses: openMF/mifos-mobile-github-actions/.github/workflows/android-build-and-publish.yaml@main
with:
release_type: ${{ inputs.release_type }}
target_branch: ${{ inputs.target_branch }}
android_package_name: 'androidApp' # <-- Change this to your android package name
tester_groups: 'mifos-mobile-testers' # <-- Change this to your Firebase tester group
secrets:
original_keystore_file: ${{ secrets.ORIGINAL_KEYSTORE_FILE }}
original_keystore_file_password: ${{ secrets.ORIGINAL_KEYSTORE_FILE_PASSWORD }}
original_keystore_alias: ${{ secrets.ORIGINAL_KEYSTORE_ALIAS }}
original_keystore_alias_password: ${{ secrets.ORIGINAL_KEYSTORE_ALIAS_PASSWORD }}
upload_keystore_file: ${{ secrets.UPLOAD_KEYSTORE_FILE }}
upload_keystore_file_password: ${{ secrets.UPLOAD_KEYSTORE_FILE_PASSWORD }}
upload_keystore_alias: ${{ secrets.UPLOAD_KEYSTORE_ALIAS }}
upload_keystore_alias_password: ${{ secrets.UPLOAD_KEYSTORE_ALIAS_PASSWORD }}
google_services: ${{ secrets.GOOGLESERVICES }}
firebase_creds: ${{ secrets.FIREBASECREDS }}
playstore_creds: ${{ secrets.PLAYSTORECREDS }}
token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -71,8 +71,7 @@ jobs:
**/build/reports/detekt/detekt.md
# Dependency Guard is a tool that checks for known vulnerabilities in your dependencies
dependency_guard:
needs: setup
runs-on: ubuntu-latest
@ -109,64 +108,8 @@ jobs:
disable_globbing: true
commit_message: "🤖 Updates baselines for Dependency Guard"
tests_and_lint:
needs: setup
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 17
- name: Run tests
run: |
./gradlew testDebug :lint:test :androidApp:lintRelease :lint:lint
- name: Upload reports
if: always()
uses: actions/upload-artifact@v4
with:
name: test-and-lint-reports
path: |
**/build/reports/lint-results-*.html
**/build/test-results/test*UnitTest/**.xml
# Add `createDebugUnitTestCoverageReport` if we ever add JVM tests for prod
- name: Generate coverage reports for Debug variants (only API 30)
run: ./gradlew createDebugCombinedCoverageReport
- name: Upload test reports
if: always()
uses: actions/upload-artifact@v4
with:
name: test-reports-${{ matrix.api-level }}
path: '**/build/reports/androidTests'
- name: Display local test coverage (only API 30)
id: jacoco
uses: madrapps/jacoco-report@v1.6.1
with:
title: Combined test coverage report
min-coverage-overall: 40
min-coverage-changed-files: 60
paths: |
${{ github.workspace }}/**/build/reports/jacoco/**/*Report.xml
token: ${{ secrets.GITHUB_TOKEN }}
- name: Upload local coverage reports (XML + HTML) (only API 30)
uses: actions/upload-artifact@v4
with:
name: coverage-reports
if-no-files-found: error
compression-level: 1
overwrite: false
path: '**/build/reports/jacoco/'
build:
needs: [ checks, dependency_guard, tests_and_lint ]
needs: [ checks, dependency_guard ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

66
.github/workflows/monthly-release.yaml vendored Normal file
View File

@ -0,0 +1,66 @@
# Automated Monthly Release Versioning Workflow
# ============================================
# Purpose:
# - Automatically create consistent monthly version tags
# - Implement a calendar-based versioning strategy
# - Facilitate easy tracking of monthly releases
# Versioning Strategy:
# - Tag format: YYYY.MM.0 (e.g., 2024.01.0 for January 2024)
# - First digit: Full year
# - Second digit: Month (01-12)
# - Third digit: Patch version (starts at 0, allows for potential updates)
# Key Features:
# - Runs automatically on the first day of each month at 3:30 AM UTC
# - Can be manually triggered via workflow_dispatch
# - Uses GitHub Actions to generate tags programmatically
# - Provides a predictable and systematic versioning approach
# Prerequisites:
# - Repository configured with GitHub Actions
# - Permissions to create tags
# - Access to actions/checkout and tag creation actions
# Workflow Triggers:
# - Scheduled monthly run
# - Manual workflow dispatch
# - Callable from other workflows
# Actions Used:
# 1. actions/checkout@v4 - Checks out repository code
# 2. josStorer/get-current-time - Retrieves current timestamp
# 3. rickstaa/action-create-tag - Creates Git tags
# Example Generated Tags:
# - 2024.01.0 (January 2024 initial release)
# - 2024.02.0 (February 2024 initial release)
# - 2024.02.1 (Potential patch for February 2024)
# https://github.com/openMF/mifos-mobile-github-actions/blob/main/.github/workflows/monthly-version-tag.yaml
# ##############################################################################
# DON'T EDIT THIS FILE UNLESS NECESSARY #
# ##############################################################################
name: Tag Monthly Release
on:
# Allow manual triggering of the workflow
workflow_dispatch:
# Schedule the workflow to run monthly
schedule:
# Runs at 03:30 UTC on the first day of every month
# Cron syntax: minute hour day-of-month month day-of-week
- cron: '30 3 1 * *'
concurrency:
group: "monthly-release"
cancel-in-progress: false
jobs:
monthly_release:
name: Tag Monthly Release
uses: openMF/mifos-mobile-github-actions/.github/workflows/monthly-version-tag.yaml@main
secrets: inherit

View File

@ -1,21 +0,0 @@
name: Bump our Calendar Version
on:
workflow_dispatch:
schedule:
- cron: '30 3 1 * *'
jobs:
tag:
name: Tag Monthly Release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get Current Time
uses: josStorer/get-current-time@v2.1.2
id: current-time
- name: Bump Calendar Version
uses: rickstaa/action-create-tag@v1.7.2
with:
tag: ${{ steps.current-time.outputs.year }}.${{ steps.current-time.outputs.month }}.0

View File

@ -0,0 +1,75 @@
# GitHub Actions Workflow for Play Store Release Promotion
#
# PURPOSE:
# This workflow automates the process of promoting a beta release
# to the production track on Google Play Store.
#
# PREREQUISITES:
# 1. Fastlane setup with Android deployment configurations
# 2. Configured Fastlane lanes:
# - `promote_to_production`: Handles beta to production promotion
#
# REQUIRED CONFIGURATION:
# - Secrets:
# PLAYSTORECREDS: Google Play Store service account JSON credentials
#
# INPUTS:
# - android_package_name: Name of the Android project module
# (REQUIRED, must match your project's module structure)
#
# WORKFLOW TRIGGERS:
# - Can be called manually or triggered by other workflows
# - Typically used after beta testing and validation
#
# DEPLOYMENT PROCESS:
# 1. Checks out repository code
# 2. Sets up Ruby and Fastlane environment
# 3. Inflates Play Store credentials
# 4. Runs Fastlane lane to promote beta to production
#
# IMPORTANT NOTES:
# - Requires proper Fastlane configuration in your project
# - Ensures consistent and automated Play Store deployments
# - Configurable retry mechanism for upload stability
#
# RECOMMENDED FASTLANE LANE IMPLEMENTATION:
# ```ruby
# lane :promote_to_production do
# upload_to_play_store(
# track: 'beta',
# track_promote_to: 'production',
# json_key: './playStorePublishServiceCredentialsFile.json'
# )
# end
# ```
# https://github.com/openMF/mifos-mobile-github-actions/blob/main/.github/workflows/promote-to-production.yaml
# ##############################################################################
# DON'T EDIT THIS FILE UNLESS NECESSARY #
# ##############################################################################
name: Promote Release to Play Store
# Workflow triggers:
# 1. Manual trigger with option to publish to Play Store
# 2. Automatic trigger when a GitHub release is published
on:
workflow_dispatch:
release:
types: [ released ]
concurrency:
group: "production-deploy"
cancel-in-progress: false
permissions:
contents: write
jobs:
# Job to promote app from beta to production in Play Store
play_promote_production:
name: Promote Beta to Production Play Store
uses: openMF/mifos-mobile-github-actions/.github/workflows/promote-to-production.yaml@main
secrets:
playstore_creds: ${{ secrets.PLAYSTORECREDS }}

View File

@ -1,162 +0,0 @@
name: Internal Or Beta Release
on:
workflow_dispatch:
inputs:
release_type:
required: false
default: 'internal'
description: Please select the release type
type: choice
options:
- internal
- beta
env:
SUPPLY_UPLOAD_MAX_RETRIES: 5
jobs:
app_build:
name: Github Release
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 17
uses: actions/setup-java@v4.2.2
with:
distribution: 'temurin'
java-version: '17'
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- uses: ./.github/actions/create-release-number
name: Create Release Number
id: rel_number
- uses: ./.github/actions/inflate-secrets
name: Inflate Secrets
with:
keystore: ${{ secrets.ORIGINAL_KEYSTORE_FILE }}
google-services: ${{ secrets.GOOGLESERVICES }}
playstore-creds: ${{ secrets.PLAYSTORECREDS }}
- uses: ./.github/actions/create-release-notes
name: Create Release Notes
with:
tag-name: ${{ steps.rel_number.outputs.version }}
gh-token: ${{ secrets.GITHUB_TOKEN }}
- name: Build Release
env:
KEYSTORE_PATH: ${{ secrets.KEYSTORE_NAME }}
KEYSTORE_PASSWORD: ${{ secrets.ORIGINAL_KEYSTORE_FILE_PASSWORD }}
KEYSTORE_ALIAS: ${{ secrets.ORIGINAL_KEYSTORE_ALIAS }}
KEYSTORE_ALIAS_PASSWORD: ${{ secrets.ORIGINAL_KEYSTORE_ALIAS_PASSWORD }}
VERSION_CODE: ${{ steps.rel_number.outputs.version-code }}
run: |
./gradlew :androidApp:assembleRelease
- name: Archive Build
uses: actions/upload-artifact@v4
with:
path: ./**/*.apk
- name: Create Version File
if: github.event.inputs.release_type == 'beta'
shell: bash
env:
VERSION_CODE: ${{ steps.rel_number.outputs.version-code }}
run: |
echo $VERSION_CODE > ./androidApp/build/outputs/version_code.txt
- name: Create Github Pre-Release
if: github.event.inputs.release_type == 'beta'
uses: softprops/action-gh-release@v2.0.8
with:
tag_name: ${{ steps.rel_number.outputs.version }}
body_path: ./androidApp/build/outputs/changelogGithub
draft: false
prerelease: true
files: |
./androidApp/build/outputs/apk/release/androidApp-release.apk
./androidApp/build/outputs/version_code.txt
- name: Print `git status`
run: git status
play_publish:
name: Play Publish
runs-on: ubuntu-latest
concurrency:
group: playstore_deploy
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 17
uses: actions/setup-java@v4.2.2
with:
distribution: 'temurin'
java-version: '17'
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
- name: Install Fastlane
run: |
gem install bundler:2.2.27
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- uses: ./.github/actions/create-release-number
name: Create Release Number
id: rel_number
- uses: ./.github/actions/inflate-secrets
name: Inflate Secrets
with:
keystore: ${{ secrets.UPLOAD_KEYSTORE_FILE }}
google-services: ${{ secrets.GOOGLESERVICES }}
playstore-creds: ${{ secrets.PLAYSTORECREDS }}
- uses: ./.github/actions/create-release-notes
name: Create Release Notes
with:
tag-name: ${{ steps.rel_number.outputs.version }}
gh-token: ${{ secrets.GITHUB_TOKEN }}
- name: Build Release
env:
KEYSTORE_PATH: ${{ secrets.KEYSTORE_NAME }}
KEYSTORE_PASSWORD: ${{ secrets.UPLOAD_KEYSTORE_FILE_PASSWORD }}
KEYSTORE_ALIAS: ${{ secrets.UPLOAD_KEYSTORE_ALIAS }}
KEYSTORE_ALIAS_PASSWORD: ${{ secrets.UPLOAD_KEYSTORE_ALIAS_PASSWORD }}
VERSION_CODE: ${{ steps.rel_number.outputs.version-code }}
run: |
./gradlew :androidApp:bundleRelease
- name: Deploy to Play Store Internal
run: bundle exec fastlane android deploy_internal
- name: Promote Internal to Beta
if: github.event.inputs.release_type == 'beta'
run: bundle exec fastlane android promote_to_beta

View File

@ -1,35 +0,0 @@
name: Promote Beta to Production Play Store
on:
workflow_dispatch:
env:
SUPPLY_UPLOAD_MAX_RETRIES: 5
jobs:
play_promote_production:
name: Play Publish Production
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
- name: Install Fastlane
run: |
gem install bundler:2.2.27
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3
- uses: ./.github/actions/inflate-secrets
name: Inflate Secrets
with:
keystore: ${{ secrets.ORIGINAL_KEYSTORE_FILE }}
google-services: ${{ secrets.GOOGLESERVICES }}
playstore-creds: ${{ secrets.PLAYSTORECREDS }}
- name: Promote Beta to Production Play Store
run: bundle exec fastlane android promote_to_production

View File

@ -31,7 +31,7 @@ jobs:
github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'release_to_internal_or_beta.yml',
workflow_id: 'android-release.yml',
ref: 'development',
inputs: {
"release_type": "beta",

22
.gitignore vendored
View File

@ -9,7 +9,6 @@
.externalNativeBuild
.idea
/*.iml
.kotlin
# files for the dex VM
*.dex
@ -22,13 +21,25 @@ bin/
gen/
out/
build/
.externalNativeBuild
.cxx
iosApp/Podfile.lock
iosApp/Pods/*
iosApp/iosApp.xcworkspace/*
iosApp/iosApp.xcodeproj/*
!iosApp/iosApp.xcodeproj/project.pbxproj
mifospay-shared/mifospay-shared.podspec
# Eclipse project files
.classpath
.project
# Windows thumbnail db
.DS_Store
# IDEA/Android Studio project files, because
# the project can be imported from settings.gradle.kts
*.iml
.idea/*
!.idea/copyright
# Keep the code styles.
@ -37,7 +48,6 @@ build/
!/.idea/codeStyles/Project.xml
!/.idea/codeStyles/codeStyleConfig.xml
# Kotlin
.kotlin
@ -48,13 +58,17 @@ captures/
app/app.iml
app/manifest-merger-release-report.txt
release_keystore.keystore
# Exclude Google services from prod flavour
androidApp/src/prod/google-services.json
#*.keystore
version.txt
fastlane/report.xml
firebaseAppDistributionServiceCredentialsFile.json
playStorePublishServiceCredentialsFile.json
# Ruby stuff we don't care about
.bundle/
vendor/
secrets/

1
.ruby-version Normal file
View File

@ -0,0 +1 @@
3.3.5

View File

@ -10,20 +10,20 @@ GEM
artifactory (3.0.17)
atomos (0.1.3)
aws-eventstream (1.3.0)
aws-partitions (1.979.0)
aws-sdk-core (3.209.1)
aws-partitions (1.1035.0)
aws-sdk-core (3.215.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.651.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.94.0)
aws-sdk-core (~> 3, >= 3.207.0)
aws-sdk-kms (1.96.0)
aws-sdk-core (~> 3, >= 3.210.0)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.166.0)
aws-sdk-core (~> 3, >= 3.207.0)
aws-sdk-s3 (1.177.0)
aws-sdk-core (~> 3, >= 3.210.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
aws-sigv4 (1.10.0)
aws-sigv4 (1.11.0)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
base64 (0.2.0)
@ -38,7 +38,7 @@ GEM
domain_name (0.6.20240107)
dotenv (2.8.1)
emoji_regex (3.2.3)
excon (0.111.0)
excon (0.112.0)
faraday (1.10.4)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
@ -58,8 +58,8 @@ GEM
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-multipart (1.1.0)
multipart-post (~> 2.0)
faraday-net_http (1.0.2)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
@ -67,8 +67,8 @@ GEM
faraday-retry (1.0.3)
faraday_middleware (1.2.1)
faraday (~> 1.0)
fastimage (2.3.1)
fastlane (2.222.0)
fastimage (2.4.0)
fastlane (2.226.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
@ -84,6 +84,7 @@ GEM
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 1.0)
fastimage (>= 2.1.0, < 3.0.0)
fastlane-sirp (>= 1.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
@ -107,8 +108,14 @@ GEM
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty (~> 0.4.0)
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
fastlane-plugin-firebase_app_distribution (0.10.0)
google-apis-firebaseappdistribution_v1 (~> 0.3.0)
google-apis-firebaseappdistribution_v1alpha (~> 0.2.0)
fastlane-plugin-increment_build_number (0.0.4)
fastlane-sirp (1.0.0)
sysrandom (~> 1.0)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.54.0)
google-apis-core (>= 0.11.0, < 2.a)
@ -120,6 +127,10 @@ GEM
representable (~> 3.0)
retriable (>= 2.0, < 4.a)
rexml
google-apis-firebaseappdistribution_v1 (0.3.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-firebaseappdistribution_v1alpha (0.2.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-iamcredentials_v1 (0.17.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-playcustomapp_v1 (0.13.0)
@ -147,23 +158,23 @@ GEM
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
highline (2.0.3)
http-cookie (1.0.7)
http-cookie (1.0.8)
domain_name (~> 0.5)
httpclient (2.8.3)
jmespath (1.6.2)
json (2.7.2)
jwt (2.9.1)
json (2.9.1)
jwt (2.10.1)
base64
mini_magick (4.13.2)
mini_mime (1.1.5)
multi_json (1.15.0)
multipart-post (2.4.1)
nanaimo (0.3.0)
nanaimo (0.4.0)
naturally (2.2.1)
nkf (0.2.0)
optparse (0.5.0)
optparse (0.6.0)
os (1.1.4)
plist (3.7.1)
plist (3.7.2)
public_suffix (6.0.1)
rake (13.2.1)
representable (3.2.0)
@ -171,10 +182,10 @@ GEM
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.3.7)
rouge (2.0.7)
rexml (3.4.0)
rouge (3.28.0)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
rubyzip (2.4.1)
security (0.1.5)
signet (0.19.0)
addressable (~> 2.8)
@ -184,6 +195,7 @@ GEM
simctl (1.6.10)
CFPropertyList
naturally
sysrandom (1.0.5)
terminal-notifier (2.0.0)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
@ -195,25 +207,26 @@ GEM
uber (0.1.0)
unicode-display_width (2.6.0)
word_wrap (1.0.0)
xcodeproj (1.25.0)
xcodeproj (1.27.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.3.0)
rexml (>= 3.3.2, < 4.0)
xcpretty (0.3.0)
rouge (~> 2.0.7)
nanaimo (~> 0.4.0)
rexml (>= 3.3.6, < 4.0)
xcpretty (0.4.0)
rouge (~> 3.28.0)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
arm64-darwin-22
ruby
x64-mingw-ucrt
x86_64-linux
DEPENDENCIES
fastlane
fastlane-plugin-firebase_app_distribution
fastlane-plugin-increment_build_number
BUNDLED WITH
2.5.18

View File

@ -35,7 +35,7 @@ android {
signingConfigs {
create("release") {
storeFile = file(System.getenv("KEYSTORE_PATH") ?: "debug_keystore.jks")
storeFile = file(System.getenv("KEYSTORE_PATH") ?: "../keystores/release_keystore.keystore")
storePassword = System.getenv("KEYSTORE_PASSWORD") ?: "mifos1234"
keyAlias = System.getenv("KEYSTORE_ALIAS") ?: "mifos-mobile"
keyPassword = System.getenv("KEYSTORE_ALIAS_PASSWORD") ?: "mifos1234"
@ -63,6 +63,17 @@ android {
compose = true
buildConfig = true
}
lint {
xmlReport = true
checkDependencies = true
abortOnError = false
// Disable this rule until we ship the libraries to some maven.
disable += "ResourceName"
baseline = File("lint-baseline.xml")
explainIssues = true
htmlReport = true
}
}
dependencyGuard {

View File

@ -1,54 +1,62 @@
{
"project_info": {
"project_number": "622027757397",
"firebase_url": "https://mifosmaps.firebaseio.com",
"project_id": "mifosmaps",
"storage_bucket": "mifosmaps.appspot.com"
"project_number": "project_number",
"firebase_url": "firebase_url",
"project_id": "project_id",
"storage_bucket": "storage_bucket"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:622027757397:android:29f731fdcd3a65a6",
"mobilesdk_app_id": "mobilesdk_app_id",
"android_client_info": {
"package_name": "org.mifos.mobile"
}
},
"api_key": [
{
"current_key": "current_key"
}
]
},
{
"client_info": {
"mobilesdk_app_id": "mobilesdk_app_id",
"android_client_info": {
"package_name": "org.mifos.mobile.demo.debug"
}
},
"api_key": [
{
"current_key": "current_key"
}
]
},
{
"client_info": {
"mobilesdk_app_id": "mobilesdk_app_id",
"android_client_info": {
"package_name": "org.mifos.mobile.demo"
}
},
"oauth_client": [
{
"client_id": "622027757397-ju1gahmo27fjfc39v5drlnn3838rleiq.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "org.mifos.mobile",
"certificate_hash": "2f8ce9c728acf6b9c50750b328742d5391c0b303"
}
},
{
"client_id": "622027757397-k76e8mb3s75ktuj5c011ng0r7mnmbgkr.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyCsoeQjAaAum-1VFKJL9vxOhGN8E9s0Plw"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 2,
"other_platform_oauth_client": [
{
"client_id": "622027757397-o2k3acan0jdhf7t0uddd9o4fd72bedda.apps.googleusercontent.com",
"client_type": 3
"current_key": "current_key"
}
]
},
"ads_service": {
"status": 2
{
"client_info": {
"mobilesdk_app_id": "mobilesdk_app_id",
"android_client_info": {
"package_name": "org.mifos.mobile.debug"
}
},
"api_key": [
{
"current_key": "current_key"
}
]
}
],
"configuration_version": "1"

View File

@ -17,22 +17,26 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="com.google.android.gms.permission.AD_ID" tools:node="remove"/>
<uses-permission
android:name="com.google.android.gms.permission.AD_ID"
tools:node="remove" />
<application
android:name=".MifosSelfServiceApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/feature_about_app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MifosSplash">
<activity
@ -56,6 +60,7 @@
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="@string/google_maps_key" />
<activity
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
android:configChanges="orientation|screenSize"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2024 Mifos Initiative
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file,
You can obtain one at https://mozilla.org/MPL/2.0/.
See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<color android:color="@color/white" />
</item>
<item
android:width="160dp"
android:height="160dp"
android:drawable="@drawable/mifos_splash_screen_logo"
android:gravity="center" />
</layer-list>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2024 Mifos Initiative
Copyright 2025 Mifos Initiative
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file,

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2024 Mifos Initiative
Copyright 2025 Mifos Initiative
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2024 Mifos Initiative
Copyright 2025 Mifos Initiative
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file,
@ -9,5 +9,5 @@
See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
<resources>
<color name="ic_launcher_background">#F6F8F7</color>
<color name="ic_launcher_background">#450951</color>
</resources>

View File

@ -1,2 +1,2 @@
json_key_file("androidApp/playStorePublishServiceCredentialsFile.json")
json_key_file("secrets/playStorePublishServiceCredentialsFile.json")
package_name("org.mifos.mobile") # e.g. org.mifos.mobile

View File

@ -1,11 +1,90 @@
default_platform(:android)
platform :android do
desc "Assemble debug APKs."
lane :assembleDebugApks do |options|
gradle(
tasks: ["assembleDebug"],
)
end
desc "Assemble Release APK"
lane :assembleReleaseApks do |options|
options[:storeFile] ||= "release_keystore.keystore"
options[:storePassword] ||= "mifos1234"
options[:keyAlias] ||= "mifos-mobile"
options[:keyPassword] ||= "mifos1234"
# Generate version
generateVersion = generateVersion()
buildAndSignApp(
taskName: "assemble",
buildType: "Release",
storeFile: options[:storeFile],
storePassword: options[:storePassword],
keyAlias: options[:keyAlias],
keyPassword: options[:keyPassword],
)
end
desc "Bundle Play Store release"
lane :bundlePlayStoreRelease do |options|
options[:storeFile] ||= "release_keystore.keystore"
options[:storePassword] ||= "mifos1234"
options[:keyAlias] ||= "mifos-mobile"
options[:keyPassword] ||= "mifos1234"
# Generate version
generateVersion = generateVersion()
# 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: "bundle",
buildType: "Release",
storeFile: options[:storeFile],
storePassword: options[:storePassword],
keyAlias: options[:keyAlias],
keyPassword: options[:keyPassword],
)
end
desc "Publish Release Play Store artifacts to Firebase App Distribution"
lane :deploy_on_firebase do |options|
options[:apkFile] ||= "androidApp/build/outputs/apk/prod/release/androidApp-prod-release.apk"
options[:serviceCredsFile] ||= "secrets/firebaseAppDistributionServiceCredentialsFile.json"
options[:groups] ||= "mifos-mobile-testers"
# Generate Release Note
releaseNotes = generateFullReleaseNote()
firebase_app_distribution(
app: "1:728434912738:android:d853a78f14af0c381a1dbb",
android_artifact_type: "APK",
android_artifact_path: options[:apkFile],
service_credentials_file: options[:serviceCredsFile],
groups: options[:groups],
release_notes: "#{releaseNotes}",
)
end
desc "Deploy internal tracks to Google Play"
lane :deploy_internal do
supply(
lane :deploy_internal do |options|
options[:aabFile] ||= "androidApp/build/outputs/bundle/prodRelease/androidApp-prod-release.aab"
upload_to_play_store(
track: 'internal',
aab: 'androidApp/build/outputs/bundle/release/androidApp-release.aab',
aab: options[:aabFile],
skip_upload_metadata: true,
skip_upload_images: true,
skip_upload_screenshots: true,
@ -14,7 +93,7 @@ platform :android do
desc "Promote internal tracks to beta on Google Play"
lane :promote_to_beta do
supply(
upload_to_play_store(
track: 'internal',
track_promote_to: 'beta',
skip_upload_changelogs: true,
@ -26,12 +105,278 @@ platform :android do
desc "Promote beta tracks to production on Google Play"
lane :promote_to_production do
supply(
upload_to_play_store(
track: 'beta',
track_promote_to: 'production',
skip_upload_changelogs: true,
sync_image_upload: true,
skip_upload_metadata: true,
skip_upload_images: true,
skip_upload_screenshots: true,
)
end
desc "Generate artifacts for the given [build] signed with the provided [keystore] and credentials."
private_lane :buildAndSignApp do |options|
# Get the project root directory
project_dir = File.expand_path('..', Dir.pwd)
# Construct the absolute path to the keystore
keystore_path = File.join(project_dir, 'keystores', options[:storeFile])
# Check if keystore exists
unless File.exist?(keystore_path)
UI.error "Keystore file not found at: #{keystore_path}"
UI.error "Please ensure the keystore file exists at the correct location"
exit 1 # Exit with error code 1
end
gradle(
task: options[:taskName],
build_type: options[:buildType],
properties: {
"android.injected.signing.store.file" => keystore_path,
"android.injected.signing.store.password" => options[:storePassword],
"android.injected.signing.key.alias" => options[:keyAlias],
"android.injected.signing.key.password" => options[:keyPassword],
},
print_command: false,
)
end
desc "Generate 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',
)
# Find highest version code
latest_code = (prod_codes + beta_codes).max || 1
new_version_code = latest_code + 1
# Generate version file
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
desc "Generate release notes"
lane :generateReleaseNotes do |options|
releaseNotes = changelog_from_git_commits(
commits_count: 1,
)
releaseNotes
end
desc "Generate release notes from specified tag or latest release tag"
lane :generateFullReleaseNote do |options|
# Platform-independent way to get the latest tag
def get_latest_tag
begin
# Try to get the latest tag without redirection
latest = `git describe --tags --abbrev=0`.strip
return latest unless latest.empty?
rescue
begin
# Alternative approach if the first one fails
latest = `git tag --sort=-creatordate`.split("\n").first
return latest unless latest.nil? || latest.empty?
rescue
return nil
end
end
nil
end
# Get the tag from options or find the latest tag
from_tag = options[:from_tag]
if from_tag
UI.message "Using specified tag: #{from_tag}"
# Verify the tag exists
unless system("git rev-parse #{from_tag}")
UI.user_error! "Tag #{from_tag} not found!"
return
end
else
from_tag = get_latest_tag
if from_tag && !from_tag.empty?
UI.message "Using latest tag: #{from_tag}"
else
UI.message "No tags found. Getting all commits..."
end
end
# Get commits since the tag
commits = if from_tag && !from_tag.empty?
`git log #{from_tag}..HEAD --pretty=format:"%B"`.split("\n")
else
`git log --pretty=format:"%B"`.split("\n")
end
# Process commits to get actual commit messages and remove Co-authored-by lines
processed_commits = []
current_commit = []
commits.each do |line|
# Skip empty lines and Co-authored-by lines
next if line.empty? || line.start_with?("Co-authored-by:")
if line.start_with?("Merge pull request")
# For merge commits, we want to get the actual commit message
next
elsif current_commit.empty? || !line.start_with?(" ")
# If it's a new commit message, store the previous one (if exists) and start a new one
processed_commits << current_commit.join(" ") unless current_commit.empty?
current_commit = [line]
else
# Continue with current commit message
current_commit << line
end
end
# Add the last commit
processed_commits << current_commit.join(" ") unless current_commit.empty?
# Remove empty strings and duplicates
processed_commits = processed_commits.reject(&:empty?).uniq
# Initialize categories
notes = {
"feat" => [], # Features
"fix" => [], # Bug fixes
"perf" => [], # Performance
"refactor" => [], # Refactoring
"style" => [], # Style
"docs" => [], # Documentation
"test" => [], # Tests
"build" => [], # Build
"ci" => [], # CI
"chore" => [], # Maintenance
"breaking" => [], # Breaking changes
"other" => [] # Other
}
# Categorize commits
processed_commits.each do |commit|
# Handle breaking changes
if commit.include?("BREAKING CHANGE:") || commit.include?("!")
notes["breaking"] << commit.sub(/^[^:]+:\s*/, "")
next
end
# Match conventional commit format
if commit =~ /^(feat|fix|perf|refactor|style|docs|test|build|ci|chore)(\(.+?\))?:/
type = $1
notes[type] << commit.sub(/^[^:]+:\s*/, "")
else
notes["other"] << commit unless commit.start_with?("Merge")
end
end
# Format release notes
sections = {
"breaking" => "💥 Breaking Changes",
"feat" => "🚀 New Features",
"fix" => "🐛 Bug Fixes",
"perf" => "⚡ Performance Improvements",
"refactor" => "♻️ Refactoring",
"style" => "💅 Style Changes",
"docs" => "📚 Documentation",
"test" => "🧪 Tests",
"build" => "📦 Build System",
"ci" => "👷 CI Changes",
"chore" => "🔧 Maintenance",
"other" => "📝 Other Changes"
}
# Build release notes
release_notes = ["# Release Notes"]
release_notes << "\nRelease date: #{Time.now.strftime('%d-%m-%Y')}"
sections.each do |type, title|
next if notes[type].empty?
release_notes << "\n## #{title}"
notes[type].each do |commit|
release_notes << "\n- #{commit}"
end
end
# Print release notes
UI.message "Generated Release Notes:"
UI.message release_notes.join("\n")
# Return the release notes string
release_notes.join("\n")
end
end
platform :ios do
desc "Build iOS application"
lane :build_ios do |options|
# Set default configuration if not provided
options[:configuration] ||= "Debug"
# automatic code signing
update_code_signing_settings(
use_automatic_signing: true,
path: "mifos-ios/iosApp.xcodeproj"
)
build_ios_app(
project: "mifos-ios/iosApp.xcodeproj",
scheme: "iosApp",
# Set configuration to debug for now
configuration: options[:configuration],
skip_codesigning: "true",
output_directory: "mifos-ios/build",
skip_archive: "true"
)
end
lane :increment_version do |options|
options[:serviceCredsFile] ||= "secrets/firebaseAppDistributionServiceCredentialsFile.json"
latest_release = firebase_app_distribution_get_latest_release(
app: "1:728434912738:ios:86a7badfaed88b841a1dbb",
service_credentials_file: options[:serviceCredsFile]
)
increment_build_number(
xcodeproj: "mifos-ios/iosApp.xcodeproj",
build_number: latest_release[:buildVersion].to_i + 1
)
end
desc "Upload iOS application to Firebase App Distribution"
lane :deploy_on_firebase do |options|
options[:serviceCredsFile] ||= "secrets/firebaseAppDistributionServiceCredentialsFile.json"
options[:groups] ||= "mifos-mobile-testers"
increment_version()
build_ios()
releaseNotes = generateReleaseNotes()
release = firebase_app_distribution(
app: "1:728434912738:ios:86a7badfaed88b841a1dbb",
service_credentials_file: options[:serviceCredsFile],
release_notes_file: "#{releaseNotes}",
groups: options[:groups]
)
end
desc "Generate release notes"
lane :generateReleaseNotes do
branchName = `git rev-parse --abbrev-ref HEAD`.chomp()
releaseNotes = changelog_from_git_commits(
commits_count: 1,
)
releaseNotes
end
end

View File

@ -1,3 +1,5 @@
# Autogenerated by fastlane
#
# Ensure this file is checked in to source control!
gem 'fastlane-plugin-firebase_app_distribution'
gem 'fastlane-plugin-increment_build_number'

View File

@ -15,6 +15,38 @@ For _fastlane_ installation instructions, see [Installing _fastlane_](https://do
## Android
### android assembleDebugApks
```sh
[bundle exec] fastlane android assembleDebugApks
```
Assemble debug APKs.
### android assembleReleaseApks
```sh
[bundle exec] fastlane android assembleReleaseApks
```
Assemble Release APK
### android bundlePlayStoreRelease
```sh
[bundle exec] fastlane android bundlePlayStoreRelease
```
Bundle Play Store release
### android deploy_on_firebase
```sh
[bundle exec] fastlane android deploy_on_firebase
```
Publish Release Play Store artifacts to Firebase App Distribution
### android deploy_internal
```sh
@ -39,6 +71,67 @@ Promote internal tracks to beta on Google Play
Promote beta tracks to production on Google Play
### android generateVersion
```sh
[bundle exec] fastlane android generateVersion
```
Generate Version
### android generateReleaseNotes
```sh
[bundle exec] fastlane android generateReleaseNotes
```
Generate release notes
### android generateFullReleaseNote
```sh
[bundle exec] fastlane android generateFullReleaseNote
```
Generate release notes from specified tag or latest release tag
----
## iOS
### ios build_ios
```sh
[bundle exec] fastlane ios build_ios
```
Build iOS application
### ios increment_version
```sh
[bundle exec] fastlane ios increment_version
```
### ios deploy_on_firebase
```sh
[bundle exec] fastlane ios deploy_on_firebase
```
Upload iOS application to Firebase App Distribution
### ios generateReleaseNotes
```sh
[bundle exec] fastlane ios generateReleaseNotes
```
Generate release notes
----
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.

View File

@ -0,0 +1,6 @@
fix: API endpoint (#2733)
* MM-102 KMP dependencies setup (#2729)
* fix: API endpoint
---------
Co-authored-by: Sk Niyaj Ali <skniyajali0@gmail.com>
Co-authored-by: Pronay Sarker <pronaycoding@gmail.com>s

View File

@ -1,3 +1,5 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.android.library)
@ -6,7 +8,11 @@ plugins {
}
kotlin {
androidTarget()
androidTarget() {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
}
}
listOf(
iosX64(),