Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Meshtastic-Android is a Kotlin Multiplatform (KMP) application for off-grid, dec
| Directory | Description |
| :--- | :--- |
| `app/` | 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.library`, `meshtastic.koin`). |
| `build-logic/` | Convention plugins for shared build configuration (e.g., `meshtastic.kmp.feature`, `meshtastic.kmp.library`, `meshtastic.koin`). |
| `config/` | Detekt static analysis rules (`config/detekt/detekt.yml`) and Spotless formatting config (`config/spotless/.editorconfig`). |
| `docs/` | Architecture docs and agent playbooks. See `docs/agent-playbooks/README.md` for version baseline and task recipes. |
| `core/model` | Domain models and common data structures. |
Expand All @@ -50,7 +50,7 @@ Meshtastic-Android is a Kotlin Multiplatform (KMP) application for off-grid, dec
| `core/ble/` | Bluetooth Low Energy stack using Nordic libraries. |
| `core/resources/` | Centralized string and image resources (Compose Multiplatform). |
| `core/testing/` | **Shared test doubles, fakes, and utilities for `commonTest` across all KMP modules.** |
| `feature/` | Feature modules (e.g., `settings`, `map`, `messaging`, `node`, `intro`, `connections`). All are KMP with `jvm()` target. |
| `feature/` | Feature modules (e.g., `settings`, `map`, `messaging`, `node`, `intro`, `connections`). All are KMP with `jvm()` target. Use `meshtastic.kmp.feature` convention plugin. |
| `desktop/` | Compose Desktop application — first non-Android KMP target. Nav 3 shell, full Koin DI graph, TCP transport with `want_config` handshake. |
| `mesh_service_example/` | Sample app showing `core:api` service integration. |

Expand All @@ -77,6 +77,7 @@ Meshtastic-Android is a Kotlin Multiplatform (KMP) application for off-grid, dec
- **JetBrains fork aliases:** Version catalog aliases for JetBrains-forked AndroidX artifacts use the `jetbrains-*` prefix (e.g., `jetbrains-lifecycle-runtime-compose`, `jetbrains-navigation3-ui`). Plain `androidx-*` aliases are true Google AndroidX artifacts. Never mix them up in `commonMain`.
- **Room KMP:** Always use `factory = { MeshtasticDatabaseConstructor.initialize() }` in `Room.databaseBuilder` and `inMemoryDatabaseBuilder`. DAOs and Entities reside in `commonMain`.
- **Testing:** Write ViewModel and business logic tests in `commonTest`. Use `core:testing` shared fakes.
- **Build-logic conventions:** In `build-logic/convention`, prefer lazy Gradle configuration (`configureEach`, `withPlugin`, provider APIs). Avoid `afterEvaluate` in convention plugins unless there is no viable lazy alternative.

### C. Namespacing
- **Standard:** Use the `org.meshtastic.*` namespace for all code.
Expand Down Expand Up @@ -116,6 +117,15 @@ Always run commands in the following order to ensure reliability. Do not attempt
```
*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.*

**CI workflow conventions (GitHub Actions):**
- Reusable CI is split into a host job and an Android matrix job in `.github/workflows/reusable-check.yml`.
- Host job runs style/static checks, explicit Android lint tasks, unit tests, and Kover XML coverage uploads once.
- Android matrix job runs explicit assemble tasks for `app` and `mesh_service_example`; instrumentation is enabled by input and matrix API.
- Prefer explicit Gradle task paths in CI (for example `app:lintFdroidDebug`, `app:connectedGoogleDebugAndroidTest`) instead of shorthand tasks like `lintDebug`.
- Pull request CI is main-only (`.github/workflows/pull-request.yml` targets `main` branch).
- Gradle cache writes are trusted on `main` and merge queue runs (`merge_group` / `gh-readonly-queue/*`); other refs use read-only cache mode in reusable CI.
- PR `check-changes` path filtering lives in `.github/workflows/pull-request.yml` and must include module dirs plus build/workflow entrypoints (`build-logic/**`, `gradle/**`, `.github/workflows/**`, `gradlew`, `settings.gradle.kts`, etc.) so CI is not skipped for infra-only changes.

### C. Documentation Sync
Update documentation continuously as part of the same change. If you modify architecture, module targets, CI tasks, validation commands, or agent workflow rules, update the relevant docs (`AGENTS.md`, `.github/copilot-instructions.md`, `GEMINI.md`, `docs/agent-playbooks/*`, `docs/kmp-status.md`, and `docs/decisions/architecture-review-2026-03.md`).

Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/merge-queue.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ jobs:
if: github.repository == 'meshtastic/Meshtastic-Android'
uses: ./.github/workflows/reusable-check.yml
with:
run_lint: true
run_unit_tests: true
run_instrumented_tests: true
api_levels: '[26, 35]' # Comprehensive testing for Merge Queue
upload_artifacts: false
secrets: inherit
Expand Down
74 changes: 70 additions & 4 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Pull Request CI

on:
pull_request:
branches: [ main, develop ]
branches: [ main ]
paths-ignore:
- '**.md'
- '**/*.md'
- 'docs/**'
- '.gitignore'

Expand All @@ -26,17 +26,78 @@ jobs:
with:
filters: |
android:
# CI/workflow implementation
- '.github/workflows/**'
- '.github/actions/**'
# Product modules validated by reusable-check
- 'app/**'
- 'baselineprofile/**'
- 'desktop/**'
- 'core/**'
- 'feature/**'
- 'mesh_service_example/**'
# Shared build infrastructure
- 'build-logic/**'
- 'config/**'
- 'gradle/**'
# Root build entrypoints/config that can alter task graph or outputs
- 'build.gradle.kts'
- 'config.properties'
- 'compose_compiler_config.conf'
- 'gradle.properties'
- 'gradlew'
- 'gradlew.bat'
- 'settings.gradle.kts'
- 'test.gradle.kts'

# 1b. FILTER DRIFT CHECK: Ensures check-changes stays aligned with module roots
verify-check-changes-filter:
if: github.repository == 'meshtastic/Meshtastic-Android' && !( github.head_ref == 'scheduled-updates' || github.head_ref == 'l10n_main' )
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Verify module roots are represented in check-changes filter
run: |
python3 - <<'PY'
import re
from pathlib import Path

settings = Path('settings.gradle.kts').read_text()
workflow = Path('.github/workflows/pull-request.yml').read_text()

module_roots = {
module.split(':')[0]
for module in re.findall(r'":([^"]+)"', settings)
}

allowed_extra_roots = {'baselineprofile'}
expected_roots = module_roots | allowed_extra_roots

filter_paths = {
path.split('/')[0]
for path in re.findall(r"-\s*'([^']+/\*\*)'", workflow)
}

actual_module_roots = filter_paths & expected_roots

missing = sorted(expected_roots - actual_module_roots)
unexpected = sorted(actual_module_roots - expected_roots)

if missing or unexpected:
print('check-changes filter drift detected:')
if missing:
print(' Missing roots:', ', '.join(missing))
if unexpected:
print(' Unexpected roots:', ', '.join(unexpected))
raise SystemExit(1)

print('check-changes filter is aligned with settings.gradle module roots.')
PY

# 2. VALIDATION & BUILD: Delegate to reusable-check.yml
# We disable instrumented tests for PRs to keep feedback fast (< 10 mins).
validate-and-build:
needs: check-changes
needs: [check-changes, verify-check-changes-filter]
if: needs.check-changes.outputs.android == 'true'
uses: ./.github/workflows/reusable-check.yml
with:
Expand All @@ -51,11 +112,16 @@ jobs:
check-workflow-status:
name: Check Workflow Status
runs-on: ubuntu-latest
needs: [check-changes, validate-and-build]
needs: [check-changes, verify-check-changes-filter, validate-and-build]
if: always()
steps:
- name: Check Workflow Status
run: |
if [[ "${{ needs.verify-check-changes-filter.result }}" == "failure" || "${{ needs.verify-check-changes-filter.result }}" == "cancelled" ]]; then
echo "::error::check-changes filter verification failed"
exit 1
fi

# If changes were detected but build failed, fail the status check
if [[ "${{ needs.check-changes.outputs.android }}" == "true" && ("${{ needs.validate-and-build.result }}" == "failure" || "${{ needs.validate-and-build.result }}" == "cancelled") ]]; then
echo "::error::Android Check failed"
Expand Down
Loading
Loading