mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-06-01 22:19:18 +02:00
refactor(build): rename entry modules and remove DESKTOP_ONLY mode (#5476)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -18,7 +18,7 @@ git submodule update --init
|
|||||||
./gradlew :core:data:allTests
|
./gradlew :core:data:allTests
|
||||||
|
|
||||||
# Single module tests (Android-only module like :app)
|
# Single module tests (Android-only module like :app)
|
||||||
./gradlew :app:testFdroidDebugUnitTest
|
./gradlew :androidApp:testFdroidDebugUnitTest
|
||||||
|
|
||||||
# Cross-platform compilation check (no tests)
|
# Cross-platform compilation check (no tests)
|
||||||
./gradlew kmpSmokeCompile
|
./gradlew kmpSmokeCompile
|
||||||
@@ -46,7 +46,7 @@ KMP modules have different task names than pure-Android modules. Using the wrong
|
|||||||
|
|
||||||
## Quick Reference
|
## Quick Reference
|
||||||
|
|
||||||
- **Architecture**: KMP project (Android, Desktop, iOS). Business logic in `commonMain`; platform shells (`app/`, `desktop/`) wire DI and host UI. See `AGENTS.md` and `.skills/kmp-architecture/`.
|
- **Architecture**: KMP project (Android, Desktop, iOS). Business logic in `commonMain`; platform shells (`androidApp/`, `desktopApp/`) wire DI and host UI. See `AGENTS.md` and `.skills/kmp-architecture/`.
|
||||||
- **Flavors**: `fdroid` (OSS) / `google` (Maps + DataDog). Only one installable at a time (different signing keys).
|
- **Flavors**: `fdroid` (OSS) / `google` (Maps + DataDog). Only one installable at a time (different signing keys).
|
||||||
- **Verify before push**: Run `./gradlew spotlessApply detekt assembleDebug test allTests`, then confirm CI with `gh pr checks <PR>`.
|
- **Verify before push**: Run `./gradlew spotlessApply detekt assembleDebug test allTests`, then confirm CI with `gh pr checks <PR>`.
|
||||||
- **Strings**: `stringResource(Res.string.key)` — run `python3 scripts/sort-strings.py` after adding strings.
|
- **Strings**: `stringResource(Res.string.key)` — run `python3 scripts/sort-strings.py` after adding strings.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ excludeAgent: "code-review"
|
|||||||
|
|
||||||
# CI Workflow Rules
|
# CI Workflow Rules
|
||||||
|
|
||||||
- Prefer explicit Gradle task paths (`app:lintFdroidDebug`) over shorthand (`lintDebug`).
|
- Prefer explicit Gradle task paths (`androidApp:lintFdroidDebug`) over shorthand (`lintDebug`).
|
||||||
- CI uses `.github/ci-gradle.properties` — don't assume local `gradle.properties` values.
|
- CI uses `.github/ci-gradle.properties` — don't assume local `gradle.properties` values.
|
||||||
- CI passes `-Pci=true` to enable full processor usage via `maxParallelForks`.
|
- CI passes `-Pci=true` to enable full processor usage via `maxParallelForks`.
|
||||||
- Use `fetch-depth: 0` only where needed (spotless ratcheting, version code). Use `fetch-depth: 1` otherwise.
|
- Use `fetch-depth: 0` only where needed (spotless ratcheting, version code). Use `fetch-depth: 1` otherwise.
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ on:
|
|||||||
- main
|
- main
|
||||||
paths:
|
paths:
|
||||||
# Only rebuild docs when source code changes (Dokka generates from KDoc)
|
# Only rebuild docs when source code changes (Dokka generates from KDoc)
|
||||||
- 'app/src/**'
|
- 'androidApp/src/**'
|
||||||
- 'core/**/src/**'
|
- 'core/**/src/**'
|
||||||
- 'feature/**/src/**'
|
- 'feature/**/src/**'
|
||||||
- 'desktop/src/**'
|
- 'desktopApp/src/**'
|
||||||
- 'build-logic/**'
|
- 'build-logic/**'
|
||||||
- 'build.gradle.kts'
|
- 'build.gradle.kts'
|
||||||
- 'settings.gradle.kts'
|
- 'settings.gradle.kts'
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ jobs:
|
|||||||
- '.github/workflows/**'
|
- '.github/workflows/**'
|
||||||
- '.github/actions/**'
|
- '.github/actions/**'
|
||||||
# Product modules validated by reusable-check
|
# Product modules validated by reusable-check
|
||||||
- 'app/**'
|
- 'androidApp/**'
|
||||||
- 'baselineprofile/**'
|
- 'baselineprofile/**'
|
||||||
- 'desktop/**'
|
- 'desktopApp/**'
|
||||||
- 'core/**'
|
- 'core/**'
|
||||||
- 'feature/**'
|
- 'feature/**'
|
||||||
- 'screenshot-tests/**'
|
- 'screenshot-tests/**'
|
||||||
@@ -95,9 +95,9 @@ jobs:
|
|||||||
PY
|
PY
|
||||||
|
|
||||||
# 2. VALIDATION & BUILD: Delegate to reusable-check.yml
|
# 2. VALIDATION & BUILD: Delegate to reusable-check.yml
|
||||||
# We disable coverage for PRs to keep feedback fast (< 10 mins).
|
# We disable coverage and desktop builds for PRs to keep feedback fast
|
||||||
# Desktop compilation is covered by :desktop:test in the shard-app test shard.
|
# (< 10 mins). Desktop compilation is already covered by the :desktopApp:test
|
||||||
# Native desktop packaging (createDistributable) only runs in release.yml.
|
# task in the shard-app test shard.
|
||||||
validate-and-build:
|
validate-and-build:
|
||||||
needs: check-changes
|
needs: check-changes
|
||||||
if: needs.check-changes.outputs.android == 'true'
|
if: needs.check-changes.outputs.android == 'true'
|
||||||
|
|||||||
@@ -150,9 +150,9 @@ jobs:
|
|||||||
GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }}
|
GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }}
|
||||||
GOOGLE_PLAY_JSON_KEY: ${{ secrets.GOOGLE_PLAY_JSON_KEY }}
|
GOOGLE_PLAY_JSON_KEY: ${{ secrets.GOOGLE_PLAY_JSON_KEY }}
|
||||||
run: |
|
run: |
|
||||||
rm -f ./app/google-services.json
|
rm -f ./androidApp/google-services.json
|
||||||
echo $GSERVICES > ./app/google-services.json
|
echo $GSERVICES > ./androidApp/google-services.json
|
||||||
echo $KEYSTORE | base64 -di > ./app/$KEYSTORE_FILENAME
|
echo $KEYSTORE | base64 -di > ./androidApp/$KEYSTORE_FILENAME
|
||||||
echo "$KEYSTORE_PROPERTIES" > ./keystore.properties
|
echo "$KEYSTORE_PROPERTIES" > ./keystore.properties
|
||||||
echo "datadogApplicationId=$DATADOG_APPLICATION_ID" >> ./secrets.properties
|
echo "datadogApplicationId=$DATADOG_APPLICATION_ID" >> ./secrets.properties
|
||||||
echo "datadogClientToken=$DATADOG_CLIENT_TOKEN" >> ./secrets.properties
|
echo "datadogClientToken=$DATADOG_CLIENT_TOKEN" >> ./secrets.properties
|
||||||
@@ -172,14 +172,14 @@ jobs:
|
|||||||
run: bundle exec fastlane internal
|
run: bundle exec fastlane internal
|
||||||
|
|
||||||
- name: List outputs
|
- name: List outputs
|
||||||
run: ls -R app/build/outputs/
|
run: ls -R androidApp/build/outputs/
|
||||||
|
|
||||||
- name: Upload Google AAB artifact
|
- name: Upload Google AAB artifact
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: google-aab
|
name: google-aab
|
||||||
path: app/build/outputs/bundle/googleRelease/app-google-release.aab
|
path: androidApp/build/outputs/bundle/googleRelease/androidApp-google-release.aab
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
- name: Upload Google APK artifact
|
- name: Upload Google APK artifact
|
||||||
@@ -187,20 +187,20 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: google-apk
|
name: google-apk
|
||||||
path: app/build/outputs/apk/google/release/*.apk
|
path: androidApp/build/outputs/apk/google/release/*.apk
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
- name: Attest Google AAB provenance
|
- name: Attest Google AAB provenance
|
||||||
if: success()
|
if: success()
|
||||||
uses: actions/attest-build-provenance@v4
|
uses: actions/attest-build-provenance@v4
|
||||||
with:
|
with:
|
||||||
subject-path: app/build/outputs/bundle/googleRelease/app-google-release.aab
|
subject-path: androidApp/build/outputs/bundle/googleRelease/androidApp-google-release.aab
|
||||||
|
|
||||||
- name: Attest Google APK provenance
|
- name: Attest Google APK provenance
|
||||||
if: success()
|
if: success()
|
||||||
uses: actions/attest-build-provenance@v4
|
uses: actions/attest-build-provenance@v4
|
||||||
with:
|
with:
|
||||||
subject-path: app/build/outputs/apk/google/release/*.apk
|
subject-path: androidApp/build/outputs/apk/google/release/*.apk
|
||||||
|
|
||||||
release-fdroid:
|
release-fdroid:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@@ -229,7 +229,7 @@ jobs:
|
|||||||
KEYSTORE_FILENAME: ${{ secrets.KEYSTORE_FILENAME }}
|
KEYSTORE_FILENAME: ${{ secrets.KEYSTORE_FILENAME }}
|
||||||
KEYSTORE_PROPERTIES: ${{ secrets.KEYSTORE_PROPERTIES }}
|
KEYSTORE_PROPERTIES: ${{ secrets.KEYSTORE_PROPERTIES }}
|
||||||
run: |
|
run: |
|
||||||
echo $KEYSTORE | base64 -di > ./app/$KEYSTORE_FILENAME
|
echo $KEYSTORE | base64 -di > ./androidApp/$KEYSTORE_FILENAME
|
||||||
echo "$KEYSTORE_PROPERTIES" > ./keystore.properties
|
echo "$KEYSTORE_PROPERTIES" > ./keystore.properties
|
||||||
|
|
||||||
- name: Setup Fastlane
|
- name: Setup Fastlane
|
||||||
@@ -245,21 +245,21 @@ jobs:
|
|||||||
run: bundle exec fastlane fdroid_build
|
run: bundle exec fastlane fdroid_build
|
||||||
|
|
||||||
- name: List outputs
|
- name: List outputs
|
||||||
run: ls -R app/build/outputs/
|
run: ls -R androidApp/build/outputs/
|
||||||
|
|
||||||
- name: Upload F-Droid APK artifact
|
- name: Upload F-Droid APK artifact
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: fdroid-apk
|
name: fdroid-apk
|
||||||
path: app/build/outputs/apk/fdroid/release/*.apk
|
path: androidApp/build/outputs/apk/fdroid/release/*.apk
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
- name: Attest F-Droid APK provenance
|
- name: Attest F-Droid APK provenance
|
||||||
if: success()
|
if: success()
|
||||||
uses: actions/attest-build-provenance@v4
|
uses: actions/attest-build-provenance@v4
|
||||||
with:
|
with:
|
||||||
subject-path: app/build/outputs/apk/fdroid/release/*.apk
|
subject-path: androidApp/build/outputs/apk/fdroid/release/*.apk
|
||||||
|
|
||||||
release-desktop:
|
release-desktop:
|
||||||
if: ${{ inputs.build_desktop }}
|
if: ${{ inputs.build_desktop }}
|
||||||
@@ -307,13 +307,13 @@ jobs:
|
|||||||
# `-PaboutLibraries.release=true` as member access on `-PaboutLibraries`,
|
# `-PaboutLibraries.release=true` as member access on `-PaboutLibraries`,
|
||||||
# splitting the token and feeding `.release=true` to Gradle as a task name.
|
# splitting the token and feeding `.release=true` to Gradle as a task name.
|
||||||
run: >
|
run: >
|
||||||
./gradlew :desktop:packageReleaseDistributionForCurrentOS
|
./gradlew :desktopApp:packageReleaseDistributionForCurrentOS
|
||||||
${{ contains(runner.os, 'macOS') && ':desktop:packageReleaseUberJarForCurrentOS' || '' }}
|
${{ contains(runner.os, 'macOS') && ':desktopApp:packageReleaseUberJarForCurrentOS' || '' }}
|
||||||
'-PaboutLibraries.release=true' --no-daemon
|
'-PaboutLibraries.release=true' --no-daemon
|
||||||
|
|
||||||
- name: List Desktop Binaries
|
- name: List Desktop Binaries
|
||||||
if: runner.os == 'Linux' || runner.os == 'macOS'
|
if: runner.os == 'Linux' || runner.os == 'macOS'
|
||||||
run: ls -R desktop/build/compose/binaries/main-release
|
run: ls -R desktopApp/build/compose/binaries/main-release
|
||||||
|
|
||||||
- name: Upload Desktop Artifacts
|
- name: Upload Desktop Artifacts
|
||||||
if: always()
|
if: always()
|
||||||
@@ -321,13 +321,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: desktop-${{ runner.os }}-${{ runner.arch }}
|
name: desktop-${{ runner.os }}-${{ runner.arch }}
|
||||||
path: |
|
path: |
|
||||||
desktop/build/compose/binaries/main-release/*/*.dmg
|
desktopApp/build/compose/binaries/main-release/*/*.dmg
|
||||||
desktop/build/compose/binaries/main-release/*/*.msi
|
desktopApp/build/compose/binaries/main-release/*/*.msi
|
||||||
desktop/build/compose/binaries/main-release/*/*.exe
|
desktopApp/build/compose/binaries/main-release/*/*.exe
|
||||||
desktop/build/compose/binaries/main-release/*/*.deb
|
desktopApp/build/compose/binaries/main-release/*/*.deb
|
||||||
desktop/build/compose/binaries/main-release/*/*.rpm
|
desktopApp/build/compose/binaries/main-release/*/*.rpm
|
||||||
desktop/build/compose/binaries/main-release/*/*.AppImage
|
desktopApp/build/compose/binaries/main-release/*/*.AppImage
|
||||||
desktop/build/compose/jars/*-release.jar
|
desktopApp/build/compose/jars/*-release.jar
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
if-no-files-found: ignore
|
if-no-files-found: ignore
|
||||||
|
|
||||||
@@ -336,13 +336,13 @@ jobs:
|
|||||||
uses: actions/attest-build-provenance@v4
|
uses: actions/attest-build-provenance@v4
|
||||||
with:
|
with:
|
||||||
subject-path: |
|
subject-path: |
|
||||||
desktop/build/compose/binaries/main-release/*/*.dmg
|
desktopApp/build/compose/binaries/main-release/*/*.dmg
|
||||||
desktop/build/compose/binaries/main-release/*/*.msi
|
desktopApp/build/compose/binaries/main-release/*/*.msi
|
||||||
desktop/build/compose/binaries/main-release/*/*.exe
|
desktopApp/build/compose/binaries/main-release/*/*.exe
|
||||||
desktop/build/compose/binaries/main-release/*/*.deb
|
desktopApp/build/compose/binaries/main-release/*/*.deb
|
||||||
desktop/build/compose/binaries/main-release/*/*.rpm
|
desktopApp/build/compose/binaries/main-release/*/*.rpm
|
||||||
desktop/build/compose/binaries/main-release/*/*.AppImage
|
desktopApp/build/compose/binaries/main-release/*/*.AppImage
|
||||||
desktop/build/compose/jars/*-release.jar
|
desktopApp/build/compose/jars/*-release.jar
|
||||||
|
|
||||||
create-flatpak-src:
|
create-flatpak-src:
|
||||||
if: ${{ inputs.build_flatpak_src }}
|
if: ${{ inputs.build_flatpak_src }}
|
||||||
@@ -370,22 +370,7 @@ jobs:
|
|||||||
gradle_encryption_key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
gradle_encryption_key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||||
cache_read_only: 'true'
|
cache_read_only: 'true'
|
||||||
|
|
||||||
- name: Python Setup
|
|
||||||
uses: actions/setup-python@v6
|
|
||||||
with:
|
|
||||||
python-version: '3.x'
|
|
||||||
|
|
||||||
- name: Install ast-grep
|
|
||||||
run: pip install ast-grep-cli
|
|
||||||
|
|
||||||
# Remove Android/iOS targets and other non-desktop bits
|
|
||||||
# that would break the flatpakGradleGenerator
|
|
||||||
- name: Prepare Offline Desktop Build
|
|
||||||
run: ./scripts/desktop-only-prep.sh
|
|
||||||
|
|
||||||
- name: Generate Flatpak Sources
|
- name: Generate Flatpak Sources
|
||||||
env:
|
|
||||||
DESKTOP_ONLY: true
|
|
||||||
run: >
|
run: >
|
||||||
./gradlew :build-logic:convention:flatpakGradleGenerator flatpakGradleGenerator
|
./gradlew :build-logic:convention:flatpakGradleGenerator flatpakGradleGenerator
|
||||||
--no-configuration-cache --refresh-dependencies --no-parallel
|
--no-configuration-cache --refresh-dependencies --no-parallel
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Lint, Analysis & KMP Smoke Compile
|
- name: Lint, Analysis & KMP Smoke Compile
|
||||||
if: inputs.run_lint == true
|
if: inputs.run_lint == true
|
||||||
run: ./gradlew spotlessCheck detekt app:lintFdroidDebug app:lintGoogleDebug core:barcode:lintFdroidDebug core:barcode:lintGoogleDebug core:api:lintDebug kmpSmokeCompile -Pci=true --continue --scan
|
run: ./gradlew spotlessCheck detekt androidApp:lintFdroidDebug androidApp:lintGoogleDebug core:barcode:lintFdroidDebug core:barcode:lintGoogleDebug core:api:lintDebug kmpSmokeCompile -Pci=true --continue --scan
|
||||||
|
|
||||||
- name: KMP Smoke Compile (lint skipped)
|
- name: KMP Smoke Compile (lint skipped)
|
||||||
if: inputs.run_lint == false
|
if: inputs.run_lint == false
|
||||||
@@ -152,13 +152,13 @@ jobs:
|
|||||||
# See: https://github.com/meshtastic/Meshtastic-Android/issues/3231
|
# See: https://github.com/meshtastic/Meshtastic-Android/issues/3231
|
||||||
|
|
||||||
echo "── Step 1: Verify aboutlibraries.json determinism ──"
|
echo "── Step 1: Verify aboutlibraries.json determinism ──"
|
||||||
rm -f app/src/main/resources/aboutlibraries.json
|
rm -f androidApp/src/main/resources/aboutlibraries.json
|
||||||
./gradlew :app:exportLibraryDefinitions -Pci=true --no-configuration-cache
|
./gradlew :androidApp:exportLibraryDefinitions -Pci=true --no-configuration-cache
|
||||||
cp app/src/main/resources/aboutlibraries.json /tmp/aboutlibraries-run1.json
|
cp androidApp/src/main/resources/aboutlibraries.json /tmp/aboutlibraries-run1.json
|
||||||
|
|
||||||
rm -f app/src/main/resources/aboutlibraries.json
|
rm -f androidApp/src/main/resources/aboutlibraries.json
|
||||||
./gradlew :app:exportLibraryDefinitions -Pci=true --no-configuration-cache --rerun-tasks
|
./gradlew :androidApp:exportLibraryDefinitions -Pci=true --no-configuration-cache --rerun-tasks
|
||||||
cp app/src/main/resources/aboutlibraries.json /tmp/aboutlibraries-run2.json
|
cp androidApp/src/main/resources/aboutlibraries.json /tmp/aboutlibraries-run2.json
|
||||||
|
|
||||||
if ! diff -q /tmp/aboutlibraries-run1.json /tmp/aboutlibraries-run2.json; then
|
if ! diff -q /tmp/aboutlibraries-run1.json /tmp/aboutlibraries-run2.json; then
|
||||||
echo "::error::aboutlibraries.json is NOT deterministic across runs!"
|
echo "::error::aboutlibraries.json is NOT deterministic across runs!"
|
||||||
@@ -168,9 +168,9 @@ jobs:
|
|||||||
echo "✅ aboutlibraries.json is deterministic"
|
echo "✅ aboutlibraries.json is deterministic"
|
||||||
|
|
||||||
echo "── Step 2: Build fdroid release APK ──"
|
echo "── Step 2: Build fdroid release APK ──"
|
||||||
./gradlew :app:assembleFdroidRelease -Pci=true -Pmeshtastic.disableAbiSplits=true --no-configuration-cache
|
./gradlew :androidApp:assembleFdroidRelease -Pci=true -Pmeshtastic.disableAbiSplits=true --no-configuration-cache
|
||||||
|
|
||||||
APK=$(find app/build/outputs/apk/fdroid/release -name "*.apk" | head -1)
|
APK=$(find androidApp/build/outputs/apk/fdroid/release -name "*.apk" | head -1)
|
||||||
if [ -z "$APK" ]; then
|
if [ -z "$APK" ]; then
|
||||||
echo "::error::No fdroid release APK found"
|
echo "::error::No fdroid release APK found"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -295,7 +295,7 @@ jobs:
|
|||||||
# Tests are split into 3 shards that run in parallel:
|
# Tests are split into 3 shards that run in parallel:
|
||||||
# shard-core: core:* KMP module tests (allTests)
|
# shard-core: core:* KMP module tests (allTests)
|
||||||
# shard-feature: feature:* KMP module tests (allTests)
|
# shard-feature: feature:* KMP module tests (allTests)
|
||||||
# shard-app: Pure-Android/JVM tests (app, desktop, core:barcode, etc.)
|
# shard-app: Pure-Android/JVM tests (androidApp, desktopApp, core:barcode, etc.)
|
||||||
test-shards:
|
test-shards:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
permissions:
|
permissions:
|
||||||
@@ -359,17 +359,17 @@ jobs:
|
|||||||
:feature:settings:koverXmlReport
|
:feature:settings:koverXmlReport
|
||||||
- name: shard-app
|
- name: shard-app
|
||||||
tasks: >-
|
tasks: >-
|
||||||
:app:testFdroidDebugUnitTest
|
:androidApp:testFdroidDebugUnitTest
|
||||||
:app:testGoogleDebugUnitTest
|
:androidApp:testGoogleDebugUnitTest
|
||||||
:desktop:test
|
:desktopApp:test
|
||||||
:core:barcode:testFdroidDebugUnitTest
|
:core:barcode:testFdroidDebugUnitTest
|
||||||
:core:barcode:testGoogleDebugUnitTest
|
:core:barcode:testGoogleDebugUnitTest
|
||||||
kover: >-
|
kover: >-
|
||||||
:app:koverXmlReportFdroidDebug
|
:androidApp:koverXmlReportFdroidDebug
|
||||||
:app:koverXmlReportGoogleDebug
|
:androidApp:koverXmlReportGoogleDebug
|
||||||
:core:barcode:koverXmlReportFdroidDebug
|
:core:barcode:koverXmlReportFdroidDebug
|
||||||
:core:barcode:koverXmlReportGoogleDebug
|
:core:barcode:koverXmlReportGoogleDebug
|
||||||
:desktop:koverXmlReport
|
:desktopApp:koverXmlReport
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
@@ -447,14 +447,14 @@ jobs:
|
|||||||
cache_read_only: ${{ needs.lint-check.outputs.cache_read_only }}
|
cache_read_only: ${{ needs.lint-check.outputs.cache_read_only }}
|
||||||
|
|
||||||
- name: Build Android APKs
|
- name: Build Android APKs
|
||||||
run: ./gradlew app:assembleFdroidDebug app:assembleGoogleDebug -Pci=true --parallel --configuration-cache --continue --scan
|
run: ./gradlew androidApp:assembleFdroidDebug androidApp:assembleGoogleDebug -Pci=true --parallel --configuration-cache --continue --scan
|
||||||
|
|
||||||
- name: Upload debug artifact
|
- name: Upload debug artifact
|
||||||
if: ${{ inputs.upload_artifacts }}
|
if: ${{ inputs.upload_artifacts }}
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: app-debug-apks
|
name: app-debug-apks
|
||||||
path: app/build/outputs/apk/*/debug/*.apk
|
path: androidApp/build/outputs/apk/*/debug/*.apk
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
- name: Report App Size
|
- name: Report App Size
|
||||||
@@ -463,8 +463,47 @@ jobs:
|
|||||||
echo "### App Size Report" >> $GITHUB_STEP_SUMMARY
|
echo "### App Size Report" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "| Artifact | Size |" >> $GITHUB_STEP_SUMMARY
|
echo "| Artifact | Size |" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "| --- | --- |" >> $GITHUB_STEP_SUMMARY
|
echo "| --- | --- |" >> $GITHUB_STEP_SUMMARY
|
||||||
find app/build/outputs/apk -name "*.apk" -exec du -h {} + | awk '{print "| " $2 " | " $1 " |"}' >> $GITHUB_STEP_SUMMARY
|
find androidApp/build/outputs/apk -name "*.apk" -exec du -h {} + | awk '{print "| " $2 " | " $1 " |"}' >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
# ── Desktop Build ───────────────────────────────────────────────────
|
||||||
|
build-desktop:
|
||||||
|
name: Build Desktop Debug (${{ matrix.os }})
|
||||||
|
if: inputs.run_desktop_builds == true
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
timeout-minutes: 60
|
||||||
|
needs: lint-check
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, windows-latest, ubuntu-24.04, ubuntu-24.04-arm]
|
||||||
|
env:
|
||||||
|
VERSION_CODE: ${{ needs.lint-check.outputs.version_code }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Gradle Setup
|
||||||
|
uses: ./.github/actions/gradle-setup
|
||||||
|
with:
|
||||||
|
gradle_encryption_key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||||
|
cache_read_only: ${{ needs.lint-check.outputs.cache_read_only }}
|
||||||
|
|
||||||
|
- name: Build Desktop
|
||||||
|
run: ./gradlew :desktopApp:createDistributable -Pci=true --scan
|
||||||
|
|
||||||
|
- name: Upload Desktop artifact
|
||||||
|
if: ${{ inputs.upload_artifacts }}
|
||||||
|
uses: actions/upload-artifact@v7
|
||||||
|
with:
|
||||||
|
name: desktop-app-${{ runner.os }}-${{ runner.arch }}
|
||||||
|
path: desktopApp/build/compose/binaries/main/app/
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
# ── Flatpak Sources ───────────────────────────────────────────────────
|
# ── Flatpak Sources ───────────────────────────────────────────────────
|
||||||
build-flatpak-src:
|
build-flatpak-src:
|
||||||
@@ -494,23 +533,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
gradle_encryption_key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
gradle_encryption_key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||||
cache_read_only: true
|
cache_read_only: true
|
||||||
|
|
||||||
- name: Python Setup
|
|
||||||
uses: actions/setup-python@v6
|
|
||||||
with:
|
|
||||||
python-version: '3.x'
|
|
||||||
|
|
||||||
- name: Install ast-grep
|
|
||||||
run: pip install ast-grep-cli
|
|
||||||
|
|
||||||
# Remove Android/iOS targets and other non-desktop bits
|
|
||||||
# that would break the flatpakGradleGenerator
|
|
||||||
- name: Prepare Offline Desktop Build
|
|
||||||
run: ./scripts/desktop-only-prep.sh
|
|
||||||
|
|
||||||
- name: Generate Flatpak Sources
|
- name: Generate Flatpak Sources
|
||||||
env:
|
|
||||||
DESKTOP_ONLY: true
|
|
||||||
run: >
|
run: >
|
||||||
./gradlew :build-logic:convention:flatpakGradleGenerator flatpakGradleGenerator
|
./gradlew :build-logic:convention:flatpakGradleGenerator flatpakGradleGenerator
|
||||||
--no-configuration-cache --refresh-dependencies --no-parallel
|
--no-configuration-cache --refresh-dependencies --no-parallel
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
- name: Update firmware releases list
|
- name: Update firmware releases list
|
||||||
id: firmware
|
id: firmware
|
||||||
run: |
|
run: |
|
||||||
firmware_file_path="app/src/main/assets/firmware_releases.json"
|
firmware_file_path="androidApp/src/main/assets/firmware_releases.json"
|
||||||
temp_firmware_file="/tmp/new_firmware_releases.json"
|
temp_firmware_file="/tmp/new_firmware_releases.json"
|
||||||
|
|
||||||
echo "Fetching latest firmware releases..."
|
echo "Fetching latest firmware releases..."
|
||||||
@@ -51,7 +51,7 @@ jobs:
|
|||||||
- name: Update hardware list
|
- name: Update hardware list
|
||||||
id: hardware
|
id: hardware
|
||||||
run: |
|
run: |
|
||||||
hardware_file_path="app/src/main/assets/device_hardware.json"
|
hardware_file_path="androidApp/src/main/assets/device_hardware.json"
|
||||||
temp_hardware_file="/tmp/new_device_hardware.json"
|
temp_hardware_file="/tmp/new_device_hardware.json"
|
||||||
|
|
||||||
echo "Fetching latest device hardware data..."
|
echo "Fetching latest device hardware data..."
|
||||||
@@ -172,8 +172,8 @@ jobs:
|
|||||||
base: 'main'
|
base: 'main'
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
add-paths: |
|
add-paths: |
|
||||||
app/src/main/assets/firmware_releases.json
|
androidApp/src/main/assets/firmware_releases.json
|
||||||
app/src/main/assets/device_hardware.json
|
androidApp/src/main/assets/device_hardware.json
|
||||||
fastlane/metadata/android/**
|
fastlane/metadata/android/**
|
||||||
**/strings.xml
|
**/strings.xml
|
||||||
**/README.md
|
**/README.md
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ When reviewing code, meticulously verify the following categories. Flag any devi
|
|||||||
- [ ] **Compose Multiplatform Resources:** Ensure NO hardcoded strings. Must use `core:resources` (e.g., `stringResource(Res.string.key)` or asynchronous `getStringSuspend(Res.string.key)` for ViewModels/Coroutines). NEVER use blocking `getString()` in a coroutine.
|
- [ ] **Compose Multiplatform Resources:** Ensure NO hardcoded strings. Must use `core:resources` (e.g., `stringResource(Res.string.key)` or asynchronous `getStringSuspend(Res.string.key)` for ViewModels/Coroutines). NEVER use blocking `getString()` in a coroutine.
|
||||||
- [ ] **String Formatting:** CMP only supports `%N$s` and `%N$d`. Flag any float formats (`%N$.1f`) in Compose string resources; they must be pre-formatted using `NumberFormatter.format()` from `core:common`. Use `MetricFormatter` for metric-specific displays (temperature, voltage, current, percent, humidity, pressure, SNR, RSSI).
|
- [ ] **String Formatting:** CMP only supports `%N$s` and `%N$d`. Flag any float formats (`%N$.1f`) in Compose string resources; they must be pre-formatted using `NumberFormatter.format()` from `core:common`. Use `MetricFormatter` for metric-specific displays (temperature, voltage, current, percent, humidity, pressure, SNR, RSSI).
|
||||||
- [ ] **Centralized Dialogs & Alerts:** Flag inline alert-rendering logic. Mandate the use of `AlertHost(alertManager)` or `SharedDialogs` from `core:ui/commonMain`.
|
- [ ] **Centralized Dialogs & Alerts:** Flag inline alert-rendering logic. Mandate the use of `AlertHost(alertManager)` or `SharedDialogs` from `core:ui/commonMain`.
|
||||||
- [ ] **Placeholders:** Require `PlaceholderScreen(name)` from `core:ui/commonMain` for unimplemented desktop/JVM features. No inline placeholders in feature modules.
|
- [ ] **Placeholders:** Require `PlaceholderScreen(name)` from `core:ui/commonMain` for unimplemented desktopApp/JVM features. No inline placeholders in feature modules.
|
||||||
- [ ] **Adaptive Layouts:** Verify use of `currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true)` to support desktop/tablet breakpoints (≥ 1200dp).
|
- [ ] **Adaptive Layouts:** Verify use of `currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true)` to support desktopApp/tablet breakpoints (≥ 1200dp).
|
||||||
|
|
||||||
### 3. Navigation & State
|
### 3. Navigation & State
|
||||||
- [ ] **Shared Navigation Graphs:** Feature navigation graphs must be defined as extension functions on `EntryProviderScope<NavKey>` in `commonMain` (e.g., `fun EntryProviderScope<NavKey>.settingsGraph(...)`). Flag any graphs defined in platform-specific source sets.
|
- [ ] **Shared Navigation Graphs:** Feature navigation graphs must be defined as extension functions on `EntryProviderScope<NavKey>` in `commonMain` (e.g., `fun EntryProviderScope<NavKey>.settingsGraph(...)`). Flag any graphs defined in platform-specific source sets.
|
||||||
@@ -56,8 +56,8 @@ When reviewing code, meticulously verify the following categories. Flag any devi
|
|||||||
- [ ] **Robolectric Configuration:** Check that Compose UI tests running via Robolectric on JVM are pinned to `@Config(sdk = [34])` to prevent Java 21 / SDK 35 compatibility issues.
|
- [ ] **Robolectric Configuration:** Check that Compose UI tests running via Robolectric on JVM are pinned to `@Config(sdk = [34])` to prevent Java 21 / SDK 35 compatibility issues.
|
||||||
|
|
||||||
### 8. ProGuard / R8 Rules
|
### 8. ProGuard / R8 Rules
|
||||||
- [ ] **New Dependencies:** If a new reflection-heavy dependency is added (DI, serialization, JNI, ServiceLoader), verify keep rules exist in **both** `app/proguard-rules.pro` (R8) and `desktop/proguard-rules.pro` (ProGuard). The two files must stay aligned.
|
- [ ] **New Dependencies:** If a new reflection-heavy dependency is added (DI, serialization, JNI, ServiceLoader), verify keep rules exist in **both** `androidApp/proguard-rules.pro` (R8) and `desktopApp/proguard-rules.pro` (ProGuard). The two files must stay aligned.
|
||||||
- [ ] **Release Smoke-Test:** For dependency or ProGuard rule changes, verify `assembleRelease` and `./gradlew :desktop:runRelease` succeed.
|
- [ ] **Release Smoke-Test:** For dependency or ProGuard rule changes, verify `assembleRelease` and `./gradlew :desktopApp:runRelease` succeed.
|
||||||
|
|
||||||
## Review Output Guidelines
|
## Review Output Guidelines
|
||||||
1. **Be Specific & Constructive:** Provide exact file references and code snippets illustrating the required project pattern.
|
1. **Be Specific & Constructive:** Provide exact file references and code snippets illustrating the required project pattern.
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
Guidelines for building shared UI, adaptive layouts, and handling strings/resources in Meshtastic-Android. The codebase uses Material 3 Adaptive.
|
Guidelines for building shared UI, adaptive layouts, and handling strings/resources in Meshtastic-Android. The codebase uses Material 3 Adaptive.
|
||||||
|
|
||||||
## 1. UI Components & Layouts
|
## 1. UI Components & Layouts
|
||||||
- **Material 3 / Adaptive:** Use `currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true)` to support Large (1200dp) and XL (1600dp) breakpoints. Investigate 3-pane "Power User" scenes using Navigation 3 Scenes and draggable dividers for desktop/tablets.
|
- **Material 3 / Adaptive:** Use `currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true)` to support Large (1200dp) and XL (1600dp) breakpoints. Investigate 3-pane "Power User" scenes using Navigation 3 Scenes and draggable dividers for desktopApp/tablets.
|
||||||
- **Dialogs & Alerts:** Use centralized components like `AlertHost(alertManager)` from `core:ui/commonMain`. Do NOT trigger alerts inline or duplicate alert logic. Use `SharedDialogs(uiViewModel)` for general popups.
|
- **Dialogs & Alerts:** Use centralized components like `AlertHost(alertManager)` from `core:ui/commonMain`. Do NOT trigger alerts inline or duplicate alert logic. Use `SharedDialogs(uiViewModel)` for general popups.
|
||||||
- **Placeholders:** Use `PlaceholderScreen(name)` from `core:ui/commonMain` for unimplemented desktop/JVM features.
|
- **Placeholders:** Use `PlaceholderScreen(name)` from `core:ui/commonMain` for unimplemented desktopApp/JVM features.
|
||||||
- **Theme Picker:** Use `ThemePickerDialog` from `feature:settings/commonMain`.
|
- **Theme Picker:** Use `ThemePickerDialog` from `feature:settings/commonMain`.
|
||||||
- **Platform Implementations:** Inject platform-specific behavior (e.g., Map providers) via `CompositionLocal` from the `app` or `desktop` shells. Do not tightly couple Google Maps/osmdroid dependencies to `commonMain`.
|
- **Platform Implementations:** Inject platform-specific behavior (e.g., Map providers) via `CompositionLocal` from the `app` or `desktop` shells. Do not tightly couple Google Maps/osmdroid dependencies to `commonMain`.
|
||||||
|
|
||||||
@@ -58,4 +58,4 @@ Choose the right tool for the job:
|
|||||||
## Reference Anchors
|
## Reference Anchors
|
||||||
- **Shared Strings:** `core/resources/src/commonMain/composeResources/values/strings.xml`
|
- **Shared Strings:** `core/resources/src/commonMain/composeResources/values/strings.xml`
|
||||||
- **Platform abstraction contract:** `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/MapViewProvider.kt`
|
- **Platform abstraction contract:** `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/MapViewProvider.kt`
|
||||||
- **Provider wiring:** `app/src/main/kotlin/org/meshtastic/app/MainActivity.kt`
|
- **Provider wiring:** `androidApp/src/main/kotlin/org/meshtastic/app/MainActivity.kt`
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ A step-by-step workflow for implementing a new feature in the Meshtastic-Android
|
|||||||
```bash
|
```bash
|
||||||
./gradlew spotlessCheck spotlessApply detekt assembleDebug test allTests
|
./gradlew spotlessCheck spotlessApply detekt assembleDebug test allTests
|
||||||
```
|
```
|
||||||
- If the feature adds a new reflection-heavy dependency, add keep rules to **both** `app/proguard-rules.pro` and `desktop/proguard-rules.pro`, then verify release builds:
|
- If the feature adds a new reflection-heavy dependency, add keep rules to **both** `androidApp/proguard-rules.pro` and `desktopApp/proguard-rules.pro`, then verify release builds:
|
||||||
```bash
|
```bash
|
||||||
./gradlew assembleFdroidRelease :desktop:runRelease
|
./gradlew assembleFdroidRelease :desktopApp:runRelease
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ Guidelines on managing Kotlin Multiplatform (KMP) source-sets, expected abstract
|
|||||||
1. Ensure all new logic compiles against the KMP core (`jvm()`, `iosArm64()`, etc.).
|
1. Ensure all new logic compiles against the KMP core (`jvm()`, `iosArm64()`, etc.).
|
||||||
2. Do not use platform-specific constructs in `commonMain` or you break the iOS/Desktop builds.
|
2. Do not use platform-specific constructs in `commonMain` or you break the iOS/Desktop builds.
|
||||||
3. Test using `kmpSmokeCompile` to verify cross-platform compilation.
|
3. Test using `kmpSmokeCompile` to verify cross-platform compilation.
|
||||||
4. For desktop wiring, copy the pattern in `desktop/src/main/kotlin/org/meshtastic/desktop/di/DesktopKoinModule.kt` and use `NoopStubs.kt` to temporarily mock missing platform implementations.
|
4. For desktop wiring, copy the pattern in `desktopApp/src/main/kotlin/org/meshtastic/desktop/di/DesktopKoinModule.kt` and use `NoopStubs.kt` to temporarily mock missing platform implementations.
|
||||||
|
|
||||||
## Reference Anchors
|
## Reference Anchors
|
||||||
- **Shared Okio I/O:** `core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCase.kt`
|
- **Shared Okio I/O:** `core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCase.kt`
|
||||||
- **Desktop DI Stubs:** `desktop/src/main/kotlin/org/meshtastic/desktop/stub/NoopStubs.kt`
|
- **Desktop DI Stubs:** `desktopApp/src/main/kotlin/org/meshtastic/desktop/stub/NoopStubs.kt`
|
||||||
- **Version Catalog:** `gradle/libs.versions.toml`
|
- **Version Catalog:** `gradle/libs.versions.toml`
|
||||||
- **Convention Plugins:** `build-logic/convention/`
|
- **Convention Plugins:** `build-logic/convention/`
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ This skill covers dependency injection (Koin Annotations 4.2.x) and JetBrains Na
|
|||||||
|
|
||||||
### Guidelines
|
### Guidelines
|
||||||
1. **Annotations First:** Use `@Module`, `@ComponentScan`, and `@KoinViewModel` annotations directly in `commonMain` shared modules to encapsulate dependency graphs per feature.
|
1. **Annotations First:** Use `@Module`, `@ComponentScan`, and `@KoinViewModel` annotations directly in `commonMain` shared modules to encapsulate dependency graphs per feature.
|
||||||
2. **App Root Assembly:** Don't assume feature/core `@Module` classes are active automatically. Ensure they are included by the app root module (`@Module(includes = [...])`) in `app/src/main/kotlin/org/meshtastic/app/di/AppKoinModule.kt` and `desktop/.../DesktopKoinModule.kt`.
|
2. **App Root Assembly:** Don't assume feature/core `@Module` classes are active automatically. Ensure they are included by the app root module (`@Module(includes = [...])`) in `androidApp/src/main/kotlin/org/meshtastic/app/di/AppKoinModule.kt` and `desktopApp/.../DesktopKoinModule.kt`.
|
||||||
3. **No Platform Bleed:** Don't put Android framework dependencies (`Context`, `Activity`, `Application`) into shared `commonMain` business logic. Inject interfaces instead.
|
3. **No Platform Bleed:** Don't put Android framework dependencies (`Context`, `Activity`, `Application`) into shared `commonMain` business logic. Inject interfaces instead.
|
||||||
4. **Resolution:** Resolve app-layer wrappers via `koinViewModel()` or injected bindings within Compose navigation graphs.
|
4. **Resolution:** Resolve app-layer wrappers via `koinViewModel()` or injected bindings within Compose navigation graphs.
|
||||||
|
|
||||||
@@ -49,8 +49,8 @@ startKoin<AndroidKoinApp> {
|
|||||||
- **Custom Backstack Mutation:** Do **not** mutate back navigation with custom stacks disconnected from the app backstack. Mutate `NavBackStack<NavKey>` directly with `add(...)` and `removeLastOrNull()`.
|
- **Custom Backstack Mutation:** Do **not** mutate back navigation with custom stacks disconnected from the app backstack. Mutate `NavBackStack<NavKey>` directly with `add(...)` and `removeLastOrNull()`.
|
||||||
|
|
||||||
## Reference Anchors
|
## Reference Anchors
|
||||||
- **App Startup / Koin Bootstrap:** `app/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.kt`
|
- **App Startup / Koin Bootstrap:** `androidApp/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.kt`
|
||||||
- **DI Bootstrap Object:** `app/src/main/kotlin/org/meshtastic/app/di/AndroidKoinApp.kt`
|
- **DI Bootstrap Object:** `androidApp/src/main/kotlin/org/meshtastic/app/di/AndroidKoinApp.kt`
|
||||||
- **DI App Wiring:** `app/src/main/kotlin/org/meshtastic/app/di/AppKoinModule.kt`
|
- **DI App Wiring:** `androidApp/src/main/kotlin/org/meshtastic/app/di/AppKoinModule.kt`
|
||||||
- **Shared Routes:** `core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/Routes.kt`
|
- **Shared Routes:** `core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/Routes.kt`
|
||||||
- **Desktop Nav Shell:** `desktop/src/main/kotlin/org/meshtastic/desktop/ui/DesktopMainScreen.kt`
|
- **Desktop Nav Shell:** `desktopApp/src/main/kotlin/org/meshtastic/desktop/ui/DesktopMainScreen.kt`
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Module directory, namespacing conventions, environment setup, and troubleshootin
|
|||||||
|
|
||||||
| Directory | Description |
|
| Directory | Description |
|
||||||
| :--- | :--- |
|
| :--- | :--- |
|
||||||
| `app/` | Main application module. Contains `MainActivity`, Koin DI modules, and app-level logic. Uses package `org.meshtastic.app`. |
|
| `androidApp/` | Main application module. Contains `MainActivity`, Koin DI modules, and app-level logic. Uses package `org.meshtastic.app`. |
|
||||||
| `build-logic/` | Convention plugins for shared build configuration (e.g., `meshtastic.kmp.feature`, `meshtastic.kmp.library`, `meshtastic.kmp.jvm.android`, `meshtastic.koin`). |
|
| `build-logic/` | Convention plugins for shared build configuration (e.g., `meshtastic.kmp.feature`, `meshtastic.kmp.library`, `meshtastic.kmp.jvm.android`, `meshtastic.koin`). |
|
||||||
| `config/` | Detekt static analysis rules (`config/detekt/detekt.yml`) and Spotless formatting config (`config/spotless/.editorconfig`). |
|
| `config/` | Detekt static analysis rules (`config/detekt/detekt.yml`) and Spotless formatting config (`config/spotless/.editorconfig`). |
|
||||||
| `docs/` | Architecture docs and agent playbooks. See `docs/kmp-status.md` and `docs/roadmap.md` for current status. |
|
| `docs/` | Architecture docs and agent playbooks. See `docs/kmp-status.md` and `docs/roadmap.md` for current status. |
|
||||||
@@ -38,7 +38,7 @@ Module directory, namespacing conventions, environment setup, and troubleshootin
|
|||||||
| `feature/` | Feature modules (e.g., `settings`, `map`, `messaging`, `node`, `intro`, `connections`, `firmware`, `wifi-provision`, `widget`). All are KMP except `widget`. Use `meshtastic.kmp.feature` convention plugin. |
|
| `feature/` | Feature modules (e.g., `settings`, `map`, `messaging`, `node`, `intro`, `connections`, `firmware`, `wifi-provision`, `widget`). All are KMP except `widget`. Use `meshtastic.kmp.feature` convention plugin. |
|
||||||
| `feature/wifi-provision` | KMP WiFi provisioning via BLE (Nymea protocol). Uses `core:ble` Kable abstractions. |
|
| `feature/wifi-provision` | KMP WiFi provisioning via BLE (Nymea protocol). Uses `core:ble` Kable abstractions. |
|
||||||
| `feature/firmware` | Fully KMP firmware update system: Unified OTA (BLE + WiFi), native Nordic Secure DFU protocol (pure KMP), USB/UF2 updates, and `FirmwareRetriever` with manifest-based resolution. Desktop is a first-class target. |
|
| `feature/firmware` | Fully KMP firmware update system: Unified OTA (BLE + WiFi), native Nordic Secure DFU protocol (pure KMP), USB/UF2 updates, and `FirmwareRetriever` with manifest-based resolution. Desktop is a first-class target. |
|
||||||
| `desktop/` | Compose Desktop application. Thin host shell relying on feature modules for shared UI. Full Koin DI graph, TCP, Serial/USB, and BLE transports. Versioning via `config.properties` + `GitVersionValueSource`. |
|
| `desktopApp/` | Compose Desktop application. Thin host shell relying on feature modules for shared UI. Full Koin DI graph, TCP, Serial/USB, and BLE transports. Versioning via `config.properties` + `GitVersionValueSource`. |
|
||||||
|
|
||||||
## Namespacing
|
## Namespacing
|
||||||
- **Standard:** Use the `org.meshtastic.*` namespace for all code.
|
- **Standard:** Use the `org.meshtastic.*` namespace for all code.
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ Run in a single invocation for routine changes to ensure code formatting, analys
|
|||||||
> In KMP modules, the `test` task name is **ambiguous**. Gradle matches both `testAndroid` and
|
> In KMP modules, the `test` task name is **ambiguous**. Gradle matches both `testAndroid` and
|
||||||
> `testAndroidHostTest` and refuses to run either, silently skipping KMP modules.
|
> `testAndroidHostTest` and refuses to run either, silently skipping KMP modules.
|
||||||
> `allTests` is the `KotlinTestReport` lifecycle task registered by the KMP plugin.
|
> `allTests` is the `KotlinTestReport` lifecycle task registered by the KMP plugin.
|
||||||
> Conversely, `allTests` does **not** cover pure-Android modules (`:app`, `:core:api`, etc.), which is why both `test` and `allTests` are needed.
|
> Conversely, `allTests` does **not** cover pure-Android modules (`:androidApp`, `:core:api`, etc.), which is why both `test` and `allTests` are needed.
|
||||||
|
|
||||||
*Note: If testing Compose UI on the JVM (Robolectric) with Java 21, pin tests to `@Config(sdk = [34])` to avoid SDK 35 compatibility crashes.*
|
*Note: If testing Compose UI on the JVM (Robolectric) with Java 21, pin tests to `@Config(sdk = [34])` to avoid SDK 35 compatibility crashes.*
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"feature_directory":"specs/20260511-211823-compose-screenshot-testing"}
|
{"feature_directory":"specs/006-kmp-project-structure"}
|
||||||
|
|||||||
@@ -49,5 +49,5 @@ You are an expert Android/KMP engineer. Maintain architectural boundaries, use M
|
|||||||
<!-- SPECKIT START -->
|
<!-- SPECKIT START -->
|
||||||
For additional context about technologies to be used, project structure,
|
For additional context about technologies to be used, project structure,
|
||||||
shell commands, and other important information, read the current plan
|
shell commands, and other important information, read the current plan
|
||||||
at `specs/20260511-211823-compose-screenshot-testing/plan.md`
|
at `specs/006-kmp-project-structure/plan.md`
|
||||||
<!-- SPECKIT END -->
|
<!-- SPECKIT END -->
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ Thank you for your interest in contributing to Meshtastic-Android! We welcome co
|
|||||||
- Add comments where necessary, especially for complex logic.
|
- Add comments where necessary, especially for complex logic.
|
||||||
- Keep methods and classes focused and concise.
|
- Keep methods and classes focused and concise.
|
||||||
- **Strings:** Use localised strings via the **Compose Multiplatform Resource** library in `:core:resources`.
|
- **Strings:** Use localised strings via the **Compose Multiplatform Resource** library in `:core:resources`.
|
||||||
- Do **not** use the legacy `app/src/main/res/values/strings.xml`.
|
- Do **not** use the legacy `androidApp/src/main/res/values/strings.xml`.
|
||||||
- **Definition:** Add strings to `core/resources/src/commonMain/composeResources/values/strings.xml`.
|
- **Definition:** Add strings to `core/resources/src/commonMain/composeResources/values/strings.xml`.
|
||||||
- **Usage:**
|
- **Usage:**
|
||||||
```kotlin
|
```kotlin
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# `:app`
|
# `:androidApp`
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
The `:app` module is the entry point for the Meshtastic Android application. It orchestrates the various feature modules, manages global state, and provides the main UI shell.
|
The `:androidApp` module is the entry point for the Meshtastic Android application. It orchestrates the various feature modules, manages global state, and provides the main UI shell.
|
||||||
|
|
||||||
## Key Components
|
## Key Components
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ The `:app` module is the entry point for the Meshtastic Android application. It
|
|||||||
The single Activity of the application. It hosts the shared `MeshtasticNavDisplay` navigation shell and manages the root UI structure (Navigation Bar, Rail, etc.).
|
The single Activity of the application. It hosts the shared `MeshtasticNavDisplay` navigation shell and manages the root UI structure (Navigation Bar, Rail, etc.).
|
||||||
|
|
||||||
### 2. `MeshService`
|
### 2. `MeshService`
|
||||||
The core background service that manages long-running communication with the mesh radio. While it is declared in the `:app` manifest for system visibility, its implementation resides in the `:core:service` module. It runs as a **Foreground Service** to ensure reliable communication even when the app is in the background.
|
The core background service that manages long-running communication with the mesh radio. While it is declared in the `:androidApp` manifest for system visibility, its implementation resides in the `:core:service` module. It runs as a **Foreground Service** to ensure reliable communication even when the app is in the background.
|
||||||
|
|
||||||
### 3. Koin Application
|
### 3. Koin Application
|
||||||
`MeshUtilApplication` is the Koin entry point, providing the global dependency injection container.
|
`MeshUtilApplication` is the Koin entry point, providing the global dependency injection container.
|
||||||
@@ -24,34 +24,34 @@ The module primarily serves as a "glue" layer, connecting:
|
|||||||
<!--region graph-->
|
<!--region graph-->
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TB
|
graph TB
|
||||||
:app[app]:::android-application
|
:androidApp[androidApp]:::android-application
|
||||||
:app -.-> :core:ble
|
:androidApp -.-> :core:ble
|
||||||
:app -.-> :core:common
|
:androidApp -.-> :core:common
|
||||||
:app -.-> :core:data
|
:androidApp -.-> :core:data
|
||||||
:app -.-> :core:database
|
:androidApp -.-> :core:database
|
||||||
:app -.-> :core:datastore
|
:androidApp -.-> :core:datastore
|
||||||
:app -.-> :core:di
|
:androidApp -.-> :core:di
|
||||||
:app -.-> :core:domain
|
:androidApp -.-> :core:domain
|
||||||
:app -.-> :core:model
|
:androidApp -.-> :core:model
|
||||||
:app -.-> :core:navigation
|
:androidApp -.-> :core:navigation
|
||||||
:app -.-> :core:network
|
:androidApp -.-> :core:network
|
||||||
:app -.-> :core:nfc
|
:androidApp -.-> :core:nfc
|
||||||
:app -.-> :core:prefs
|
:androidApp -.-> :core:prefs
|
||||||
:app -.-> :core:proto
|
:androidApp -.-> :core:proto
|
||||||
:app -.-> :core:service
|
:androidApp -.-> :core:service
|
||||||
:app -.-> :core:resources
|
:androidApp -.-> :core:resources
|
||||||
:app -.-> :core:ui
|
:androidApp -.-> :core:ui
|
||||||
:app -.-> :core:barcode
|
:androidApp -.-> :core:barcode
|
||||||
:app -.-> :core:takserver
|
:androidApp -.-> :core:takserver
|
||||||
:app -.-> :feature:intro
|
:androidApp -.-> :feature:intro
|
||||||
:app -.-> :feature:messaging
|
:androidApp -.-> :feature:messaging
|
||||||
:app -.-> :feature:connections
|
:androidApp -.-> :feature:connections
|
||||||
:app -.-> :feature:map
|
:androidApp -.-> :feature:map
|
||||||
:app -.-> :feature:node
|
:androidApp -.-> :feature:node
|
||||||
:app -.-> :feature:settings
|
:androidApp -.-> :feature:settings
|
||||||
:app -.-> :feature:firmware
|
:androidApp -.-> :feature:firmware
|
||||||
:app -.-> :feature:wifi-provision
|
:androidApp -.-> :feature:wifi-provision
|
||||||
:app -.-> :feature:widget
|
:androidApp -.-> :feature:widget
|
||||||
|
|
||||||
classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
|
classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
|
||||||
classDef android-application-compose fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
|
classDef android-application-compose fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
|
||||||
@@ -116,7 +116,7 @@ configure<ApplicationExtension> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Disable ABI splits for bundle builds or when explicitly requested via Gradle property.
|
// Disable ABI splits for bundle builds or when explicitly requested via Gradle property.
|
||||||
// Usage: ./gradlew :app:bundleGoogleRelease -Pmeshtastic.disableAbiSplits=true
|
// Usage: ./gradlew :androidApp:bundleGoogleRelease -Pmeshtastic.disableAbiSplits=true
|
||||||
val disableSplits = providers.gradleProperty("meshtastic.disableAbiSplits").map { it.toBoolean() }.getOrElse(false)
|
val disableSplits = providers.gradleProperty("meshtastic.disableAbiSplits").map { it.toBoolean() }.getOrElse(false)
|
||||||
|
|
||||||
// Enable ABI splits to generate smaller APKs per architecture for F-Droid/IzzyOnDroid
|
// Enable ABI splits to generate smaller APKs per architecture for F-Droid/IzzyOnDroid
|
||||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user