From d2d5487d92d89dbd8f3d4aaf7bc731a0e81057f9 Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Tue, 9 Sep 2025 16:08:11 -0500 Subject: [PATCH] feat(ci): migrate to fastlane for release management (#3038) Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com> (cherry picked from commit 5cfa44cb64ae969e6a258025f355e83f592239c6) --- .github/workflows/release.yml | 211 +++++++--------- .gitignore | 2 + .ruby-version | 1 + Gemfile | 3 + Gemfile.lock | 229 ++++++++++++++++++ RELEASE_PROCESS.md | 185 ++++++++------ .../play/listings/en-US/full-description.txt | 3 - .../play/listings/en-US/graphics/icon/1.png | Bin 16059 -> 0 bytes .../play/listings/en-US/short-description.txt | 1 - app/src/main/play/listings/en-US/title.txt | 1 - .../main/play/release-notes/en-US/alpha.txt | 1 - .../main/play/release-notes/en-US/beta.txt | 1 - .../play/release-notes/en-US/internal.txt | 1 - .../play/release-notes/en-US/production.txt | 1 - fastlane/Appfile | 2 + fastlane/Fastfile | 86 +++++++ fastlane/README.md | 48 ++++ .../metadata/android}/contact-email.txt | 0 .../metadata/android}/contact-website.txt | 0 .../metadata/android}/default-language.txt | 0 .../android/en-US/changelogs/default.txt | 1 + .../android/en-US/full_description.txt | 21 ++ .../android/en-US/images/featureGraphic.jpeg | Bin .../metadata/android/en-US/images/icon.png | Bin 0 -> 13234 bytes .../en-US/images/phoneScreenshots/1_en-US.png | Bin .../en-US/images/phoneScreenshots/2_en-US.png | Bin .../en-US/images/phoneScreenshots/3_en-US.png | Bin .../en-US/images/phoneScreenshots/4_en-US.png | Bin .../en-US/images/phoneScreenshots/5_en-US.png | Bin .../android/en-US/short_description.txt | 1 + fastlane/metadata/android/en-US/title.txt | 1 + .../metadata/android/en-US/video.txt | 0 32 files changed, 588 insertions(+), 212 deletions(-) create mode 100644 .ruby-version create mode 100644 Gemfile create mode 100644 Gemfile.lock delete mode 100644 app/src/main/play/listings/en-US/full-description.txt delete mode 100644 app/src/main/play/listings/en-US/graphics/icon/1.png delete mode 100644 app/src/main/play/listings/en-US/short-description.txt delete mode 100644 app/src/main/play/listings/en-US/title.txt delete mode 100644 app/src/main/play/release-notes/en-US/alpha.txt delete mode 100644 app/src/main/play/release-notes/en-US/beta.txt delete mode 100644 app/src/main/play/release-notes/en-US/internal.txt delete mode 100644 app/src/main/play/release-notes/en-US/production.txt create mode 100644 fastlane/Appfile create mode 100644 fastlane/Fastfile create mode 100644 fastlane/README.md rename {app/src/main/play => fastlane/metadata/android}/contact-email.txt (100%) rename {app/src/main/play => fastlane/metadata/android}/contact-website.txt (100%) rename {app/src/main/play => fastlane/metadata/android}/default-language.txt (100%) create mode 100644 fastlane/metadata/android/en-US/changelogs/default.txt create mode 100644 fastlane/metadata/android/en-US/full_description.txt rename app/src/main/play/listings/en-US/graphics/feature-graphic/1.png => fastlane/metadata/android/en-US/images/featureGraphic.jpeg (100%) create mode 100644 fastlane/metadata/android/en-US/images/icon.png rename app/src/main/play/listings/en-US/graphics/phone-screenshots/1.png => fastlane/metadata/android/en-US/images/phoneScreenshots/1_en-US.png (100%) rename app/src/main/play/listings/en-US/graphics/phone-screenshots/2.png => fastlane/metadata/android/en-US/images/phoneScreenshots/2_en-US.png (100%) rename app/src/main/play/listings/en-US/graphics/phone-screenshots/3.png => fastlane/metadata/android/en-US/images/phoneScreenshots/3_en-US.png (100%) rename app/src/main/play/listings/en-US/graphics/phone-screenshots/4.png => fastlane/metadata/android/en-US/images/phoneScreenshots/4_en-US.png (100%) rename app/src/main/play/listings/en-US/graphics/phone-screenshots/5.png => fastlane/metadata/android/en-US/images/phoneScreenshots/5_en-US.png (100%) create mode 100644 fastlane/metadata/android/en-US/short_description.txt create mode 100644 fastlane/metadata/android/en-US/title.txt rename app/src/main/play/listings/en-US/video-url.txt => fastlane/metadata/android/en-US/video.txt (100%) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7c4e91866..21e7bdc03 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,7 +55,7 @@ jobs: # This will overflow Integer.MAX_VALUE in the year 6052, hopefully we'll have moved on by then. run: echo "versionCode=$(( $(date +%s) / 60 ))" >> $GITHUB_OUTPUT - build-fdroid: + release-google: runs-on: ubuntu-latest needs: prepare-build-info steps: @@ -79,54 +79,7 @@ jobs: build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' build-scan-terms-of-use-agree: 'yes' - - name: Load Fdroid secrets - run: | - echo $KEYSTORE | base64 -di > ./app/$KEYSTORE_FILENAME - echo "$KEYSTORE_PROPERTIES" > ./keystore.properties - env: - KEYSTORE: ${{ secrets.KEYSTORE }} - KEYSTORE_FILENAME: ${{ secrets.KEYSTORE_FILENAME }} - KEYSTORE_PROPERTIES: ${{ secrets.KEYSTORE_PROPERTIES }} - - - name: Build F-Droid Release APK - run: | - ./gradlew :app:assembleFdroidRelease --parallel --continue --scan - env: - VERSION_NAME: ${{ needs.prepare-build-info.outputs.APP_VERSION_NAME }} - VERSION_CODE: ${{ needs.prepare-build-info.outputs.APP_VERSION_CODE }} - - - name: Upload F-Droid APK artifact - uses: actions/upload-artifact@v4 - with: - name: fdroid-apk - path: app/build/outputs/apk/fdroid/release/app-fdroid-release.apk - retention-days: 1 - - build-google: - runs-on: ubuntu-latest - needs: prepare-build-info - steps: - - name: Checkout code - uses: actions/checkout@v5 - with: - fetch-depth: 0 - submodules: 'recursive' - - - name: Set up JDK 21 - uses: actions/setup-java@v5 - with: - java-version: '21' - distribution: 'jetbrains' - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - with: - cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - build-scan-publish: true - build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' - build-scan-terms-of-use-agree: 'yes' - - - name: Load Google secrets + - name: Load secrets env: GSERVICES: ${{ secrets.GSERVICES }} KEYSTORE: ${{ secrets.KEYSTORE }} @@ -135,6 +88,7 @@ jobs: DATADOG_APPLICATION_ID: ${{ secrets.DATADOG_APPLICATION_ID }} DATADOG_CLIENT_TOKEN: ${{ secrets.DATADOG_CLIENT_TOKEN }} GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }} + GOOGLE_PLAY_JSON_KEY: ${{ secrets.GOOGLE_PLAY_JSON_KEY }} run: | rm -f ./app/google-services.json # Ensure clean state echo $GSERVICES > ./app/google-services.json @@ -143,13 +97,33 @@ jobs: echo "datadogApplicationId=$DATADOG_APPLICATION_ID" >> ./secrets.properties echo "datadogClientToken=$DATADOG_CLIENT_TOKEN" >> ./secrets.properties echo "MAPS_API_KEY=$GOOGLE_MAPS_API_KEY" >> ./secrets.properties + echo "$GOOGLE_PLAY_JSON_KEY" > ./fastlane/play-store-credentials.json - - name: Build Google Release Artifacts (AAB and APK) + - name: Setup Fastlane + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2' + bundler-cache: true + + - name: Determine Fastlane Lane + id: fastlane_lane run: | - ./gradlew :app:bundleGoogleRelease :app:assembleGoogleRelease --parallel --continue --scan + TAG_NAME="${{ github.ref_name }}" + if [[ "$TAG_NAME" == *"-internal"* ]]; then + echo "lane=internal" >> $GITHUB_OUTPUT + elif [[ "$TAG_NAME" == *"-closed"* ]]; then + echo "lane=closed" >> $GITHUB_OUTPUT + elif [[ "$TAG_NAME" == *"-open"* ]]; then + echo "lane=open" >> $GITHUB_OUTPUT + else + echo "lane=production" >> $GITHUB_OUTPUT + fi + + - name: Build and Deploy Google Play Tracks with Fastlane env: VERSION_NAME: ${{ needs.prepare-build-info.outputs.APP_VERSION_NAME }} VERSION_CODE: ${{ needs.prepare-build-info.outputs.APP_VERSION_CODE }} + run: bundle exec fastlane ${{ steps.fastlane_lane.outputs.lane }} - name: Upload Google AAB artifact uses: actions/upload-artifact@v4 @@ -165,21 +139,9 @@ jobs: path: app/build/outputs/apk/google/release/app-google-release.apk retention-days: 1 - - name: Upload Mapping File - uses: actions/upload-artifact@v4 - with: - name: mapping - path: app/build/outputs/mapping/googleRelease/mapping.txt - retention-days: 1 - - publish-release: + release-fdroid: runs-on: ubuntu-latest - needs: [prepare-build-info, build-fdroid, build-google] - outputs: - RELEASE_UPLOAD_URL: ${{ steps.create_gh_release.outputs.upload_url }} - CHANGELOG: ${{ steps.generate_changelog.outputs.changelog }} - APP_VERSION_NAME: ${{ needs.prepare-build-info.outputs.APP_VERSION_NAME }} - APP_VERSION_CODE: ${{ needs.prepare-build-info.outputs.APP_VERSION_CODE }} + needs: prepare-build-info steps: - name: Checkout code uses: actions/checkout@v5 @@ -187,34 +149,69 @@ jobs: fetch-depth: 0 submodules: 'recursive' - - name: Download F-Droid APK - uses: actions/download-artifact@v5 + - name: Set up JDK 21 + uses: actions/setup-java@v5 + with: + java-version: '21' + distribution: 'jetbrains' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} + build-scan-publish: true + build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' + build-scan-terms-of-use-agree: 'yes' + + - name: Load secrets + env: + KEYSTORE: ${{ secrets.KEYSTORE }} + KEYSTORE_FILENAME: ${{ secrets.KEYSTORE_FILENAME }} + KEYSTORE_PROPERTIES: ${{ secrets.KEYSTORE_PROPERTIES }} + run: | + echo $KEYSTORE | base64 -di > ./app/$KEYSTORE_FILENAME + echo "$KEYSTORE_PROPERTIES" > ./keystore.properties + + - name: Setup Fastlane + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2' + bundler-cache: true + + - name: Build F-Droid with Fastlane + env: + VERSION_NAME: ${{ needs.prepare-build-info.outputs.APP_VERSION_NAME }} + VERSION_CODE: ${{ needs.prepare-build-info.outputs.APP_VERSION_CODE }} + run: bundle exec fastlane fdroid_build + + - name: Upload F-Droid APK artifact + uses: actions/upload-artifact@v4 with: name: fdroid-apk - path: ./build-artifacts/fdroid + path: app/build/outputs/apk/fdroid/release/app-fdroid-release.apk + retention-days: 1 + finalize-release: + runs-on: ubuntu-latest + needs: [release-google, release-fdroid] + steps: - name: Download Google AAB - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v4 with: name: google-aab - path: ./build-artifacts/google/bundle + path: ./google/bundle - name: Download Google APK - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v4 with: name: google-apk - path: ./build-artifacts/google/apk + path: ./google/apk - - name: Download Mapping File - uses: actions/download-artifact@v5 + - name: Download F-Droid APK + uses: actions/download-artifact@v4 with: - name: mapping - path: ./build-artifacts/google/mapping - - - name: Create version_info.txt - run: | - echo "versionNameBase=${{ needs.prepare-build-info.outputs.APP_VERSION_NAME }}" > ./version_info.txt - echo "versionCode=${{ needs.prepare-build-info.outputs.APP_VERSION_CODE }}" >> ./version_info.txt + name: fdroid-apk + path: ./fdroid - name: Create GitHub Release id: create_gh_release @@ -224,56 +221,10 @@ jobs: name: Release ${{ github.ref_name }} generate_release_notes: true files: | - ./build-artifacts/google/bundle/app-google-release.aab - ./build-artifacts/google/apk/app-google-release.apk - ./build-artifacts/fdroid/app-fdroid-release.apk - ./version_info.txt + ./google/bundle/app-google-release.aab + ./google/apk/app-google-release.apk + ./fdroid/app-fdroid-release.apk draft: true prerelease: ${{ contains(github.ref_name, '-internal') || contains(github.ref_name, '-closed') || contains(github.ref_name, '-open') }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Create Play Store whatsnew File - run: | - mkdir -p whatsnew - echo "For detailed release notes, please visit: ${{ steps.create_gh_release.outputs.url }}" > whatsnew/whatsnew-en-US - shell: bash - - # Attest the build artifacts for supply chain security. - # See: https://github.com/meshtastic/Meshtastic-Android/attestations - - name: Attest Build Provenance - uses: actions/attest-build-provenance@v3 - with: - subject-path: | - ./build-artifacts/google/bundle/app-google-release.aab - ./build-artifacts/google/apk/app-google-release.apk - ./build-artifacts/fdroid/app-fdroid-release.apk - - - name: Determine Play Store Track - id: play_track - run: | - TAG_NAME="${{ github.ref_name }}" - if [[ "$TAG_NAME" == *"-internal"* ]]; then - echo "track=internal" >> $GITHUB_OUTPUT - elif [[ "$TAG_NAME" == *"-closed"* ]]; then - echo "track=NewAlpha" >> $GITHUB_OUTPUT - elif [[ "$TAG_NAME" == *"-open"* ]]; then - echo "track=beta" >> $GITHUB_OUTPUT - else - echo "track=production" >> $GITHUB_OUTPUT - echo "user_fraction=0.1" >> $GITHUB_OUTPUT - echo "status=inProgress" >> $GITHUB_OUTPUT - fi - - - name: Upload to Google Play - if: success() && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') - uses: r0adkll/upload-google-play@v1.1.3 - with: - serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_JSON_KEY }} - packageName: com.geeksville.mesh - releaseFiles: ./build-artifacts/google/bundle/app-google-release.aab - track: ${{ steps.play_track.outputs.track }} - status: ${{ steps.play_track.outputs.status || (steps.play_track.outputs.track == 'internal' && 'completed' || 'draft') }} - userFraction: ${{ steps.play_track.outputs.userFraction }} - whatsNewDirectory: ./whatsnew/ - mappingFile: ./build-artifacts/google/mapping/mapping.txt diff --git a/.gitignore b/.gitignore index a8fe89f77..ba935995c 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ keystore.properties # Secrets /secrets.properties +/fastlane/play-store-credentials.json +/fastlane/report.xml diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 000000000..a3ec5a4bd --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.2 diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..7a118b49b --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gem "fastlane" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 000000000..8a8905f02 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,229 @@ +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.4.0) + aws-partitions (1.1157.0) + aws-sdk-core (3.232.0) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) + base64 + bigdecimal + jmespath (~> 1, >= 1.6.1) + logger + aws-sdk-kms (1.112.0) + aws-sdk-core (~> 3, >= 3.231.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.199.0) + aws-sdk-core (~> 3, >= 3.231.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.12.1) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + base64 (0.3.0) + bigdecimal (3.2.3) + 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.7.0) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.6.20240107) + dotenv (2.8.1) + emoji_regex (3.2.3) + excon (0.112.0) + faraday (1.10.4) + 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.1) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.1.1) + multipart-post (~> 2.0) + 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.1) + faraday (~> 1.0) + fastimage (2.4.0) + fastlane (2.228.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) + 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) + 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.4.1) + xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) + 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) + 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.8.0) + 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.5.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.8) + domain_name (~> 0.5) + httpclient (2.9.0) + mutex_m + jmespath (1.6.2) + json (2.13.2) + jwt (2.10.2) + base64 + logger (1.7.0) + mini_magick (4.13.2) + mini_mime (1.1.5) + multi_json (1.17.0) + multipart-post (2.4.1) + mutex_m (0.3.0) + nanaimo (0.4.0) + naturally (2.3.0) + nkf (0.2.0) + optparse (0.6.0) + os (1.1.4) + plist (3.7.2) + public_suffix (6.0.2) + rake (13.3.0) + 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.4.3) + rouge (3.28.0) + ruby2_keywords (0.0.5) + rubyzip (2.4.1) + security (0.1.5) + signet (0.21.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) + jwt (>= 1.5, < 4.0) + multi_json (~> 1.10) + 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) + 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.6.0) + word_wrap (1.0.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.4.0) + rexml (>= 3.3.6, < 4.0) + xcpretty (0.4.1) + rouge (~> 3.28.0) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + arm64-darwin-24 + ruby + +DEPENDENCIES + fastlane + +BUNDLED WITH + 2.6.9 diff --git a/RELEASE_PROCESS.md b/RELEASE_PROCESS.md index 3661d1692..8eff7360b 100644 --- a/RELEASE_PROCESS.md +++ b/RELEASE_PROCESS.md @@ -1,92 +1,131 @@ # Meshtastic-Android Release Process -This document outlines the steps for releasing a new version of the Meshtastic-Android application. Adhering to this process ensures consistency and helps manage the release lifecycle, leveraging automation via the `release.yml` GitHub Action. +This document outlines the steps for releasing a new version of the Meshtastic-Android application. The process is heavily automated using GitHub Actions and Fastlane, triggered by pushing a Git tag from a `release/*` branch. -**Note on Automation:** The `release.yml` GitHub Action is primarily triggered by **pushing a Git tag** matching the pattern `v*` (e.g., `v1.2.3`, `v1.2.3-open.1`). It can also be manually triggered via `workflow_dispatch` from the GitHub Actions UI. +## High-Level Overview -The workflow uses a simple and robust **"upload-only"** model. It automatically: -* Determines a `versionName` from the Git tag. -* Generates a unique, always-increasing `versionCode` based on the number of minutes since the Unix epoch. This prevents `versionCode` conflicts and will not overflow until the year 6052. -* Builds fresh F-Droid (APK) and Google (AAB, APK) artifacts for every run. -* Creates a **draft GitHub Release** and attaches the artifacts. -* Attests build provenance for the artifacts. -* **Uploads** the newly built AAB directly to the appropriate track in the Google Play Console based on the tag. +The automation is designed to be safe, repeatable, and efficient. When a new tag matching the `v*` pattern is pushed from a release branch, the `release.yml` GitHub Action workflow will: -There is no promotion of builds between tracks; every release is a new, independent upload. Finalizing and publishing the GitHub Release and the Google Play Store submission remain **manual steps**. +1. **Determine Versioning:** A `versionName` is derived from the Git tag, and a unique, always-increasing `versionCode` is generated from the current timestamp. +2. **Build in Parallel:** Two jobs run simultaneously to build the `google` and `fdroid` flavors of the app. +3. **Deploy to Google Play:** The `google` build job uses Fastlane to automatically upload the Android App Bundle (AAB) to the correct track on the Google Play Console, based on the tag name. +4. **Create a Draft GitHub Release:** After both builds are complete, a final job gathers the artifacts (AAB, Google APK, and F-Droid APK) and creates a single, consolidated **draft** release on GitHub. -## Prerequisites +Finalizing and publishing the release on both GitHub and the Google Play Console are the only **manual steps**. -Before initiating the release process, ensure the following are completed: +## Versioning and Tagging Strategy -1. **Main Branch Stability:** The `main` branch (or your chosen release branch) must be stable, with all features and bug fixes intended for the release merged and thoroughly tested. -2. **Automated Testing:** All automated tests must be passing. -3. **Versioning and Tagging Strategy:** - * Tags **must** start with `v` and follow Semantic Versioning (e.g., `vX.X.X`). - * Use the correct suffixes for the desired release track: - * **Internal/QA:** `vX.X.X-internal.Y` - * **Closed Alpha:** `vX.X.X-closed.Y` - * **Open Alpha/Beta:** `vX.X.X-open.Y` - * **Production:** `vX.X.X` (no suffix) - * **Recommendation:** Before tagging, update `VERSION_NAME_BASE` in `buildSrc/src/main/kotlin/Configs.kt` to match the `X.X.X` part of your tag. This ensures consistency for local development builds. +The entire process is driven by your Git tagging strategy. Tags **must** start with `v` and should follow Semantic Versioning. Use the correct suffix for the desired release track: -## Core Release Workflow: Triggering via Tag Push +* **Internal Track:** `vX.X.X-internal.Y` (e.g., `v2.3.5-internal.1`) +* **Closed Track:** `vX.X.X-closed.Y` (e.g., `v2.3.5-closed.1`) +* **Open Track:** `vX.X.X-open.Y` (e.g., `v2.3.5-open.1`) +* **Production Track:** `vX.X.X` (e.g., `v2.3.5`) -1. **Create and push a tag for the desired release track.** - ```bash - # This build will be uploaded and rolled out on the 'internal' track - git tag v1.2.3-internal.1 - git push origin v1.2.3-internal.1 - ``` -2. **Wait for the workflow to complete.** -3. **Verify the build** in the Google Play Console and with testers. -4. When ready to advance to the next track, create and push a new tag. - ```bash - # This will create and upload a NEW build to the 'NewAlpha' (closed alpha) track - git tag v1.2.3-closed.1 - git push origin v1.2.3-closed.1 - ``` +The `.Y` suffix is for iterations. If you find a bug in `v2.3.5-closed.1`, you would fix it on the release branch and tag the new commit as `v2.3.5-closed.2`. -## Iterating on a Bad Build +## Core Release Workflow -If you discover a critical bug in a build, the process is simple: +The entire release process happens on a dedicated release branch, allowing `main` to remain open for new feature development. -1. **Fix the Code:** Merge the necessary bug fixes into your main branch. -2. **Create a New Iteration Tag:** Create a new tag for the same release phase, simply incrementing the final number. - ```bash - # If v1.2.3-internal.1 was bad, the new build is v1.2.3-internal.2 - git tag v1.2.3-internal.2 - git push origin v1.2.3-internal.2 - ``` -3. **A New Build is Uploaded:** The workflow will run, generate a new epoch-minute-based `versionCode`, and upload a fresh build to the `internal` track. There is no risk of a `versionCode` collision. +### 1. Creating the Release Branch +First, create a `release/X.X.X` branch from a stable `main`. This branch is now "feature frozen." Only critical bug fixes should be added. -## Managing Different Release Phases (Manual Steps Post-Workflow) +As a housekeeping step, it's recommended to update the `VERSION_NAME_BASE` in `buildSrc/src/main/kotlin/Configs.kt` on this new branch. While the final release version is set by the Git tag in CI, this ensures local development builds have a sensible version name. -After the `release.yml` workflow completes, manual actions are needed on GitHub and in the Google Play Console. +```bash +git checkout main +git pull origin main +git checkout -b release/2.3.5 +# (Now, update the version in buildSrc, commit the change, and then push) +git push origin release/2.3.5 +``` -### Phase 1: Internal / QA Release -* **Tag format:** `vX.X.X-internal.Y` -* **Automated Action:** The AAB is **uploaded** to the `internal` track and rolled out automatically. -* **Manual Steps:** - 1. **GitHub:** Find the **draft release**, verify artifacts, and publish it if desired. - 2. **Google Play Console:** Verify the release has been successfully rolled out to internal testers. +### 2. Testing and Iterating on a Track +Start by deploying to the `internal` track to begin testing. -### Phase 2: Closed Alpha Release -* **Tag format:** `vX.X.X-closed.Y` -* **Automated Action:** A new AAB is built and **uploaded** as a **draft** to the `NewAlpha` track. -* **Manual Steps:** - 1. **GitHub:** Find and publish the **draft release**. - 2. **Google Play Console:** Manually review the draft release and submit it for your closed alpha testers. +**A. Create and Push a Tag:** +Tag a commit on the release branch to trigger the automation. +```bash +# Ensure you are on the release branch +git checkout release/2.3.5 -### Phase 3: Open Alpha / Beta Release -* **Tag format:** `vX.X.X-open.Y` -* **Automated Action:** A new AAB is built and **uploaded** as a **draft** to the `beta` track. -* **Manual Steps:** - 1. **GitHub:** Find and publish the **draft pre-release**. - 2. **Google Play Console:** Manually review the draft, add release notes, and submit it. +# Tag for the "Internal" track +git tag v2.3.5-internal.1 +git push origin v2.3.5-internal.1 +``` -### Phase 4: Production Release -* **Tag format:** `vX.X.X` -* **Automated Action:** A new AAB is built and **uploaded** to the `production` track. By default, it is configured for a 10% staged rollout. -* **Manual Steps:** - 1. **GitHub:** Find the **draft release**. **Crucially, uncheck "This is a pre-release"** before publishing. - 2. **Google Play Console:** Manually review the release, add release notes, and **start the staged rollout**. +**B. Monitor and Verify:** +Monitor the workflow in GitHub Actions. Once complete, verify the build in the Google Play Console and with your internal testers. + +**C. Apply Fixes (If Necessary):** +If a bug is found, commit the fix to the release branch. Remember to also cherry-pick or merge this fix back to `main`. Then, create an iterated tag. +```bash +# Assuming you've committed a fix +git tag v2.3.5-internal.2 +git push origin v2.3.5-internal.2 +``` +This will upload a new, fixed build to the same `internal` track. Repeat this process until the build is stable. + +### 3. Promoting to the Next Track +Once you are confident that a build is stable, you can "promote" it to a wider audience by tagging the **exact same commit** for the next track. + +```bash +# The commit tagged as v2.3.5-internal.2 is stable and ready for the "Closed" track +git tag v2.3.5-closed.1 +git push origin v2.3.5-closed.1 +``` +This triggers the workflow again, but this time it will send the build to the `NewAlpha` track for your closed testers. You can then continue the cycle of testing, fixing, and promoting all the way to production. + +### 4. Merging Back to `main` +After the final production release is complete and verified, merge the release branch back into `main` to ensure any hotfixes are included. Then, delete the release branch. +```bash +git checkout main +git pull origin main +git merge release/2.3.5 +git push origin main +git branch -d release/2.3.5 +git push origin --delete release/2.3.5 +``` + +## Manual Finalization Steps + +### For Internal Releases + +* **Automated Action:** The AAB is uploaded to the `internal` track and is **rolled out to 100% of testers automatically**. +* **Your Manual Step:** + 1. **Verify the build** in the Google Play Console and with your internal testers. + +### For Closed Releases + +* **Automated Action:** The AAB is uploaded to the `NewAlpha` track and is **rolled out to 100% of testers automatically**. +* **Your Manual Step:** + 1. **Verify the build** in the Google Play Console and with your closed track testers. + +### For Open Releases + +* **Automated Action:** The AAB is uploaded to the `beta` track and begins a **staged rollout to 25% of your open track testers automatically**. +* **Your Manual Steps:** + 1. **Verify the build** in the Google Play Console. + 2. **(Optional)** Go to the GitHub "Releases" page, find the **draft release**, and publish it so your open track testers can see the official release notes. + +### For Production Releases + +* **Automated Action:** + * The AAB is uploaded to the `production` track in a **draft** state. It is **not** rolled out to any users. + * A corresponding **draft** release is created on GitHub with all build artifacts. +* **Your Manual Steps:** + 1. **Publish on GitHub First:** Go to the GitHub "Releases" page and find the draft. Review the release notes and artifacts, then **Publish release**. This makes the release notes publicly visible. + 2. **Promote on Google Play Second:** *After* publishing on GitHub, go to your Google Play Console. Find the draft release, review it, and then proceed to **start the rollout to production**. + +## Monitoring the Release + +After a release has been rolled out to users (especially for Open and Production), it is crucial to monitor its performance. + +* **Google Play Console:** Keep a close eye on the **Vitals** section for your app. Pay special attention to the crash rate and ANR (Application Not Responding) rate. A sudden spike in these numbers is a strong indicator of a problem. +* **Datadog:** Check your Datadog dashboards for any unusual trends or new errors that may have been introduced with the release. +* **Crashlytics:** Review crash reports in Firebase Crashlytics to identify any new issues that users may be experiencing. +* **User Reviews:** Monitor user reviews on the Google Play Store for any negative feedback or reports of bugs. +* **Community Feedback:** Monitor Discord, GitHub Issues, and community forums for feedback from users who have received the update. + +If you identify a critical issue, be prepared to halt the rollout in the Google Play Console and tag a new, fixed version. diff --git a/app/src/main/play/listings/en-US/full-description.txt b/app/src/main/play/listings/en-US/full-description.txt deleted file mode 100644 index 07c4e52a6..000000000 --- a/app/src/main/play/listings/en-US/full-description.txt +++ /dev/null @@ -1,3 +0,0 @@ -This is a beta release of the meshtastic.org project. We'd love you to try it and tell us what you think. You'll need to buy an inexpensive ($30ish) radio from a variety of vendors to use this application, see our website for details. We don't make these devices. - -***Please*** if you encounter problems or have questions: post on our forum at https://github.com/orgs/meshtastic/discussions and we'll work together to fix them (we are volunteer hobbyists). We would really appreciate good Google reviews if you think this is a good project. diff --git a/app/src/main/play/listings/en-US/graphics/icon/1.png b/app/src/main/play/listings/en-US/graphics/icon/1.png deleted file mode 100644 index c4069fac7beb246b2e5a1a30914df1853852e1f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16059 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.org. This application is made by volunteers. We are friendly and actively respond to forum posts with any questions you have. Post at https://github.com/orgs/meshtastic/discussions and we'll help. diff --git a/app/src/main/play/release-notes/en-US/beta.txt b/app/src/main/play/release-notes/en-US/beta.txt deleted file mode 100644 index f87731416..000000000 --- a/app/src/main/play/release-notes/en-US/beta.txt +++ /dev/null @@ -1 +0,0 @@ -For more information visit meshtastic.org. This application is made by volunteers. We are friendly and actively respond to forum posts with any questions you have. Post at https://github.com/orgs/meshtastic/discussions and we'll help. diff --git a/app/src/main/play/release-notes/en-US/internal.txt b/app/src/main/play/release-notes/en-US/internal.txt deleted file mode 100644 index 322aae877..000000000 --- a/app/src/main/play/release-notes/en-US/internal.txt +++ /dev/null @@ -1 +0,0 @@ -An internal build diff --git a/app/src/main/play/release-notes/en-US/production.txt b/app/src/main/play/release-notes/en-US/production.txt deleted file mode 100644 index f87731416..000000000 --- a/app/src/main/play/release-notes/en-US/production.txt +++ /dev/null @@ -1 +0,0 @@ -For more information visit meshtastic.org. This application is made by volunteers. We are friendly and actively respond to forum posts with any questions you have. Post at https://github.com/orgs/meshtastic/discussions and we'll help. diff --git a/fastlane/Appfile b/fastlane/Appfile new file mode 100644 index 000000000..619a4638e --- /dev/null +++ b/fastlane/Appfile @@ -0,0 +1,2 @@ +json_key_file("./fastlane/play-store-credentials.json") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one +package_name("com.geeksville.mesh") # e.g. com.krausefx.app diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 000000000..b68ab9dd3 --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,86 @@ +# This file contains the fastlane.tools configuration +# You can find the documentation at https://docs.fastlane.tools +# +# For a list of all available actions, check out +# +# https://docs.fastlane.tools/actions +# +# For a list of all available plugins, check out +# +# https://docs.fastlane.tools/plugins/available-plugins +# + +# Uncomment the line if you want fastlane to automatically update itself +# update_fastlane + +default_platform(:android) + +platform :android do + desc "Runs all the tests" + lane :test do + gradle(task: "test") + end + + desc "Deploy a new version to the internal track on Google Play" + lane :internal do + aab_path = build_google_release + upload_to_play_store( + track: 'internal', + aab: aab_path, + release_status: 'completed' + ) + end + + desc "Deploy a new version to the closed track on Google Play" + lane :closed do + aab_path = build_google_release + upload_to_play_store( + track: 'NewAlpha', + aab: aab_path, + release_status: 'completed' + ) + end + + desc "Deploy a new version to the open track on Google Play" + lane :open do + aab_path = build_google_release + upload_to_play_store( + track: 'beta', + aab: aab_path, + release_status: 'inProgress', + rollout: '0.25' + ) + end + + desc "Deploy a new version to the production track on Google Play" + lane :production do + aab_path = build_google_release + upload_to_play_store( + track: 'production', + aab: aab_path, + release_status: 'draft' + ) + end + + desc "Build the F-Droid release" + lane :fdroid_build do + gradle( + task: "clean assembleFdroidRelease", + properties: { + "android.injected.version.name" => ENV['VERSION_NAME'], + "android.injected.version.code" => ENV['VERSION_CODE'] + } + ) + end + + private_lane :build_google_release do + gradle( + task: "clean bundleGoogleRelease assembleGoogleRelease", + print_command: false, + properties: { + "android.injected.version.name" => ENV['VERSION_NAME'], + "android.injected.version.code" => ENV['VERSION_CODE'] + } + ) + end +end diff --git a/fastlane/README.md b/fastlane/README.md new file mode 100644 index 000000000..7ec1207f1 --- /dev/null +++ b/fastlane/README.md @@ -0,0 +1,48 @@ +fastlane documentation +---- + +# Installation + +Make sure you have the latest version of the Xcode command line tools installed: + +```sh +xcode-select --install +``` + +For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane) + +# Available Actions + +## Android + +### android test + +```sh +[bundle exec] fastlane android test +``` + +Runs all the tests + +### android beta + +```sh +[bundle exec] fastlane android beta +``` + +Submit a new Beta Build to Crashlytics Beta + +### android deploy + +```sh +[bundle exec] fastlane android deploy +``` + +Deploy a new version to the Google Play + +---- + +This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. + +More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools). + +The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools). diff --git a/app/src/main/play/contact-email.txt b/fastlane/metadata/android/contact-email.txt similarity index 100% rename from app/src/main/play/contact-email.txt rename to fastlane/metadata/android/contact-email.txt diff --git a/app/src/main/play/contact-website.txt b/fastlane/metadata/android/contact-website.txt similarity index 100% rename from app/src/main/play/contact-website.txt rename to fastlane/metadata/android/contact-website.txt diff --git a/app/src/main/play/default-language.txt b/fastlane/metadata/android/default-language.txt similarity index 100% rename from app/src/main/play/default-language.txt rename to fastlane/metadata/android/default-language.txt 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..0553de284 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/default.txt @@ -0,0 +1 @@ +For detailed release notes, please visit: https://github.com/meshtastic/Meshtastic-Android/releases/ \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt new file mode 100644 index 000000000..84a470095 --- /dev/null +++ b/fastlane/metadata/android/en-US/full_description.txt @@ -0,0 +1,21 @@ +Meshtastic is a tool for using Android devices with open-source, off-grid mesh radios. This app is the main client for the Meshtastic project, allowing you to manage your mesh devices and communicate with other users. + +For more information about the Meshtastic project, please visit our website: [meshtastic.org](https://www.meshtastic.org). The firmware that runs on the radio devices is a separate open-source project, which you can find here: [https://github.com/meshtastic/Meshtastic-device](https://github.com/meshtastic/Meshtastic-device). + +**Community and Support** + +This project is currently in beta. We would love to hear from you! If you have questions, feedback, or encounter any problems, please join our friendly and active community: + +* **Discussion Forum:** [https://github.com/orgs/meshtastic/discussions](https://github.com/orgs/meshtastic/discussions) +* **Discord:** [https://discord.gg/meshtastic](https://discord.gg/meshtastic) +* **Report an Issue:** [https://github.com/meshtastic/Meshtastic-Android/issues](https://github.com/meshtastic/Meshtastic-Android/issues) + +**Documentation** + +To learn more about the features and capabilities of this app and Meshtastic, please view our official documentation: +[**View Documentation**](https://meshtastic.org/docs/) + +**Translations** + +You can help translate the app into your native language using Crowdin: +[https://crowdin.meshtastic.org/android](https://crowdin.meshtastic.org/android) diff --git a/app/src/main/play/listings/en-US/graphics/feature-graphic/1.png b/fastlane/metadata/android/en-US/images/featureGraphic.jpeg similarity index 100% rename from app/src/main/play/listings/en-US/graphics/feature-graphic/1.png rename to fastlane/metadata/android/en-US/images/featureGraphic.jpeg diff --git a/fastlane/metadata/android/en-US/images/icon.png b/fastlane/metadata/android/en-US/images/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e3e10fb55a859496ef8fe105935abd723802f4d6 GIT binary patch literal 13234 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 literal 0 HcmV?d00001 diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/1.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/1_en-US.png similarity index 100% rename from app/src/main/play/listings/en-US/graphics/phone-screenshots/1.png rename to fastlane/metadata/android/en-US/images/phoneScreenshots/1_en-US.png diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/2.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/2_en-US.png similarity index 100% rename from app/src/main/play/listings/en-US/graphics/phone-screenshots/2.png rename to fastlane/metadata/android/en-US/images/phoneScreenshots/2_en-US.png diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/3.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/3_en-US.png similarity index 100% rename from app/src/main/play/listings/en-US/graphics/phone-screenshots/3.png rename to fastlane/metadata/android/en-US/images/phoneScreenshots/3_en-US.png diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/4.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/4_en-US.png similarity index 100% rename from app/src/main/play/listings/en-US/graphics/phone-screenshots/4.png rename to fastlane/metadata/android/en-US/images/phoneScreenshots/4_en-US.png diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/5.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/5_en-US.png similarity index 100% rename from app/src/main/play/listings/en-US/graphics/phone-screenshots/5.png rename to fastlane/metadata/android/en-US/images/phoneScreenshots/5_en-US.png diff --git a/fastlane/metadata/android/en-US/short_description.txt b/fastlane/metadata/android/en-US/short_description.txt new file mode 100644 index 000000000..076a74f8a --- /dev/null +++ b/fastlane/metadata/android/en-US/short_description.txt @@ -0,0 +1 @@ +The official app for Meshtastic, an open-source, off-grid, mesh GPS radio. \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/title.txt b/fastlane/metadata/android/en-US/title.txt new file mode 100644 index 000000000..c3ff69353 --- /dev/null +++ b/fastlane/metadata/android/en-US/title.txt @@ -0,0 +1 @@ +Meshtastic \ No newline at end of file diff --git a/app/src/main/play/listings/en-US/video-url.txt b/fastlane/metadata/android/en-US/video.txt similarity index 100% rename from app/src/main/play/listings/en-US/video-url.txt rename to fastlane/metadata/android/en-US/video.txt