diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 024a3556e..5c181d333 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -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.
\ No newline at end of file
+#### 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.
\ No newline at end of file
diff --git a/.github/actions/create-release-notes/action.yml b/.github/actions/create-release-notes/action.yml
deleted file mode 100644
index 6cc1e7464..000000000
--- a/.github/actions/create-release-notes/action.yml
+++ /dev/null
@@ -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
diff --git a/.github/actions/create-release-number/action.yml b/.github/actions/create-release-number/action.yml
deleted file mode 100644
index 9d35f42b5..000000000
--- a/.github/actions/create-release-number/action.yml
+++ /dev/null
@@ -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
\ No newline at end of file
diff --git a/.github/actions/inflate-secrets/action.yml b/.github/actions/inflate-secrets/action.yml
deleted file mode 100644
index 5ba553c3a..000000000
--- a/.github/actions/inflate-secrets/action.yml
+++ /dev/null
@@ -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
diff --git a/.github/mock-google-services.json b/.github/mock-google-services.json
deleted file mode 100644
index 79c227c1e..000000000
--- a/.github/mock-google-services.json
+++ /dev/null
@@ -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"
-}
diff --git a/.github/workflows/android-release.yml b/.github/workflows/android-release.yml
new file mode 100644
index 000000000..bf4161e95
--- /dev/null
+++ b/.github/workflows/android-release.yml
@@ -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 }}
\ No newline at end of file
diff --git a/.github/workflows/master_dev_ci.yml b/.github/workflows/master_dev_ci.yml
index 4a9e63b96..0b28fe863 100644
--- a/.github/workflows/master_dev_ci.yml
+++ b/.github/workflows/master_dev_ci.yml
@@ -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
diff --git a/.github/workflows/monthly-release.yaml b/.github/workflows/monthly-release.yaml
new file mode 100644
index 000000000..35abad5de
--- /dev/null
+++ b/.github/workflows/monthly-release.yaml
@@ -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
\ No newline at end of file
diff --git a/.github/workflows/monthly_release.yaml b/.github/workflows/monthly_release.yaml
deleted file mode 100644
index 9f173e9ed..000000000
--- a/.github/workflows/monthly_release.yaml
+++ /dev/null
@@ -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
diff --git a/.github/workflows/promote-to-production.yml b/.github/workflows/promote-to-production.yml
new file mode 100644
index 000000000..0ebbc4017
--- /dev/null
+++ b/.github/workflows/promote-to-production.yml
@@ -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 }}
\ No newline at end of file
diff --git a/.github/workflows/release_to_internal_or_beta.yml b/.github/workflows/release_to_internal_or_beta.yml
deleted file mode 100644
index 36eba8093..000000000
--- a/.github/workflows/release_to_internal_or_beta.yml
+++ /dev/null
@@ -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
diff --git a/.github/workflows/release_to_production.yml b/.github/workflows/release_to_production.yml
deleted file mode 100644
index 51a0cbafe..000000000
--- a/.github/workflows/release_to_production.yml
+++ /dev/null
@@ -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
diff --git a/.github/workflows/weekly_release.yaml b/.github/workflows/tag-weekly-release.yaml
similarity index 93%
rename from .github/workflows/weekly_release.yaml
rename to .github/workflows/tag-weekly-release.yaml
index d314a1313..8e9d1d62f 100644
--- a/.github/workflows/weekly_release.yaml
+++ b/.github/workflows/tag-weekly-release.yaml
@@ -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",
diff --git a/.gitignore b/.gitignore
index 225608487..a3f39954a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
-version.txt
+#*.keystore
+version.txt
+fastlane/report.xml
firebaseAppDistributionServiceCredentialsFile.json
playStorePublishServiceCredentialsFile.json
# Ruby stuff we don't care about
.bundle/
vendor/
+secrets/
\ No newline at end of file
diff --git a/.ruby-version b/.ruby-version
new file mode 100644
index 000000000..0163af7e8
--- /dev/null
+++ b/.ruby-version
@@ -0,0 +1 @@
+3.3.5
\ No newline at end of file
diff --git a/Gemfile.lock b/Gemfile.lock
index f625ab70e..1a2689ac2 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -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
diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts
index c74ac18df..a9a6fb33f 100644
--- a/androidApp/build.gradle.kts
+++ b/androidApp/build.gradle.kts
@@ -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 {
diff --git a/androidApp/google-services.json b/androidApp/google-services.json
index bf28d5179..8c353d3e9 100644
--- a/androidApp/google-services.json
+++ b/androidApp/google-services.json
@@ -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.demo"
+ "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"
}
},
- "oauth_client": [
+ "api_key": [
{
- "client_id": "622027757397-ju1gahmo27fjfc39v5drlnn3838rleiq.apps.googleusercontent.com",
- "client_type": 1,
- "android_info": {
- "package_name": "org.mifos.mobile",
- "certificate_hash": "2f8ce9c728acf6b9c50750b328742d5391c0b303"
- }
- },
+ "current_key": "current_key"
+ }
+ ]
+ },
+ {
+ "client_info": {
+ "mobilesdk_app_id": "mobilesdk_app_id",
+ "android_client_info": {
+ "package_name": "org.mifos.mobile.demo"
+ }
+ },
+ "api_key": [
{
- "client_id": "622027757397-k76e8mb3s75ktuj5c011ng0r7mnmbgkr.apps.googleusercontent.com",
- "client_type": 3
+ "current_key": "current_key"
}
- ],
+ ]
+ },
+ {
+ "client_info": {
+ "mobilesdk_app_id": "mobilesdk_app_id",
+ "android_client_info": {
+ "package_name": "org.mifos.mobile.debug"
+ }
+ },
"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
- }
- ]
- },
- "ads_service": {
- "status": 2
- }
- }
+ "current_key": "current_key"
+ }
+ ]
}
],
"configuration_version": "1"
diff --git a/androidApp/src/main/AndroidManifest.xml b/androidApp/src/main/AndroidManifest.xml
index a7eb8a779..20104c72a 100644
--- a/androidApp/src/main/AndroidManifest.xml
+++ b/androidApp/src/main/AndroidManifest.xml
@@ -17,22 +17,26 @@
+
-
-
+
-
+
+
-
-
- -
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
index b68a884e0..1c76cd711 100644
--- a/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -1,6 +1,6 @@
- #F6F8F7
+ #450951
\ No newline at end of file
diff --git a/fastlane/AppFile b/fastlane/AppFile
index 24e9344da..b3ba92ccd 100644
--- a/fastlane/AppFile
+++ b/fastlane/AppFile
@@ -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
\ No newline at end of file
diff --git a/fastlane/FastFile b/fastlane/FastFile
index 0734cbc16..3f979c127 100644
--- a/fastlane/FastFile
+++ b/fastlane/FastFile
@@ -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
\ No newline at end of file
diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile
index 273a6b6f4..c3f69f2b0 100644
--- a/fastlane/Pluginfile
+++ b/fastlane/Pluginfile
@@ -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'
diff --git a/fastlane/README.md b/fastlane/README.md
index f6333c179..1fa7f0946 100644
--- a/fastlane/README.md
+++ b/fastlane/README.md
@@ -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.
diff --git a/fastlane/metadata/android/en-US/changelogs/default.txt b/fastlane/metadata/android/en-US/changelogs/default.txt
new file mode 100644
index 000000000..4f4b2d8ca
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/default.txt
@@ -0,0 +1,6 @@
+fix: API endpoint (#2733)
+* MM-102 KMP dependencies setup (#2729)
+* fix: API endpoint
+---------
+Co-authored-by: Sk Niyaj Ali
+Co-authored-by: Pronay Sarker s
\ No newline at end of file
diff --git a/androidApp/debug_keystore.jks b/keystores/release_keystore.keystore
similarity index 100%
rename from androidApp/debug_keystore.jks
rename to keystores/release_keystore.keystore
diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts
index a7ba5874b..df6be1a6a 100644
--- a/shared/build.gradle.kts
+++ b/shared/build.gradle.kts
@@ -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(),