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(),