diff --git a/.github/actions/gradle-setup/action.yml b/.github/actions/gradle-setup/action.yml new file mode 100644 index 000000000..c04c27a14 --- /dev/null +++ b/.github/actions/gradle-setup/action.yml @@ -0,0 +1,38 @@ +name: Gradle Setup +description: Setup Java and Gradle for KMP builds +inputs: + cache_read_only: + description: 'Whether Gradle cache is read-only' + default: 'true' + jdk_distribution: + description: 'JDK distribution (temurin or jetbrains)' + default: 'temurin' +runs: + using: composite + steps: + - name: Checkout code + uses: actions/checkout@v6 + with: + fetch-depth: 0 + submodules: 'recursive' + + - name: Validate Gradle Wrapper + uses: gradle/actions/wrapper-validation@v6 + + - name: Set up JDK 21 + uses: actions/setup-java@v5 + with: + java-version: '21' + distribution: ${{ inputs.jdk_distribution }} + token: ${{ github.token }} + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v6 + with: + cache-read-only: ${{ inputs.cache_read_only }} + cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} + cache-cleanup: on-success + build-scan-publish: true + build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' + build-scan-terms-of-use-agree: 'yes' + add-job-summary: always \ No newline at end of file diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 07ee9eae8..48aaf6d14 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -8,7 +8,7 @@ For execution-focused recipes, see `docs/agent-playbooks/README.md`. Meshtastic-Android is a Kotlin Multiplatform (KMP) application for off-grid, decentralized mesh networks. The goal is to decouple business logic from the Android framework, enabling future expansion to iOS and other platforms while maintaining a high-performance native Android experience. - **Language:** Kotlin (primary), AIDL. -- **Build System:** Gradle (Kotlin DSL). JDK 17 is REQUIRED. +- **Build System:** Gradle (Kotlin DSL). JDK 21 is REQUIRED. - **Target SDK:** API 36. Min SDK: API 26 (Android 8.0). - **Flavors:** - `fdroid`: Open source only, no tracking/analytics. @@ -99,7 +99,7 @@ Meshtastic-Android is a Kotlin Multiplatform (KMP) application for off-grid, dec ## 4. Execution Protocol ### A. Environment Setup -1. **JDK 17 MUST be used** to prevent Gradle sync/build failures. +1. **JDK 21 MUST be used** to prevent Gradle sync/build failures. 2. **Secrets:** You must copy `secrets.defaults.properties` to `local.properties`: ```properties MAPS_API_KEY=dummy_key @@ -128,7 +128,7 @@ Always run commands in the following order to ensure reliability. Do not attempt ./gradlew testFdroidDebug testGoogleDebug # Flavor-specific unit tests ./gradlew lintFdroidDebug lintGoogleDebug # Flavor-specific lint checks ``` -*Note: If testing Compose UI on the JVM (Robolectric) with Java 17, pin your tests to `@Config(sdk = [34])` to avoid SDK 35 compatibility crashes.* +*Note: If testing Compose UI on the JVM (Robolectric) with Java 21, pin your tests to `@Config(sdk = [34])` to avoid SDK 35 compatibility crashes.* **CI workflow conventions (GitHub Actions):** - Reusable CI is split into a host job and an Android matrix job in `.github/workflows/reusable-check.yml`. @@ -154,6 +154,6 @@ Update documentation continuously as part of the same change. If you modify arch ## 5. Troubleshooting - **Build Failures:** Check `gradle/libs.versions.toml` for dependency conflicts. - **Missing Secrets:** Check `local.properties`. -- **JDK Version:** JDK 17 is required. +- **JDK Version:** JDK 21 is required. - **Configuration Cache:** Add `--no-configuration-cache` flag if cache-related issues persist. - **Koin Injection Failures:** Verify the KMP component is included in `app` root module wiring (`AppKoinModule`). \ No newline at end of file diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 250e34253..e67a217c7 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -70,7 +70,7 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' # See 'Supported distributions' for available options - java-version: '17' + java-version: '21' token: ${{ github.token }} # Initializes the CodeQL tools for scanning. diff --git a/.github/workflows/dependency-submission.yml b/.github/workflows/dependency-submission.yml index 4e14783bf..10535d723 100644 --- a/.github/workflows/dependency-submission.yml +++ b/.github/workflows/dependency-submission.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/setup-java@v5 with: distribution: temurin - java-version: 17 + java-version: 21 token: ${{ github.token }} - name: Generate and submit dependency graph diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 12cee4d46..9a17020bc 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -47,15 +47,8 @@ jobs: submodules: 'recursive' ref: ${{ inputs.ref || '' }} - - name: Set up JDK 17 - uses: actions/setup-java@v5 - with: - java-version: '17' - distribution: 'temurin' - token: ${{ github.token }} - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v6 + - name: Gradle Setup + uses: ./.github/actions/gradle-setup - name: Build Dokka HTML documentation run: ./gradlew dokkaGeneratePublicationHtml diff --git a/.github/workflows/publish-core.yml b/.github/workflows/publish-core.yml index 59c22505a..bc893b6b0 100644 --- a/.github/workflows/publish-core.yml +++ b/.github/workflows/publish-core.yml @@ -23,19 +23,8 @@ jobs: with: submodules: 'recursive' - - name: Set up JDK 17 - uses: actions/setup-java@v5 - with: - java-version: '17' - distribution: 'temurin' - token: ${{ github.token }} - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v6 - with: - build-scan-publish: true - build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' - build-scan-terms-of-use-agree: 'yes' + - name: Gradle Setup + uses: ./.github/actions/gradle-setup - name: Configure Version id: version diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4b7f6f4b9..07ef63ddc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -119,25 +119,10 @@ jobs: -Dorg.gradle.workers.max=4 -Dkotlin.daemon.jvm.options=-Xmx2g -XX:+UseParallelGC steps: - - name: Checkout code - uses: actions/checkout@v6 + - name: Gradle Setup + uses: ./.github/actions/gradle-setup with: - ref: ${{ inputs.tag_name }} - fetch-depth: 0 - submodules: 'recursive' - - name: Set up JDK 17 - uses: actions/setup-java@v5 - with: - java-version: '17' - distribution: 'temurin' - token: ${{ github.token }} - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v6 - 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' + cache_read_only: 'false' - name: Load secrets env: @@ -216,25 +201,10 @@ jobs: -Dorg.gradle.workers.max=4 -Dkotlin.daemon.jvm.options=-Xmx2g -XX:+UseParallelGC steps: - - name: Checkout code - uses: actions/checkout@v6 + - name: Gradle Setup + uses: ./.github/actions/gradle-setup with: - ref: ${{ inputs.tag_name }} - fetch-depth: 0 - submodules: 'recursive' - - name: Set up JDK 17 - uses: actions/setup-java@v5 - with: - java-version: '17' - distribution: 'temurin' - token: ${{ github.token }} - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v6 - 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' + cache_read_only: 'false' - name: Load secrets env: @@ -288,27 +258,10 @@ jobs: GRADLE_CACHE_USERNAME: ${{ secrets.GRADLE_CACHE_USERNAME }} GRADLE_CACHE_PASSWORD: ${{ secrets.GRADLE_CACHE_PASSWORD }} steps: - - name: Checkout code - uses: actions/checkout@v6 + - name: Gradle Setup + uses: ./.github/actions/gradle-setup with: - ref: ${{ inputs.tag_name }} - fetch-depth: 0 - submodules: 'recursive' - - - name: Set up JDK 17 - uses: actions/setup-java@v5 - with: - java-version: '17' - distribution: 'temurin' - token: ${{ github.token }} - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v6 - 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' + cache_read_only: 'false' - name: Install dependencies for AppImage if: runner.os == 'Linux' diff --git a/.github/workflows/reusable-check.yml b/.github/workflows/reusable-check.yml index c880de771..69fb1190b 100644 --- a/.github/workflows/reusable-check.yml +++ b/.github/workflows/reusable-check.yml @@ -58,34 +58,24 @@ jobs: permissions: contents: read timeout-minutes: 60 + outputs: + cache_read_only: ${{ steps.cache_config.outputs.cache_read_only }} steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - fetch-depth: 0 - submodules: 'recursive' + - name: Determine cache read-only setting + id: cache_config + shell: bash + run: | + if [[ "${{ github.ref }}" == "refs/heads/main" ]] || [[ "${{ github.event_name }}" == "merge_group" ]] || [[ "${{ github.ref }}" == gh-readonly-queue/* ]]; then + echo "cache_read_only=false" >> "$GITHUB_OUTPUT" + else + echo "cache_read_only=true" >> "$GITHUB_OUTPUT" + fi - - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@v6 - - - name: Set up JDK 17 - uses: actions/setup-java@v5 + - name: Gradle Setup + uses: ./.github/actions/gradle-setup with: - java-version: '17' - distribution: 'temurin' - token: ${{ github.token }} - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v6 - with: - cache-read-only: ${{ github.ref != 'refs/heads/main' && github.event_name != 'merge_group' && !startsWith(github.ref, 'refs/heads/gh-readonly-queue/') }} - cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - cache-cleanup: on-success - build-scan-publish: true - build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' - build-scan-terms-of-use-agree: 'yes' - add-job-summary: always + cache_read_only: ${{ steps.cache_config.outputs.cache_read_only }} - name: Code Style & Static Analysis if: inputs.run_lint == true @@ -138,38 +128,17 @@ jobs: permissions: contents: read timeout-minutes: 60 + needs: host-check strategy: fail-fast: true matrix: api_level: ${{ fromJson(inputs.api_levels) }} steps: - - name: Checkout code - uses: actions/checkout@v6 + - name: Gradle Setup + uses: ./.github/actions/gradle-setup with: - fetch-depth: 0 - submodules: 'recursive' - - - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@v6 - - - name: Set up JDK 17 - uses: actions/setup-java@v5 - with: - java-version: '17' - distribution: 'temurin' - token: ${{ github.token }} - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v6 - with: - cache-read-only: ${{ github.ref != 'refs/heads/main' && github.event_name != 'merge_group' && !startsWith(github.ref, 'refs/heads/gh-readonly-queue/') }} - cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - cache-cleanup: on-success - build-scan-publish: true - build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' - build-scan-terms-of-use-agree: 'yes' - add-job-summary: always + cache_read_only: ${{ needs.host-check.outputs.cache_read_only }} - name: Determine matrix metadata id: matrix_meta @@ -266,3 +235,28 @@ jobs: **/build/outputs/androidTest-results retention-days: 7 if-no-files-found: ignore + + build-desktop: + name: Build Desktop + runs-on: ubuntu-latest + permissions: + contents: read + timeout-minutes: 60 + needs: host-check + + steps: + - name: Gradle Setup + uses: ./.github/actions/gradle-setup + with: + cache_read_only: ${{ needs.host-check.outputs.cache_read_only }} + + - name: Build Desktop + run: ./gradlew :desktop:assemble -Pci=true --scan + + - name: Upload Desktop artifact + if: ${{ inputs.upload_artifacts }} + uses: actions/upload-artifact@v7 + with: + name: desktop-app + path: desktop/build/libs/*.jar + retention-days: 7 diff --git a/.github/workflows/scheduled-updates.yml b/.github/workflows/scheduled-updates.yml index 3fdec9f9d..6b6f68273 100644 --- a/.github/workflows/scheduled-updates.yml +++ b/.github/workflows/scheduled-updates.yml @@ -81,21 +81,10 @@ jobs: - name: Fix file permissions run: sudo chown -R $USER:$USER . - - name: Set up JDK 17 - uses: actions/setup-java@v5 + - name: Gradle Setup + uses: ./.github/actions/gradle-setup with: - java-version: '17' - distribution: 'temurin' - token: ${{ github.token }} - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v6 - 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' - add-job-summary: always + cache_read_only: 'false' - name: Update Graphs run: ./gradlew graphUpdate diff --git a/AGENTS.md b/AGENTS.md index c4375c016..f80e848fa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,7 +8,7 @@ For execution-focused recipes, see `docs/agent-playbooks/README.md`. Meshtastic-Android is a Kotlin Multiplatform (KMP) application for off-grid, decentralized mesh networks. The goal is to decouple business logic from the Android framework, enabling future expansion to iOS and other platforms while maintaining a high-performance native Android experience. - **Language:** Kotlin (primary), AIDL. -- **Build System:** Gradle (Kotlin DSL). JDK 17 is REQUIRED. +- **Build System:** Gradle (Kotlin DSL). JDK 21 is REQUIRED. - **Target SDK:** API 36. Min SDK: API 26 (Android 8.0). - **Flavors:** - `fdroid`: Open source only, no tracking/analytics. @@ -100,7 +100,7 @@ Meshtastic-Android is a Kotlin Multiplatform (KMP) application for off-grid, dec ## 4. Execution Protocol ### A. Environment Setup -1. **JDK 17 MUST be used** to prevent Gradle sync/build failures. +1. **JDK 21 MUST be used** to prevent Gradle sync/build failures. 2. **Secrets:** You must copy `secrets.defaults.properties` to `local.properties`: ```properties MAPS_API_KEY=dummy_key @@ -129,7 +129,7 @@ Always run commands in the following order to ensure reliability. Do not attempt ./gradlew testFdroidDebug testGoogleDebug # Flavor-specific unit tests ./gradlew lintFdroidDebug lintGoogleDebug # Flavor-specific lint checks ``` -*Note: If testing Compose UI on the JVM (Robolectric) with Java 17, pin your tests to `@Config(sdk = [34])` to avoid SDK 35 compatibility crashes.* +*Note: If testing Compose UI on the JVM (Robolectric) with Java 21, pin your tests to `@Config(sdk = [34])` to avoid SDK 35 compatibility crashes.* **CI workflow conventions (GitHub Actions):** - Reusable CI is split into a host job and an Android matrix job in `.github/workflows/reusable-check.yml`. @@ -155,6 +155,6 @@ Update documentation continuously as part of the same change. If you modify arch ## 5. Troubleshooting - **Build Failures:** Check `gradle/libs.versions.toml` for dependency conflicts. - **Missing Secrets:** Check `local.properties`. -- **JDK Version:** JDK 17 is required. +- **JDK Version:** JDK 21 is required. - **Configuration Cache:** Add `--no-configuration-cache` flag if cache-related issues persist. - **Koin Injection Failures:** Verify the KMP component is included in `app` root module wiring (`AppKoinModule`). \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d64fe9976..d4fe0b740 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,7 +48,7 @@ Meshtastic-Android uses unit tests, Robolectric JVM tests, and instrumented UI t - **Unit tests** are located in the `src/test/` directory of each module. - **Compose UI Tests (JVM)** are preferred for component testing and are also located in `src/test/` using **Robolectric**. - - Note: If using Java 17, pin your Robolectric tests to `@Config(sdk = [34])` to avoid SDK 35 compatibility issues. + - Note: If using Java 21, pin your Robolectric tests to `@Config(sdk = [34])` to avoid SDK 35 compatibility issues. - **Instrumented tests** (including full E2E UI tests) are located in `src/androidTest/`. For Compose UI, use the [Jetpack Compose Testing APIs](https://developer.android.com/jetpack/compose/testing). #### Guidelines for Testing diff --git a/GEMINI.md b/GEMINI.md index 0db75b419..16432e35e 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -8,7 +8,7 @@ For execution-focused recipes, see `docs/agent-playbooks/README.md`. Meshtastic-Android is a Kotlin Multiplatform (KMP) application for off-grid, decentralized mesh networks. The goal is to decouple business logic from the Android framework, enabling future expansion to iOS and other platforms while maintaining a high-performance native Android experience. - **Language:** Kotlin (primary), AIDL. -- **Build System:** Gradle (Kotlin DSL). JDK 17 is REQUIRED. +- **Build System:** Gradle (Kotlin DSL). JDK 21 is REQUIRED. - **Target SDK:** API 36. Min SDK: API 26 (Android 8.0). - **Flavors:** - `fdroid`: Open source only, no tracking/analytics. @@ -96,7 +96,7 @@ Meshtastic-Android is a Kotlin Multiplatform (KMP) application for off-grid, dec ## 4. Execution Protocol ### A. Environment Setup -1. **JDK 17 MUST be used** to prevent Gradle sync/build failures. +1. **JDK 21 MUST be used** to prevent Gradle sync/build failures. 2. **Secrets:** You must copy `secrets.defaults.properties` to `local.properties`: ```properties MAPS_API_KEY=dummy_key @@ -125,7 +125,7 @@ Always run commands in the following order to ensure reliability. Do not attempt ./gradlew testFdroidDebug testGoogleDebug # Flavor-specific unit tests ./gradlew lintFdroidDebug lintGoogleDebug # Flavor-specific lint checks ``` -*Note: If testing Compose UI on the JVM (Robolectric) with Java 17, pin your tests to `@Config(sdk = [34])` to avoid SDK 35 compatibility crashes.* +*Note: If testing Compose UI on the JVM (Robolectric) with Java 21, pin your tests to `@Config(sdk = [34])` to avoid SDK 35 compatibility crashes.* **CI workflow conventions (GitHub Actions):** - Reusable CI is split into a host job and an Android matrix job in `.github/workflows/reusable-check.yml`. @@ -151,6 +151,6 @@ Update documentation continuously as part of the same change. If you modify arch ## 5. Troubleshooting - **Build Failures:** Check `gradle/libs.versions.toml` for dependency conflicts. - **Missing Secrets:** Check `local.properties`. -- **JDK Version:** JDK 17 is required. +- **JDK Version:** JDK 21 is required. - **Configuration Cache:** Add `--no-configuration-cache` flag if cache-related issues persist. - **Koin Injection Failures:** Verify the KMP component is included in `app` root module wiring (`AppKoinModule`). \ No newline at end of file diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index 858b0a708..faaeb9f68 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -25,14 +25,14 @@ plugins { group = "org.meshtastic.buildlogic" -// Configure the build-logic plugins to target JDK 17 +// Configure the build-logic plugins to target JDK 21 // This improves compatibility for developers building the project or consuming its libraries. java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } -kotlin { compilerOptions { jvmTarget = JvmTarget.JVM_17 } } +kotlin { compilerOptions { jvmTarget = JvmTarget.JVM_21 } } dependencies { // This allows the use of the 'libs' type-safe accessor in the Kotlin source of the plugins diff --git a/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/KotlinAndroid.kt b/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/KotlinAndroid.kt index fd9f307ad..7f96dd45a 100644 --- a/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/KotlinAndroid.kt +++ b/build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/KotlinAndroid.kt @@ -49,8 +49,13 @@ internal fun Project.configureKotlinAndroid(commonExtension: CommonExtension) { defaultConfig.targetSdk = targetSdkVersion } - compileOptions.sourceCompatibility = JavaVersion.VERSION_17 - compileOptions.targetCompatibility = JavaVersion.VERSION_17 + val javaVersion = if (project.name in listOf("api", "model", "proto")) { + JavaVersion.VERSION_17 + } else { + JavaVersion.VERSION_21 + } + compileOptions.sourceCompatibility = javaVersion + compileOptions.targetCompatibility = javaVersion } configureMokkery() @@ -170,9 +175,10 @@ internal fun Project.configureKotlinJvm() { /** Configure base Kotlin options */ private inline fun Project.configureKotlin() { extensions.configure { - // Using Java 17 for better compatibility with consumers (e.g. plugins, older environments) - // while still supporting modern Kotlin features. - jvmToolchain(17) + val javaVersion = if (project.name in listOf("api", "model", "proto")) 17 else 21 + // Using Java 17 for published modules for better compatibility with consumers (e.g. plugins, older environments), + // and Java 21 for the rest of the app. + jvmToolchain(javaVersion) if (this is KotlinMultiplatformExtension) { targets.configureEach { @@ -201,7 +207,8 @@ private inline fun Project.configureKotlin() { tasks.withType().configureEach { compilerOptions { - jvmTarget.set(JvmTarget.JVM_17) + val isPublishedModule = project.name in listOf("api", "model", "proto") + jvmTarget.set(if (isPublishedModule) JvmTarget.JVM_17 else JvmTarget.JVM_21) allWarningsAsErrors.set(warningsAsErrors) freeCompilerArgs.addAll( // Enable experimental coroutines APIs, including Flow diff --git a/jitpack.yml b/jitpack.yml index 351ecead5..b3935efcb 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -1,5 +1,5 @@ jdk: - - openjdk17 + - openjdk21 before_install: - ./gradlew --stop install: