From 496de4776656192d004a8ac5e47a0efce4e5fe5f Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Wed, 25 Jun 2025 23:16:55 +0000 Subject: [PATCH] Refactor: Improve GitHub release workflow and build configuration (#2251) --- .github/workflows/release.yml | 307 +++++++++++++----- app/build.gradle.kts | 14 +- .../geeksville/mesh/database/dao/PacketDao.kt | 4 +- build.gradle.kts | 13 +- buildSrc/src/main/kotlin/Configs.kt | 1 - gradle.properties | 24 +- mesh_service_example/build.gradle.kts | 23 +- network/build.gradle.kts | 10 +- 8 files changed, 284 insertions(+), 112 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d02a60ff6..b7e5dd1e0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,108 +2,253 @@ name: Make Release on: workflow_dispatch: + inputs: + branch: + description: 'Branch to build from' + required: true + default: 'main' # Or your most common release branch + type: string + create_github_release: + description: 'Create a GitHub Release (and upload assets)' + required: true + default: true + type: boolean -permissions: write-all +permissions: write-all # Needed for creating releases and uploading assets jobs: + # Job to prepare common environment variables like version + prepare-release-info: + runs-on: ubuntu-latest + outputs: + versionCode: ${{ steps.get_version.outputs.versionCode }} + versionName: ${{ steps.get_version.outputs.versionName }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.branch }} + submodules: 'recursive' - release-build: + - name: Get `versionCode` & `versionName` + id: get_version + run: | + echo "versionCode=$(grep -oP 'VERSION_CODE = \K\d+' ./buildSrc/src/main/kotlin/Configs.kt)" >> $GITHUB_OUTPUT + echo "versionName=$(grep -oP 'VERSION_NAME = \"\K[^\"]+' ./buildSrc/src/main/kotlin/Configs.kt)" >> $GITHUB_OUTPUT + + # Job for F-Droid build + build-fdroid: + needs: prepare-release-info # Depends on version info runs-on: ubuntu-latest if: github.repository == 'meshtastic/Meshtastic-Android' + outputs: + apk_path: app/build/outputs/apk/fdroid/release/app-fdroid-release.apk + apk_name: fdroidRelease-${{ needs.prepare-release-info.outputs.versionName }}.apk steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.branch }} + submodules: 'recursive' - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: 'recursive' + - name: Cache Gradle packages + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- - - name: Get `versionCode` & `versionName` - run: | - echo "versionCode=$(grep -oP 'VERSION_CODE = \K\d+' ./buildSrc/src/main/kotlin/Configs.kt)" >> $GITHUB_ENV - echo "versionName=$(grep -oP 'VERSION_NAME = \"\K[^\"]+' ./buildSrc/src/main/kotlin/Configs.kt)" >> $GITHUB_ENV + - name: Validate Gradle wrapper + uses: gradle/actions/wrapper-validation@v4 - - name: Validate Gradle wrapper - uses: gradle/actions/wrapper-validation@v4 + - name: Load secrets (only keystore for F-Droid) + 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: Load secrets - run: | - rm ./app/google-services.json - echo $GSERVICES > ./app/google-services.json - echo $KEYSTORE | base64 -di > ./app/$KEYSTORE_FILENAME - echo "$KEYSTORE_PROPERTIES" > ./keystore.properties - echo -e "versionCode=$versionCode\nversionName=$versionName" > ./version_info.txt - env: + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'jetbrains' + + - name: Build F-Droid release + run: ./gradlew assembleFdroidRelease + + - name: Upload F-Droid APK artifact (for release job) + uses: actions/upload-artifact@v4 + with: + name: fdroid-apk + path: app/build/outputs/apk/fdroid/release/app-fdroid-release.apk + retention-days: 1 # Keep for a short period as it will be uploaded to release + + # Job for Play Store build + build-google: + needs: prepare-release-info # Depends on version info + runs-on: ubuntu-latest + if: github.repository == 'meshtastic/Meshtastic-Android' + outputs: + aab_path: app/build/outputs/bundle/googleRelease/app-google-release.aab + aab_name: googleRelease-${{ needs.prepare-release-info.outputs.versionName }}.aab + apk_path: app/build/outputs/apk/google/release/app-google-release.apk + apk_name: googleRelease-${{ needs.prepare-release-info.outputs.versionName }}.apk + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.branch }} + submodules: 'recursive' + + - name: Cache Gradle packages + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Validate Gradle wrapper + uses: gradle/actions/wrapper-validation@v4 + + - name: Load secrets + run: | + rm -f ./app/google-services.json # Ensure clean state + echo $GSERVICES > ./app/google-services.json + echo $KEYSTORE | base64 -di > ./app/$KEYSTORE_FILENAME + echo "$KEYSTORE_PROPERTIES" > ./keystore.properties + env: GSERVICES: ${{ secrets.GSERVICES }} KEYSTORE: ${{ secrets.KEYSTORE }} KEYSTORE_FILENAME: ${{ secrets.KEYSTORE_FILENAME }} KEYSTORE_PROPERTIES: ${{ secrets.KEYSTORE_PROPERTIES }} - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'jetbrains' - # Note: we don't use caches on release builds because we don't want to accidentally not have a virgin build machine + - name: Set up JDK 24 + uses: actions/setup-java@v4 + with: + java-version: '24' + distribution: 'jetbrains' - - name: Build F-Droid release - run: ./gradlew assembleFdroidRelease + - name: Build Play Store release + run: ./gradlew bundleGoogleRelease assembleGoogleRelease - - name: Enable Crashlytics - run: sed -i 's/USE_CRASHLYTICS = false/USE_CRASHLYTICS = true/g' ./buildSrc/src/main/kotlin/Configs.kt + - name: Upload Play Store AAB artifact (for release job) + uses: actions/upload-artifact@v4 + with: + name: google-aab + path: app/build/outputs/bundle/googleRelease/app-google-release.aab + retention-days: 1 - - name: Build Play Store release - run: ./gradlew bundleGoogleRelease assembleGoogleRelease + - name: Upload Play Store APK artifact (for release job) + uses: actions/upload-artifact@v4 + with: + name: google-apk + path: app/build/outputs/apk/google/release/app-google-release.apk + retention-days: 1 - - name: Create GitHub release - uses: actions/create-release@v1 - id: create_release - with: - draft: true - prerelease: true - release_name: Meshtastic Android ${{ env.versionName }} alpha - tag_name: ${{ env.versionName }} - body: | - Autogenerated by github action, developer should edit as required before publishing... - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Job to create GitHub release and upload assets (runs after builds if enabled) + create-github-release: + needs: [ prepare-release-info, build-fdroid, build-google ] + runs-on: ubuntu-latest + # Only run this job if the input create_github_release is true + if: github.repository == 'meshtastic/Meshtastic-Android' && github.event.inputs.create_github_release == 'true' + steps: + # We need version info again for release name and tag + # Alternatively, we could pass it as an artifact, but this is simpler for just two values + - name: Checkout code (for version_info.txt and version retrieval) + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.branch }} # Checkout the specified branch - - name: Add F-Droid APK to release - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: app/build/outputs/apk/fdroid/release/app-fdroid-release.apk - asset_name: fdroidRelease-${{ env.versionName }}.apk - asset_content_type: application/zip + - name: Get `versionCode` & `versionName` (again for this job's env) + id: get_version # Unique ID within this job + run: | + echo "versionCode=$(grep -oP 'VERSION_CODE = \K\d+' ./buildSrc/src/main/kotlin/Configs.kt)" >> $GITHUB_ENV + echo "versionName=$(grep -oP 'VERSION_NAME = \"\K[^\"]+' ./buildSrc/src/main/kotlin/Configs.kt)" >> $GITHUB_ENV - - name: Add Play Store AAB to release - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: app/build/outputs/bundle/googleRelease/app-google-release.aab - asset_name: googleRelease-${{ env.versionName }}.aab - asset_content_type: application/zip + - name: Create version_info.txt + run: | + echo -e "versionCode=${{ env.versionCode }}\nversionName=${{ env.versionName }}" > ./version_info.txt - - name: Add Play Store APK to release - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: app/build/outputs/apk/google/release/app-google-release.apk - asset_name: googleRelease-${{ env.versionName }}.apk - asset_content_type: application/zip + - name: Download F-Droid APK + uses: actions/download-artifact@v4 + with: + name: fdroid-apk + path: ./fdroid-apk-download # Download to a specific folder - # https://github.com/f-droid/fdroiddata/blob/main/metadata/com.geeksville.mesh.yml#L34 - - name: Add `version_info.txt` to release - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: version_info.txt - asset_name: version_info.txt - asset_content_type: text/plain + - name: Download Google AAB + uses: actions/download-artifact@v4 + with: + name: google-aab + path: ./google-aab-download + + - name: Download Google APK + uses: actions/download-artifact@v4 + with: + name: google-apk + path: ./google-apk-download + + - name: Create GitHub release + uses: actions/create-release@v1 + id: create_release_step + with: + draft: true + prerelease: true + release_name: Meshtastic Android ${{ env.versionName }} alpha (Branch ${{ github.event.inputs.branch }}) + tag_name: ${{ env.versionName }} # Consider making tag unique if version can be same across branches, e.g., ${{ env.versionName }}-${{ github.event.inputs.branch }} + target_commitish: ${{ github.event.inputs.branch }} + body: | + Release built from branch: `${{ github.event.inputs.branch }}` + Version: ${{ env.versionName }} (Code: ${{ env.versionCode }}) + + Autogenerated by GitHub Action. Please review and edit before publishing. + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Add F-Droid APK to release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release_step.outputs.upload_url }} + asset_path: ./fdroid-apk-download/app-fdroid-release.apk # Path from download + asset_name: fdroidRelease-${{ env.versionName }}.apk + asset_content_type: application/vnd.android.package-archive + + - name: Add Play Store AAB to release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release_step.outputs.upload_url }} + asset_path: ./google-aab-download/app-google-release.aab # Path from download + asset_name: googleRelease-${{ env.versionName }}.aab + asset_content_type: application/octet-stream # More generic for AAB + + - name: Add Play Store APK to release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release_step.outputs.upload_url }} + asset_path: ./google-apk-download/app-google-release.apk # Path from download + asset_name: googleRelease-${{ env.versionName }}.apk + asset_content_type: application/vnd.android.package-archive + + - name: Add version_info.txt to release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release_step.outputs.upload_url }} + asset_path: ./version_info.txt + asset_name: version_info.txt + asset_content_type: text/plain \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 99747858a..bbec90e60 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -72,11 +72,9 @@ android { } create("google") { dimension = "default" - if (Configs.USE_CRASHLYTICS) { - // Enable Firebase Crashlytics for Google Play builds - apply(plugin = libs.plugins.google.services.get().pluginId) - apply(plugin = libs.plugins.firebase.crashlytics.get().pluginId) - } + // Enable Firebase Crashlytics for Google Play builds + apply(plugin = libs.plugins.google.services.get().pluginId) + apply(plugin = libs.plugins.firebase.crashlytics.get().pluginId) } } buildTypes { @@ -126,7 +124,7 @@ android { aidl = true buildConfig = true } - // Configure the build-logic plugins to target JDK 17 + // Configure the build-logic plugins to target JDK 21 // This matches the JDK used to build the project, and is not related to what is running on device. compileOptions { sourceCompatibility = JavaVersion.VERSION_21 @@ -144,7 +142,9 @@ android { kotlin { compilerOptions { - jvmTarget = JvmTarget.JVM_21 // match Java 21 + jvmToolchain { + JvmTarget.JVM_21 + } freeCompilerArgs.addAll( "-opt-in=kotlin.RequiresOptIn", "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", diff --git a/app/src/main/java/com/geeksville/mesh/database/dao/PacketDao.kt b/app/src/main/java/com/geeksville/mesh/database/dao/PacketDao.kt index 122f5e644..94753113e 100644 --- a/app/src/main/java/com/geeksville/mesh/database/dao/PacketDao.kt +++ b/app/src/main/java/com/geeksville/mesh/database/dao/PacketDao.kt @@ -85,6 +85,7 @@ interface PacketDao { @Upsert suspend fun insert(packet: Packet) + @Transaction @Query( """ SELECT * FROM packet @@ -93,7 +94,6 @@ interface PacketDao { ORDER BY received_time DESC """ ) - @Transaction fun getMessagesFrom(contact: String): Flow> @Query( @@ -164,6 +164,7 @@ interface PacketDao { ) suspend fun getDataPackets(): List + @Transaction @Query( """ SELECT * FROM packet @@ -174,6 +175,7 @@ interface PacketDao { ) suspend fun getPacketById(requestId: Int): Packet? + @Transaction @Query("SELECT * FROM packet WHERE packet_id = :packetId LIMIT 1") suspend fun getPacketByPacketId(packetId: Int): PacketEntity? diff --git a/build.gradle.kts b/build.gradle.kts index 04b2a989b..464807150 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,17 +24,8 @@ buildscript { classpath(libs.agp) classpath(libs.kotlin.gradle.plugin) classpath(libs.kotlin.serialization) - - // Google Play Services and Firebase Crashlytics - // If you want to use Firebase Crashlytics, - // set Configs.USE_CRASHLYTICS to true in: - // buildSrc/src/main/kotlin/Configs.kt - // Disabled by default for fdroid builds - if (Configs.USE_CRASHLYTICS) { - classpath(libs.google.services) - classpath(libs.firebase.crashlytics.gradle) - } - + classpath(libs.google.services) + classpath(libs.firebase.crashlytics.gradle) classpath(libs.protobuf.gradle.plugin) classpath(libs.hilt.android.gradle.plugin) } diff --git a/buildSrc/src/main/kotlin/Configs.kt b/buildSrc/src/main/kotlin/Configs.kt index d6eb87546..6c799b302 100644 --- a/buildSrc/src/main/kotlin/Configs.kt +++ b/buildSrc/src/main/kotlin/Configs.kt @@ -22,7 +22,6 @@ object Configs { const val COMPILE_SDK = 36 const val VERSION_CODE = 30625 // format is Mmmss (where M is 1+the numeric major number) const val VERSION_NAME = "2.6.25" - const val USE_CRASHLYTICS = false // Set to true if you want to use Firebase Crashlytics const val MIN_FW_VERSION = "2.5.14" // Minimum device firmware version supported by this app const val ABS_MIN_FW_VERSION = "2.3.15" // Minimum device firmware version supported by this app } diff --git a/gradle.properties b/gradle.properties index ad21e3f2c..3f255313e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,20 @@ +# +# Copyright (c) 2025 Meshtastic LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* @@ -13,10 +30,9 @@ org.gradle.jvmargs=-Xmx6g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true + org.gradle.parallel=true -# Not encouraged by Gradle and can produce weird results. Wait for isolated projects instead. -org.gradle.configureondemand=false +org.gradle.configureondemand=true # Enable caching between builds. org.gradle.caching=true @@ -29,6 +45,8 @@ org.gradle.configuration-cache=true # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true +android.enableJetifier=false + # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official diff --git a/mesh_service_example/build.gradle.kts b/mesh_service_example/build.gradle.kts index 302b998d5..138c8531e 100644 --- a/mesh_service_example/build.gradle.kts +++ b/mesh_service_example/build.gradle.kts @@ -17,6 +17,23 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget +/* + * Copyright (c) 2025 Meshtastic LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + /* * Copyright (c) 2025 Meshtastic LLC * @@ -68,8 +85,8 @@ android { } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } buildFeatures { aidl = true @@ -78,7 +95,7 @@ android { kotlin { compilerOptions { - jvmTarget = JvmTarget.JVM_11 // match Java 11 + jvmTarget = JvmTarget.JVM_21 } } diff --git a/network/build.gradle.kts b/network/build.gradle.kts index f07f330c4..e8090f545 100644 --- a/network/build.gradle.kts +++ b/network/build.gradle.kts @@ -30,15 +30,15 @@ android { buildFeatures { buildConfig = true } - compileSdk = 35 + compileSdk = Configs.COMPILE_SDK defaultConfig { - minSdk = 21 + minSdk = Configs.MIN_SDK_VERSION } namespace = "com.geeksville.mesh.network" compileOptions { - sourceCompatibility(JavaVersion.VERSION_17) - targetCompatibility(JavaVersion.VERSION_17) + sourceCompatibility(JavaVersion.VERSION_21) + targetCompatibility(JavaVersion.VERSION_21) } flavorDimensions += "default" @@ -54,7 +54,7 @@ android { kotlin { compilerOptions { - jvmTarget.set(JvmTarget.JVM_17) + jvmTarget.set(JvmTarget.JVM_21) } }