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 c4069fac7..000000000 Binary files a/app/src/main/play/listings/en-US/graphics/icon/1.png and /dev/null differ diff --git a/app/src/main/play/listings/en-US/short-description.txt b/app/src/main/play/listings/en-US/short-description.txt deleted file mode 100644 index 7d1be0bfd..000000000 --- a/app/src/main/play/listings/en-US/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -An inexpensive open-source GPS mesh radio for hiking, skiing, flying, marching. diff --git a/app/src/main/play/listings/en-US/title.txt b/app/src/main/play/listings/en-US/title.txt deleted file mode 100644 index efef08b61..000000000 --- a/app/src/main/play/listings/en-US/title.txt +++ /dev/null @@ -1 +0,0 @@ -Meshtastic diff --git a/app/src/main/play/release-notes/en-US/alpha.txt b/app/src/main/play/release-notes/en-US/alpha.txt deleted file mode 100644 index f87731416..000000000 --- a/app/src/main/play/release-notes/en-US/alpha.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/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 000000000..e3e10fb55 Binary files /dev/null and b/fastlane/metadata/android/en-US/images/icon.png differ 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