From 3ce0b36b71fbeba70bb205dedbceaa4dcc6632d2 Mon Sep 17 00:00:00 2001 From: Sk Niyaj Ali Date: Fri, 6 Sep 2024 15:49:46 +0530 Subject: [PATCH] Configured Fastlane & CI Workflow for publishing app to Play Store & GitHub (#1761) * Configured CI/CD And Fastlane to Release App to Play Store This commit introduces a comprehensive CI/CD pipeline using Github Actions and Fastlane. The following workflows have been added: - **On Push:** Builds, archives, and creates Github pre-releases for beta builds. Deploys to Playstore internal track and promotes to beta if specified. - **Play Publish:** Builds and deploys to Playstore internal track. Optionally promotes to beta. - ** Production Deploy:** Promotes beta releases to production on the Playstore. - **Bump our Calendar Version:** Creates monthly releases. - **Tag Weekly Release:** Tags weekly releases and triggers the On Push workflow for beta builds. Additionally, Fastlane has been configured to handle deployments to the Playstore and Amazon Appstore. Secret inflation and release note generation have been automated using Github Actions. * Configured CI/CD And Fastlane to Release App to Play Store This commit introduces a comprehensive CI/CD pipeline using Github Actions and Fastlane. The following workflows have been added: - **On Push:** Builds, archives, and creates Github pre-releases for beta builds. Deploys to Playstore internal track and promotes to beta if specified. - **Play Publish:** Builds and deploys to Playstore internal track. Optionally promotes to beta. - ** Production Deploy:** Promotes beta releases to production on the Playstore. - **Bump our Calendar Version:** Creates monthly releases. - **Tag Weekly Release:** Tags weekly releases and triggers the On Push workflow for beta builds. Additionally, Fastlane has been configured to handle deployments to the Playstore and Amazon Appstore. Secret inflation and release note generation have been automated using Github Actions. * fix: Improved pre-commit and pre-push scripts * Updated Badging * Updated Badging --- .../actions/create-release-notes/action.yml | 56 +++++ .../actions/create-release-number/action.yml | 27 +++ .github/actions/inflate-secrets/action.yml | 41 ++++ .github/mock-google-services.json | 63 +++++ .github/workflows/monthly.yaml | 21 ++ .github/workflows/onPush.yml | 154 ++++++++++++ .github/workflows/release_to_production.yml | 31 +++ .github/workflows/weekly.yaml | 39 ++++ .gitignore | 11 + Gemfile | 6 + Gemfile.lock | 219 ++++++++++++++++++ build.gradle.kts | 7 + fastlane/AppFile | 2 + fastlane/FastFile | 60 +++++ fastlane/PluginFile | 5 + mifospay/build.gradle.kts | 49 ++-- mifospay/google-services.json | 6 +- mifospay/prodRelease-badging.txt | 6 +- mifospay/release_keystore.keystore | Bin 0 -> 2500 bytes mifospay/src/main/res/values/splash.xml | 9 +- scripts/pre-commit.sh | 10 +- scripts/pre-push.sh | 26 ++- settings.gradle.kts | 10 + 23 files changed, 824 insertions(+), 34 deletions(-) create mode 100644 .github/actions/create-release-notes/action.yml create mode 100644 .github/actions/create-release-number/action.yml create mode 100644 .github/actions/inflate-secrets/action.yml create mode 100644 .github/mock-google-services.json create mode 100644 .github/workflows/monthly.yaml create mode 100644 .github/workflows/onPush.yml create mode 100644 .github/workflows/release_to_production.yml create mode 100644 .github/workflows/weekly.yaml create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 fastlane/AppFile create mode 100644 fastlane/FastFile create mode 100644 fastlane/PluginFile create mode 100644 mifospay/release_keystore.keystore diff --git a/.github/actions/create-release-notes/action.yml b/.github/actions/create-release-notes/action.yml new file mode 100644 index 000000000..dce0e7be5 --- /dev/null +++ b/.github/actions/create-release-notes/action.yml @@ -0,0 +1,56 @@ +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: 'dev', + 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 ./app/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" > ./mifospay/build/outputs/changelogGithub + + echo "Beta Changelog:" + git log --format="* %s" HEAD^..HEAD + git log --format="* %s" HEAD^..HEAD > ./mifospay/build/outputs/changelogBeta diff --git a/.github/actions/create-release-number/action.yml b/.github/actions/create-release-number/action.yml new file mode 100644 index 000000000..d9d16cf61 --- /dev/null +++ b/.github/actions/create-release-number/action.yml @@ -0,0 +1,27 @@ +name: 'Create Release Numbers' +description: 'Creates the current release number based on checked out code' +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 + id: version-generator + shell: bash + run: | + ./gradlew versionFile + COMMITS=`git rev-list --count HEAD` + TAGS=`git tag | grep -v beta | wc -l` + VC=$((((COMMITS+TAGS) * 3) << 1)) + echo Number Commits $COMMITS + echo Number Tags $TAGS + echo Version Code $VC + echo "version-code=$VC" >> $GITHUB_OUTPUT + VERSION=`cat version.txt` + echo Version $VERSION + echo "version=$VERSION" >> $GITHUB_OUTPUT diff --git a/.github/actions/inflate-secrets/action.yml b/.github/actions/inflate-secrets/action.yml new file mode 100644 index 000000000..d9a013eda --- /dev/null +++ b/.github/actions/inflate-secrets/action.yml @@ -0,0 +1,41 @@ +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 mifospay/src/demo/google-services.json + cp .github/mock-google-services.json mifospay/src/prod/google-services.json + + - name: Inflate release_keystore.keystore + shell: bash + env: + KEYSTORE: ${{ inputs.keystore }} + run: | + echo $KEYSTORE | base64 --decode > mifospay/release_keystore.keystore + + - name: Inflate google-services.json + shell: bash + env: + GOOGLE_SERVICES: ${{ inputs.google-services }} + run: | + echo $GOOGLE_SERVICES > mifospay/google-services.json + + - name: Inflate playStorePublishServiceCredentialsFile.json + shell: bash + env: + CREDS: ${{ inputs.playstore-creds }} + run: | + echo $CREDS > mifospay/playStorePublishServiceCredentialsFile.json diff --git a/.github/mock-google-services.json b/.github/mock-google-services.json new file mode 100644 index 000000000..2f054dffe --- /dev/null +++ b/.github/mock-google-services.json @@ -0,0 +1,63 @@ +{ + "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.mifospay" + } + }, + "api_key": [ + { + "current_key": "current_key" + } + ] + }, + { + "client_info": { + "mobilesdk_app_id": "mobilesdk_app_id", + "android_client_info": { + "package_name": "org.mifospay.demo.debug" + } + }, + "api_key": [ + { + "current_key": "current_key" + } + ] + }, + { + "client_info": { + "mobilesdk_app_id": "mobilesdk_app_id", + "android_client_info": { + "package_name": "org.mifospay.demo" + } + }, + "api_key": [ + { + "current_key": "current_key" + } + ] + }, + { + "client_info": { + "mobilesdk_app_id": "mobilesdk_app_id", + "android_client_info": { + "package_name": "org.mifospay.debug" + } + }, + "api_key": [ + { + "current_key": "current_key" + } + ] + } + ], + "configuration_version": "1" +} diff --git a/.github/workflows/monthly.yaml b/.github/workflows/monthly.yaml new file mode 100644 index 000000000..9f173e9ed --- /dev/null +++ b/.github/workflows/monthly.yaml @@ -0,0 +1,21 @@ +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/onPush.yml b/.github/workflows/onPush.yml new file mode 100644 index 000000000..791b679e2 --- /dev/null +++ b/.github/workflows/onPush.yml @@ -0,0 +1,154 @@ +name: On Push + +on: + workflow_dispatch: + inputs: + beta: + description: 'true if this is a beta release' + required: false + default: 'false' + push: + branches: + - master + +env: + SUPPLY_UPLOAD_MAX_RETRIES: 5 + +jobs: + app_build: + name: Github, Firebase, and Sentry 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_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 :mifospay:assembleRelease + + - name: Archive Build + uses: actions/upload-artifact@v4 + with: + path: ./**/*.apk + + - name: Create Version File + if: github.event.inputs.beta == 'true' + shell: bash + env: + VERSION_CODE: ${{ steps.rel_number.outputs.version-code }} + run: | + echo $VERSION_CODE > ./app/build/outputs/version_code.txt + + - name: Create Github Pre-Release + if: github.event.inputs.beta == 'true' + uses: softprops/action-gh-release@v2.0.8 + with: + tag_name: ${{ steps.rel_number.outputs.version }} + body_path: ./app/build/outputs/changelogGithub + draft: false + prerelease: true + files: | + ./mifospay/build/outputs/apk/demo/release/mifospay-demo-release.apk + ./mifospay/build/outputs/apk/prod/release/mifospay-prod-release.apk + ./mifospay/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: 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_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 :mifospay:bundleRelease + + - name: Deploy to Playstore Internal + run: bundle exec fastlane deploy_internal + + - name: Promote Internal to Beta + if: github.event.inputs.beta == 'true' + run: bundle exec fastlane promote_to_beta diff --git a/.github/workflows/release_to_production.yml b/.github/workflows/release_to_production.yml new file mode 100644 index 000000000..347ccc4ba --- /dev/null +++ b/.github/workflows/release_to_production.yml @@ -0,0 +1,31 @@ +name: Production Deploy + +on: + workflow_dispatch: + release: + types: [ released ] + +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 + + - 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 promote_to_production diff --git a/.github/workflows/weekly.yaml b/.github/workflows/weekly.yaml new file mode 100644 index 000000000..2e5281576 --- /dev/null +++ b/.github/workflows/weekly.yaml @@ -0,0 +1,39 @@ +name: Tag Weekly Release + +on: + workflow_dispatch: + schedule: + - cron: '0 4 * * 0' +jobs: + tag: + name: Tag Weekly Release + runs-on: ubuntu-latest + 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' + + - name: Tag Weekly Release + env: + GITHUB_TOKEN: ${{ secrets.TAG_PUSH_TOKEN }} + run: ./gradlew :reckonTagPush -Preckon.stage=final + + - name: Trigger Workflow + uses: actions/github-script@v7 + with: + script: | + github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'onPush.yml', + ref: 'master', + inputs: { + "beta": "true", + }, + }) diff --git a/.gitignore b/.gitignore index 1696ea9b4..510626127 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,14 @@ app/manifest-merger-release-report.txt # Exclude Google services from prod flavour mifospay/src/prod/google-services.json + +#*.keystore + +version.txt + +firebaseAppDistributionServiceCredentialsFile.json +playStorePublishServiceCredentialsFile.json + +# Ruby stuff we don't care about +.bundle/ +vendor/ diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..1e4d11c03 --- /dev/null +++ b/Gemfile @@ -0,0 +1,6 @@ +source "https://rubygems.org" + +gem "fastlane" + +plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') +eval_gemfile(plugins_path) if File.exist?(plugins_path) \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 000000000..fe069a866 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,219 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.7) + base64 + nkf + rexml + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + artifactory (3.0.17) + atomos (0.1.3) + aws-eventstream (1.3.0) + aws-partitions (1.971.0) + aws-sdk-core (3.203.0) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.9) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.89.0) + aws-sdk-core (~> 3, >= 3.203.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.160.0) + aws-sdk-core (~> 3, >= 3.203.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.9.1) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + base64 (0.2.0) + claide (1.1.0) + colored (1.2) + colored2 (3.1.2) + commander (4.6.0) + highline (~> 2.0.0) + declarative (0.0.20) + digest-crc (0.6.5) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.6.20240107) + dotenv (2.8.1) + emoji_regex (3.2.3) + excon (0.111.0) + faraday (1.10.3) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-cookie_jar (0.0.7) + faraday (>= 0.8.0) + http-cookie (~> 1.0.0) + faraday-em_http (1.0.0) + 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-net_http (1.0.2) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.0) + faraday (~> 1.0) + fastimage (2.3.1) + fastlane (2.222.0) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.8, < 3.0.0) + artifactory (~> 3.0) + aws-sdk-s3 (~> 1.0) + babosa (>= 1.0.3, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored (~> 1.2) + commander (~> 4.6) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 4.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 1.0) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 1.0) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-env (>= 1.6.0, < 2.0.0) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + http-cookie (~> 1.0.5) + json (< 3.0.0) + jwt (>= 2.1.0, < 3) + mini_magick (>= 4.9.4, < 5.0.0) + multipart-post (>= 2.0.0, < 3.0.0) + naturally (~> 2.2) + optparse (>= 0.1.1, < 1.0.0) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 2.0.0, < 3.0.0) + security (= 0.1.5) + simctl (~> 1.6.3) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (~> 3) + tty-screen (>= 0.6.3, < 1.0.0) + 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-travis-formatter (>= 0.0.3, < 2.0.0) + fastlane-plugin-increment_version_code (0.4.3) + gh_inspector (1.1.3) + google-apis-androidpublisher_v3 (0.54.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-core (0.11.3) + addressable (~> 2.5, >= 2.5.1) + googleauth (>= 0.16.2, < 2.a) + httpclient (>= 2.8.1, < 3.a) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.a) + rexml + google-apis-iamcredentials_v1 (0.17.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-playcustomapp_v1 (0.13.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-storage_v1 (0.31.0) + google-apis-core (>= 0.11.0, < 2.a) + google-cloud-core (1.7.1) + google-cloud-env (>= 1.0, < 3.a) + google-cloud-errors (~> 1.0) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.4.0) + google-cloud-storage (1.47.0) + addressable (~> 2.8) + digest-crc (~> 0.4) + google-apis-iamcredentials_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.31.0) + google-cloud-core (~> 1.6) + googleauth (>= 0.16.2, < 2.a) + mini_mime (~> 1.0) + googleauth (1.8.1) + faraday (>= 0.17.3, < 3.a) + jwt (>= 1.4, < 3.0) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-cookie (1.0.7) + domain_name (~> 0.5) + httpclient (2.8.3) + jmespath (1.6.2) + json (2.7.2) + jwt (2.8.2) + 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) + naturally (2.2.1) + nkf (0.2.0) + optparse (0.5.0) + os (1.1.4) + plist (3.7.1) + public_suffix (6.0.1) + rake (13.2.1) + representable (3.2.0) + declarative (< 0.1.0) + trailblazer-option (>= 0.1.1, < 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) + rexml (3.3.7) + rouge (2.0.7) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + security (0.1.5) + signet (0.19.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.10) + CFPropertyList + naturally + terminal-notifier (2.0.0) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + trailblazer-option (0.1.2) + tty-cursor (0.7.1) + tty-screen (0.8.2) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + uber (0.1.0) + unicode-display_width (2.5.0) + word_wrap (1.0.0) + xcodeproj (1.25.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) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + x64-mingw-ucrt + +DEPENDENCIES + fastlane + fastlane-plugin-increment_version_code + +BUNDLED WITH + 2.5.18 diff --git a/build.gradle.kts b/build.gradle.kts index 064849ef6..455b4e37b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,4 +34,11 @@ plugins { alias(libs.plugins.compose.compiler) apply false alias(libs.plugins.kotlinMultiplatform) apply false alias(libs.plugins.wire) apply false +} + +tasks.register("versionFile").configure { + group = "publishing" + doLast { + File(projectDir, "version.txt").writeText(project.version.toString()) + } } \ No newline at end of file diff --git a/fastlane/AppFile b/fastlane/AppFile new file mode 100644 index 000000000..d85271893 --- /dev/null +++ b/fastlane/AppFile @@ -0,0 +1,2 @@ +json_key_file("") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one +package_name(ENV["STAGING_PACKAGE_NAME"]) # e.g. org.mifospay.demo \ No newline at end of file diff --git a/fastlane/FastFile b/fastlane/FastFile new file mode 100644 index 000000000..00428123f --- /dev/null +++ b/fastlane/FastFile @@ -0,0 +1,60 @@ +default_platform(:android) + +platform :android do + desc "Deploy internal tracks to Google Play" + lane :deploy_internal do + supply( + track: 'internal', + aab: 'app/build/outputs/bundle/prod/app-prod-release.aab', + skip_upload_metadata: true, + skip_upload_images: true, + skip_upload_screenshots: true, + ) + end + + desc "Promote internal tracks to beta on Google Play" + lane :promote_to_beta do + supply( + track: 'internal', + track_promote_to: 'beta', + skip_upload_changelogs: true, + skip_upload_metadata: true, + skip_upload_images: true, + skip_upload_screenshots: true, + ) + end + + desc "Promote beta tracks to production on Google Play" + lane :promote_to_production do + supply( + track: 'beta', + track_promote_to: 'production', + skip_upload_changelogs: true, + sync_image_upload: true, + ) + end + + desc "Prep Amazon Appstore submission" + lane :prep_amazon do + amazon_app_submission( + client_id: ENV["AMAZON_APPSTORE_CLIENT_ID"], + client_secret: ENV["AMAZON_APPSTORE_CLIENT_SECRET"], + app_id: ENV["AMAZON_APPSTORE_APP_ID"], + apk_path: "app/build/outputs/apk/prod/release/app-prod-release.apk", + upload_apk: true, + changelogs_path: "fastlane/metadata/android/en-US/changelogs/", + upload_changelogs: true, + submit_for_review: false + ) + end + + desc "Submit to Amazon Appstore" + lane :submit_amazon do + amazon_app_submission( + client_id: ENV["AMAZON_APPSTORE_CLIENT_ID"], + client_secret: ENV["AMAZON_APPSTORE_CLIENT_SECRET"], + app_id: ENV["AMAZON_APPSTORE_APP_ID"], + submit_for_review: true + ) + end +end \ No newline at end of file diff --git a/fastlane/PluginFile b/fastlane/PluginFile new file mode 100644 index 000000000..32ea0b1fb --- /dev/null +++ b/fastlane/PluginFile @@ -0,0 +1,5 @@ +# Autogenerated by fastlane +# +# Ensure this file is checked in to source control! + +gem 'fastlane-plugin-increment_version_code' \ No newline at end of file diff --git a/mifospay/build.gradle.kts b/mifospay/build.gradle.kts index ef519cd2b..4460c2e83 100644 --- a/mifospay/build.gradle.kts +++ b/mifospay/build.gradle.kts @@ -9,6 +9,16 @@ */ import org.mifospay.MifosBuildType +/* + * Copyright 2024 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * See https://github.com/openMF/mobile-wallet/blob/master/LICENSE.md + */ + plugins { alias(libs.plugins.mifospay.android.application) alias(libs.plugins.mifospay.android.application.compose) @@ -21,29 +31,39 @@ plugins { android { namespace = "org.mifospay" + defaultConfig { applicationId = "org.mifospay" - versionCode = 1 - versionName = "1.0" + versionName = project.version.toString() + versionCode = System.getenv("VERSION_CODE")?.toIntOrNull() ?: 1 vectorDrawables.useSupportLibrary = true testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } + + signingConfigs { + create("release") { + storeFile = file(System.getenv("KEYSTORE_PATH") ?: "release_keystore.keystore") + storePassword = System.getenv("KEYSTORE_PASSWORD") ?: "Mifospay" + keyAlias = System.getenv("KEYSTORE_ALIAS") ?: "key0" + keyPassword = System.getenv("KEYSTORE_ALIAS_PASSWORD") ?: "Mifos@123" + enableV1Signing = true + enableV2Signing = true + } + } + buildTypes { debug { - // applicationIdSuffix = MifosBuildType.DEBUG.applicationIdSuffix + applicationIdSuffix = MifosBuildType.DEBUG.applicationIdSuffix } + release { isMinifyEnabled = true - // applicationIdSuffix = MifosBuildType.RELEASE.applicationIdSuffix - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - - // To publish on the Play store a private signing key is required, but to allow anyone - // who clones the code to sign and run the release variant, use the debug signing key. - // TODO: Abstract the signing configuration to a separate file to avoid hardcoding this. - signingConfig = signingConfigs.getByName("debug") + applicationIdSuffix = MifosBuildType.RELEASE.applicationIdSuffix + isShrinkResources = true + isDebuggable = false + isJniDebuggable = false + signingConfig = signingConfigs.getByName("release") + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } } @@ -57,6 +77,7 @@ android { excludes.add("/META-INF/{AL2.0,LGPL2.1}") } } + testOptions { unitTests { isIncludeAndroidResources = true @@ -146,4 +167,4 @@ dependencyGuard { modules = true tree = true } -} +} \ No newline at end of file diff --git a/mifospay/google-services.json b/mifospay/google-services.json index 585a9d112..9a4001bd2 100644 --- a/mifospay/google-services.json +++ b/mifospay/google-services.json @@ -9,7 +9,7 @@ "client_info": { "mobilesdk_app_id": "1:728434912738:android:49282a75468730891a1dbb", "android_client_info": { - "package_name": "org.mifos.pisp.android" + "package_name": "org.mifospay" } }, "oauth_client": [ @@ -38,7 +38,7 @@ "client_info": { "mobilesdk_app_id": "1:728434912738:android:ef7156e455c6a1a41a1dbb", "android_client_info": { - "package_name": "org.mifos.pisp.android.debug" + "package_name": "org.mifospay.demo.debug" } }, "oauth_client": [ @@ -67,7 +67,7 @@ "client_info": { "mobilesdk_app_id": "1:728434912738:android:0490c291986f0a691a1dbb", "android_client_info": { - "package_name": "org.mifospay" + "package_name": "org.mifospay.debug" } }, "oauth_client": [ diff --git a/mifospay/prodRelease-badging.txt b/mifospay/prodRelease-badging.txt index 88d68cddf..8cdf267e8 100644 --- a/mifospay/prodRelease-badging.txt +++ b/mifospay/prodRelease-badging.txt @@ -1,4 +1,4 @@ -package: name='org.mifospay' versionCode='1' versionName='1.0' platformBuildVersionName='14' platformBuildVersionCode='34' compileSdkVersion='34' compileSdkVersionCodename='14' +package: name='org.mifospay' versionCode='1' versionName='0.0.1-beta.0.833+20240905T230255Z' platformBuildVersionName='14' platformBuildVersionCode='34' compileSdkVersion='34' compileSdkVersionCodename='14' sdkVersion:'26' targetSdkVersion:'34' uses-permission: name='android.permission.INTERNET' @@ -115,13 +115,11 @@ application-label-zh-CN:'Mifos Pay' application-label-zh-HK:'Mifos Pay' application-label-zh-TW:'Mifos Pay' application-label-zu:'Mifos Pay' -application-icon-120:'res/mipmap/ic_launcher.png' application-icon-160:'res/mipmap/ic_launcher.png' application-icon-240:'res/mipmap/ic_launcher.png' application-icon-320:'res/mipmap/ic_launcher.png' application-icon-480:'res/mipmap/ic_launcher.png' application-icon-640:'res/mipmap/ic_launcher.png' -application-icon-65534:'res/mipmap/ic_launcher.png' application: label='Mifos Pay' icon='res/mipmap/ic_launcher.png' launchable-activity: name='org.mifospay.MainActivity' label='' icon='' property: name='android.adservices.AD_SERVICES_CONFIG' resource='res/xml/ga_ad_services_config.xml' @@ -141,5 +139,5 @@ other-services supports-screens: 'small' 'normal' 'large' 'xlarge' supports-any-density: 'true' locales: '--_--' 'af' 'am' 'ar' 'as' 'az' 'be' 'bg' 'bn' 'bs' 'ca' 'cs' 'da' 'de' 'de-DE' 'el' 'en-AU' 'en-CA' 'en-GB' 'en-IN' 'en-XC' 'eo' 'es' 'es-US' 'et' 'eu' 'fa' 'fi' 'fr' 'fr-CA' 'ga' 'gd' 'gl' 'gu' 'hi' 'hr' 'hu' 'hy' 'in' 'in-ID' 'is' 'it' 'it-IT' 'iw' 'ja' 'jv' 'ka' 'kk' 'km' 'kn' 'ko' 'ku' 'ky' 'lb' 'lo' 'lt' 'lv' 'mk' 'ml' 'mn' 'mr' 'ms' 'my' 'nb' 'ne' 'nl' 'no' 'or' 'pa' 'pl' 'pt' 'pt-BR' 'pt-PT' 'ro' 'ru' 'ru-RU' 'si' 'sk' 'sl' 'so' 'sq' 'sr' 'sr-Latn' 'sv' 'sw' 'ta' 'te' 'th' 'tl' 'tr' 'tr-TR' 'uk' 'ur' 'uz' 'vi' 'zh' 'zh-CN' 'zh-HK' 'zh-TW' 'zu' -densities: '120' '160' '240' '320' '480' '640' '65534' +densities: '160' '240' '320' '480' '640' native-code: 'arm64-v8a' 'armeabi-v7a' 'x86' 'x86_64' diff --git a/mifospay/release_keystore.keystore b/mifospay/release_keystore.keystore new file mode 100644 index 0000000000000000000000000000000000000000..0ba61aef44b2c2b12862f6a94ad631460c9d9734 GIT binary patch literal 2500 zcma)8X*3%O7EUA~v9CofvA2zuB!t9Pl+uP0YYAx|FGy@vYOfHQ+Da`grqmKkQPtX> zouU*)4Q;HCR;|<0+SNAZ%$&Y6zh-{ibI<+0``vT@-0xf@9oEhU1R?1#Dmx6pFlFrU z06BnpbQlp#huNRN)<`;}?7t|;6EGc;aRR5EL^3Q%<`5~_+tV*t*<1nNay7_g)S6?Cn??v*%5Rn9!mLcIoac)_FtVWanBs?Wv zY#=##3p99B+P;0Q+jLql%6KV$DDq78=pD@~t!3*X?vSjCb*_z)p6Xp-vtITGX!iHd zwmwBcsm;{eoG~`Fxu)-#2^!JKm3Hc7eNnte0s!ukH#-K z&dHV1N-wb8{B|uzu(yQEFbx zfB5eF;r@XLHxbYtL&WajSz~G+e<0 zIo=MJ({+djnbuP1mZJQ&PX5QM_EIZCD$3r6_0ur%C5_u@?=-Hpn+qwV+)u+{%SR0{ zfxW#$JH~^1QQA}bVg8tUF39dTw9&QMuUCg9k6jbOZ0e>w03{Bp??o*x5G=#-)<_BL zJCD`GE|o2EgKU0FYTKFeu;u_>L*L(B75&{3_yX1j97Jze)~QK{P90e+H&3TGf5_@F z$#sh8d@4QUBZNpFoEg^|sXdtZupvh6A?c~%gH&!`9jRWrXtoSBGkLY@Gj+O3Ze6~S zB9-iS2Ax@PmPF1~$ci|HUifocuwZuO*70Z4#o1ZuKFH%l380SE9!JjDidBaJijz+J z$xoaevGdz~b&PdX*2s_x8ewYeQA zbbFexRzS*04e?GJ3dzWxwBj2s`1Hu8xNt}p$r5(iin^6;7)H{36f_7>XmW&0Z3P$T zlO3z&jbwuYwY*t+q*<>_Wu{WG0uH=4+ti2Q!@tgbRv*F@Jx1+NRo5tc(bU8f;TQ4L}x|F_iaq_wQ}=aS6mlen&-cU8{`l+k*{RU zjrC1EjFiSI6}(T;SJ})%#U>LF-&CItTm!+8FKhlGZ!et5)|NkIhR^MHV&TQi_n%59 z*}27uIaJk8^{~B#kM^n7a^>B->eKOM-}t(g@>sBO=rb{!MlZ)aaRmdrTm-FhZG$?| zc2~jht>J+TII0gge8Hl>_>8=(VNcH*nHapFx2|a4mG-cVM#li^E{~5|qa+Tue7BTv z7vignWJ)fN129^_lKVK1S8y2FXdNF`>3q>BKu0P)C|~(JM}Z$s_|f(_m~;M;uhJC- zeNPefq|un6fLvyBotG6=#@N>K;bn%W)E=k)$6;AzGr=Yy?M-QIt!5KVl_pS1{Bg`pi^idJkfOir5+;n|l!gL40c1c30Ev|Six5Exg9U9U-eeh+CK`j$#Ax6& z)zD}p9hCp8iGw|l4oW?NlGuQNlXU+t2Kc`$x_Fj0&@OAFXI=E}i4fn;7*3vQ)!3v>Pwg?9jMr z>fD9TU?OC(NI{y&y{EA>fR|EBvI~fL)5VJEX4_1l+=x=ht!LCm1)N5~PuEgD+ zXT^)v41YKte2O8&!HBNY3w2jbDa@$6h#Z7<7DMRlumjAQ~2;rTn5Z%KMbo{-q z!{cu26e4lVc2?0{Ja}En_Iq!?6AA4eF+ABC9(!wcg2Sn*hJBjHM@LMmjUh(h#i4c`xyO$EL zmcn7ty0n3(rANb%3b)4=1-#;0<&}=4M8R2!eQo9<`x60YuhM9ns*pvi&z^W6YTvaZT-NTRAurgnVnsH!O5!77EsWO8D2^-pbkI zKM2hqGc7aLeVOR0`5E;XPf90)9CzdXnHs4Xs#w8$vP)^-i&jhyz>98OscIYa51*fH zuP$EqMuxu}Lg)IHA~O=g*Q}A|NEDLi*Js0avH)1dpv7$y->u#&oQ0J+JFr?z)kZmb pH^@9of?n>5G;i$(3kQZv+7cnC=ag#GIz{|Ub5VG;lU literal 0 HcmV?d00001 diff --git a/mifospay/src/main/res/values/splash.xml b/mifospay/src/main/res/values/splash.xml index c3208953d..9bcf80035 100644 --- a/mifospay/src/main/res/values/splash.xml +++ b/mifospay/src/main/res/values/splash.xml @@ -12,13 +12,14 @@ diff --git a/scripts/pre-commit.sh b/scripts/pre-commit.sh index 9ddd35a29..c49bfe109 100644 --- a/scripts/pre-commit.sh +++ b/scripts/pre-commit.sh @@ -37,16 +37,19 @@ run_spotless_checks() { # Function to run ktlint checks run_dependency_guard() { printf "\nšŸš€ Brace yourself! We're about to generate dependency guard baseline!" - ./gradlew dependencyGuardBaseline + ./gradlew dependencyGuardBaseline > /tmp/dependency-result KT_EXIT_CODE=$? if [ ${KT_EXIT_CODE} -ne 0 ]; then + cat /tmp/dependency-result + rm /tmp/dependency-result printf "\n*********************************************************************************" echo " šŸ’„ Oh no! Something went wrong! šŸ’„" echo " šŸ’” Unable to generate dependency baseline. šŸ› ļø" printf "*********************************************************************************\n" exit ${KT_EXIT_CODE} else + rm /tmp/dependency-result echo "šŸŽ‰ Bravo! Dependency baseline has been generated successfully! Keep rocking that clean code! šŸš€šŸ’«" fi } @@ -74,16 +77,19 @@ run_detekt_checks() { # Function to run Version Catalog checks run_version_catalog_checks() { echo "\nšŸš€ Version catalog linter is now analyzing your catalog for potential issues!" - ./gradlew formatVersionCatalog + ./gradlew formatVersionCatalog > /tmp/catalog-result DETEKT_EXIT_CODE=$? if [ ${DETEKT_EXIT_CODE} -ne 0 ]; then + cat /tmp/catalog-result + rm /tmp/catalog-result echo "\n*********************************************************************************" echo " šŸ’„ Oh no! Version Catalog found issues in the code! Time to fix those issues! šŸ’„" echo " šŸ’” Tip: Review the Version Catalog logs to resolve these issues. šŸ› ļø" echo "*********************************************************************************" exit ${DETEKT_EXIT_CODE} else + rm /tmp/catalog-result echo "šŸŽ‰ Fantastic work! Your Version catalog has been formatted successfully šŸš€šŸŒŸ" fi } diff --git a/scripts/pre-push.sh b/scripts/pre-push.sh index d2c0f6779..cad5ba9a1 100644 --- a/scripts/pre-push.sh +++ b/scripts/pre-push.sh @@ -28,7 +28,9 @@ run_spotless_checks() { echo " šŸ’” Tip: Check the reported issues and fix formatting errors. šŸ› ļø" echo "*********************************************************************************" echo "šŸš€ Attempting to apply Spotless formatting fixes..." - ./gradlew spotlessApply --daemon + ./gradlew spotlessApply --daemon > /tmp/spotless-result + rm /tmp/spotless-result + echo "šŸŽ‰ Stellar job! Your code is pristine and has passed Spotless's formatting checks without a hitch! Keep shining bright! āœØšŸš€" else rm /tmp/spotless-result echo "šŸŽ‰ Stellar job! Your code is pristine and has passed Spotless's formatting checks without a hitch! Keep shining bright! āœØšŸš€" @@ -58,36 +60,46 @@ run_detekt_checks() { # Function to run ktlint checks run_dependency_guard() { printf "\nšŸš€ Brace yourself! We're about to generate dependency guard baseline!" - ./gradlew dependencyGuard + ./gradlew dependencyGuard > /tmp/dependency-result KT_EXIT_CODE=$? if [ ${KT_EXIT_CODE} -ne 0 ]; then + cat /tmp/dependency-result + rm /tmp/dependency-result printf "\n*********************************************************************************" echo " šŸ’„ Oh no! Something went wrong! šŸ’„" echo " šŸ’” Unable to generate dependency baseline. šŸ› ļø" printf "*********************************************************************************\n" echo "šŸš€ Attempting to generate dependency baseline again..." - ./gradlew dependencyGuardBaseline - else + ./gradlew dependencyGuardBaseline > /tmp/dependency-result + rm /tmp/dependency-result echo "šŸŽ‰ Bravo! Dependency baseline has been generated successfully! Keep rocking that clean code! šŸš€šŸ’«" + else + rm /tmp/dependency-result + echo "šŸŽ‰ Bravo! Dependency baseline has been checked successfully! Keep rocking that clean code! šŸš€šŸ’«" fi } # Function to run Version Catalog checks run_version_catalog_checks() { echo "\nšŸš€ Version catalog linter is now analyzing your catalog for potential issues!" - ./gradlew checkVersionCatalog + ./gradlew checkVersionCatalog > /tmp/catalog-result DETEKT_EXIT_CODE=$? if [ ${DETEKT_EXIT_CODE} -ne 0 ]; then + cat /tmp/catalog-result + rm /tmp/catalog-result echo "\n*********************************************************************************" echo " šŸ’„ Oh no! Version Catalog found issues in the code! Time to fix those issues! šŸ’„" echo " šŸ’” Tip: Review the Version Catalog logs to resolve these issues. šŸ› ļø" echo "*********************************************************************************" echo "šŸš€ Attempting to format the Version Catalog again..." - ./gradlew formatVersionCatalog - else + ./gradlew formatVersionCatalog > /tmp/catalog-result + rm /tmp/catalog-result echo "šŸŽ‰ Fantastic work! Your Version catalog has been formatted successfully šŸš€šŸŒŸ" + else + rm /tmp/catalog-result + echo "šŸŽ‰ Fantastic work! Your Version catalog has been formatted properly šŸš€šŸŒŸ" fi } diff --git a/settings.gradle.kts b/settings.gradle.kts index 3b96a9751..f43a17c79 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,6 +19,16 @@ dependencyResolutionManagement { plugins { id("org.gradle.toolchains.foojay-resolver-convention") version("0.8.0") + id("org.ajoberstar.reckon.settings") version("0.18.3") +} + +extensions.configure { + setDefaultInferredScope("patch") + stages("beta", "rc", "final") + setScopeCalc { java.util.Optional.of(org.ajoberstar.reckon.core.Scope.PATCH) } + setScopeCalc(calcScopeFromProp().or(calcScopeFromCommitMessages())) + setStageCalc(calcStageFromProp()) + setTagWriter { it.toString() } } rootProject.name = "mobile-wallet"