mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat(ci): shard test suite and enable JUnit 5 parallel execution (#4977)
This commit is contained in:
parent
7e041c00e1
commit
51251ab16a
80 changed files with 438 additions and 2730 deletions
7
.github/actions/gradle-setup/action.yml
vendored
7
.github/actions/gradle-setup/action.yml
vendored
|
|
@ -13,6 +13,10 @@ inputs:
|
|||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Copy CI Gradle properties
|
||||
shell: bash
|
||||
run: mkdir -p ~/.gradle && cp .github/ci-gradle.properties ~/.gradle/gradle.properties
|
||||
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/actions/wrapper-validation@v6
|
||||
|
||||
|
|
@ -29,7 +33,4 @@ runs:
|
|||
cache-read-only: ${{ inputs.cache_read_only }}
|
||||
cache-encryption-key: ${{ inputs.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
|
||||
52
.github/ci-gradle.properties
vendored
Normal file
52
.github/ci-gradle.properties
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#
|
||||
# CI-specific Gradle properties.
|
||||
#
|
||||
# This file is copied to ~/.gradle/gradle.properties by the gradle-setup
|
||||
# composite action, overriding the dev-oriented values in the repo-root
|
||||
# gradle.properties. Inspired by the nowinandroid & sqldelight patterns.
|
||||
#
|
||||
|
||||
# ── Daemon ────────────────────────────────────────────────────────────
|
||||
# Single-use CI runners never reuse a daemon, so the startup cost is pure
|
||||
# overhead. Disabling it also avoids "daemon disappeared" warnings.
|
||||
org.gradle.daemon=false
|
||||
|
||||
# ── Memory ────────────────────────────────────────────────────────────
|
||||
# Standard GitHub runners have 7 GB RAM. Keep Gradle + Kotlin daemon
|
||||
# within budget (4g Gradle + 2g Kotlin daemon + 1g OS/tooling headroom).
|
||||
org.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8
|
||||
kotlin.daemon.jvm.options=-Xmx2g -XX:+UseParallelGC
|
||||
|
||||
# ── Parallelism ───────────────────────────────────────────────────────
|
||||
org.gradle.parallel=true
|
||||
org.gradle.workers.max=4
|
||||
|
||||
# ── Caching & Configuration ──────────────────────────────────────────
|
||||
org.gradle.caching=true
|
||||
org.gradle.configuration-cache=true
|
||||
org.gradle.configureondemand=false
|
||||
org.gradle.vfs.watch=false
|
||||
org.gradle.isolated-projects=true
|
||||
|
||||
# ── Kotlin ────────────────────────────────────────────────────────────
|
||||
# Incremental compilation is wasted on fresh CI checkouts (no prior build
|
||||
# state to diff against). Disabling avoids the overhead of maintaining
|
||||
# incremental state that will never be reused.
|
||||
kotlin.incremental=false
|
||||
kotlin.code.style=official
|
||||
kotlin.parallel.tasks.in.project=true
|
||||
|
||||
# ── KSP ──────────────────────────────────────────────────────────────
|
||||
# In CI, KSP incremental processing adds overhead without benefit (fresh
|
||||
# checkouts). Keep intermodule incremental off (no prior state).
|
||||
ksp.incremental=false
|
||||
ksp.run.in.process=true
|
||||
|
||||
# ── Android ──────────────────────────────────────────────────────────
|
||||
android.experimental.lint.analysisPerComponent=true
|
||||
# Disable unused build features to reduce build time
|
||||
android.defaults.buildfeatures.resvalues=false
|
||||
android.defaults.buildfeatures.shaders=false
|
||||
|
||||
# ── Misc ─────────────────────────────────────────────────────────────
|
||||
org.gradle.welcome=never
|
||||
5
.github/workflows/pull-request.yml
vendored
5
.github/workflows/pull-request.yml
vendored
|
|
@ -99,15 +99,16 @@ jobs:
|
|||
PY
|
||||
|
||||
# 2. VALIDATION & BUILD: Delegate to reusable-check.yml
|
||||
# We disable instrumented tests for PRs to keep feedback fast (< 10 mins).
|
||||
# We disable instrumented tests and coverage for PRs to keep feedback fast (< 10 mins).
|
||||
validate-and-build:
|
||||
needs: [check-changes, verify-check-changes-filter]
|
||||
needs: check-changes
|
||||
if: needs.check-changes.outputs.android == 'true'
|
||||
uses: ./.github/workflows/reusable-check.yml
|
||||
with:
|
||||
run_lint: true
|
||||
run_unit_tests: true
|
||||
run_instrumented_tests: false
|
||||
run_coverage: false
|
||||
api_levels: '[35]'
|
||||
upload_artifacts: true
|
||||
secrets: inherit
|
||||
|
|
|
|||
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
|
|
@ -113,11 +113,6 @@ jobs:
|
|||
GRADLE_CACHE_URL: ${{ secrets.GRADLE_CACHE_URL }}
|
||||
GRADLE_CACHE_USERNAME: ${{ secrets.GRADLE_CACHE_USERNAME }}
|
||||
GRADLE_CACHE_PASSWORD: ${{ secrets.GRADLE_CACHE_PASSWORD }}
|
||||
GRADLE_OPTS: >-
|
||||
-Dorg.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8
|
||||
-Dorg.gradle.vfs.watch=false
|
||||
-Dorg.gradle.workers.max=4
|
||||
-Dkotlin.daemon.jvm.options=-Xmx2g -XX:+UseParallelGC
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
|
@ -203,11 +198,6 @@ jobs:
|
|||
GRADLE_CACHE_URL: ${{ secrets.GRADLE_CACHE_URL }}
|
||||
GRADLE_CACHE_USERNAME: ${{ secrets.GRADLE_CACHE_USERNAME }}
|
||||
GRADLE_CACHE_PASSWORD: ${{ secrets.GRADLE_CACHE_PASSWORD }}
|
||||
GRADLE_OPTS: >-
|
||||
-Dorg.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8
|
||||
-Dorg.gradle.vfs.watch=false
|
||||
-Dorg.gradle.workers.max=4
|
||||
-Dkotlin.daemon.jvm.options=-Xmx2g -XX:+UseParallelGC
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
|
|
|||
203
.github/workflows/reusable-check.yml
vendored
203
.github/workflows/reusable-check.yml
vendored
|
|
@ -12,6 +12,9 @@ on:
|
|||
run_instrumented_tests:
|
||||
type: boolean
|
||||
default: true
|
||||
run_coverage:
|
||||
type: boolean
|
||||
default: true
|
||||
api_levels:
|
||||
type: string
|
||||
default: '[35]'
|
||||
|
|
@ -44,29 +47,28 @@ env:
|
|||
GRADLE_CACHE_URL: ${{ secrets.GRADLE_CACHE_URL }}
|
||||
GRADLE_CACHE_USERNAME: ${{ secrets.GRADLE_CACHE_USERNAME }}
|
||||
GRADLE_CACHE_PASSWORD: ${{ secrets.GRADLE_CACHE_PASSWORD }}
|
||||
# CI JVM tuning: override gradle.properties values (8g heap + 4g Kotlin daemon)
|
||||
# that exceed the 7GB RAM on ubuntu-24.04 standard runners.
|
||||
GRADLE_OPTS: >-
|
||||
-Dorg.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8
|
||||
-Dorg.gradle.vfs.watch=false
|
||||
-Dorg.gradle.workers.max=4
|
||||
-Dkotlin.daemon.jvm.options=-Xmx2g -XX:+UseParallelGC
|
||||
# Fallback VERSION_CODE for the lint-check job itself (which computes the real
|
||||
# value from git). Downstream jobs override this with the git-derived value.
|
||||
VERSION_CODE: ${{ github.run_number }}
|
||||
|
||||
jobs:
|
||||
host-check:
|
||||
# ── Lint & Static Analysis ──────────────────────────────────────────
|
||||
lint-check:
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: read
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 30
|
||||
outputs:
|
||||
cache_read_only: ${{ steps.cache_config.outputs.cache_read_only }}
|
||||
version_code: ${{ steps.version_code.outputs.version_code }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: 'recursive'
|
||||
filter: 'blob:none'
|
||||
submodules: true
|
||||
|
||||
- name: Determine cache read-only setting
|
||||
id: cache_config
|
||||
|
|
@ -78,64 +80,172 @@ jobs:
|
|||
echo "cache_read_only=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Calculate version code from git commit count
|
||||
id: version_code
|
||||
shell: bash
|
||||
run: |
|
||||
COMMIT_COUNT=$(git rev-list --count HEAD)
|
||||
OFFSET=$(grep '^VERSION_CODE_OFFSET=' config.properties | cut -d'=' -f2 || echo 0)
|
||||
VERSION_CODE=$((COMMIT_COUNT + OFFSET))
|
||||
echo "version_code=$VERSION_CODE" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Gradle Setup
|
||||
uses: ./.github/actions/gradle-setup
|
||||
with:
|
||||
gradle_encryption_key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
cache_read_only: ${{ steps.cache_config.outputs.cache_read_only }}
|
||||
|
||||
- name: Code Style & Static Analysis
|
||||
- name: Lint, Analysis & KMP Smoke Compile
|
||||
if: inputs.run_lint == true
|
||||
run: ./gradlew spotlessCheck detekt -Pci=true --scan
|
||||
run: ./gradlew spotlessCheck detekt app:lintFdroidDebug app:lintGoogleDebug core:barcode:lintFdroidDebug core:barcode:lintGoogleDebug core:api:lintDebug mesh_service_example:lintDebug kmpSmokeCompile -Pci=true --continue --scan
|
||||
|
||||
- name: Android Lint
|
||||
if: inputs.run_lint == true
|
||||
run: ./gradlew app:lintFdroidDebug app:lintGoogleDebug core:barcode:lintFdroidDebug core:barcode:lintGoogleDebug core:api:lintDebug mesh_service_example:lintDebug -Pci=true --continue --scan
|
||||
|
||||
- name: Shared Unit Tests & Coverage
|
||||
if: inputs.run_unit_tests == true
|
||||
run: ./gradlew test allTests koverXmlReport app:koverXmlReportFdroidDebug app:koverXmlReportGoogleDebug core:api:koverXmlReportDebug core:barcode:koverXmlReportFdroidDebug core:barcode:koverXmlReportGoogleDebug mesh_service_example:koverXmlReportDebug desktop:koverXmlReport -Pci=true --continue --scan
|
||||
|
||||
- name: KMP Smoke Compile
|
||||
- name: KMP Smoke Compile (lint skipped)
|
||||
if: inputs.run_lint == false
|
||||
run: ./gradlew kmpSmokeCompile -Pci=true --continue --scan
|
||||
|
||||
- name: Upload coverage results to Codecov
|
||||
if: ${{ !cancelled() && inputs.run_unit_tests }}
|
||||
uses: codecov/codecov-action@v6
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
slug: meshtastic/Meshtastic-Android
|
||||
flags: host-unit
|
||||
fail_ci_if_error: false
|
||||
files: "**/build/reports/kover/report*.xml"
|
||||
# ── Sharded Unit Tests ──────────────────────────────────────────────
|
||||
# Tests are split into 3 shards that run in parallel:
|
||||
# shard-core: core:* KMP module tests (allTests)
|
||||
# shard-feature: feature:* KMP module tests (allTests)
|
||||
# shard-app: Pure-Android/JVM tests (app, desktop, core:barcode, etc.)
|
||||
test-shards:
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: read
|
||||
timeout-minutes: 45
|
||||
needs: lint-check
|
||||
if: inputs.run_unit_tests == true
|
||||
env:
|
||||
VERSION_CODE: ${{ needs.lint-check.outputs.version_code }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard:
|
||||
- name: shard-core
|
||||
tasks: >-
|
||||
:core:ble:allTests
|
||||
:core:common:allTests
|
||||
:core:data:allTests
|
||||
:core:database:allTests
|
||||
:core:domain:allTests
|
||||
:core:model:allTests
|
||||
:core:navigation:allTests
|
||||
:core:network:allTests
|
||||
:core:prefs:allTests
|
||||
:core:repository:allTests
|
||||
:core:service:allTests
|
||||
:core:takserver:allTests
|
||||
:core:testing:allTests
|
||||
:core:ui:allTests
|
||||
kover: >-
|
||||
:core:ble:koverXmlReport
|
||||
:core:common:koverXmlReport
|
||||
:core:data:koverXmlReport
|
||||
:core:database:koverXmlReport
|
||||
:core:domain:koverXmlReport
|
||||
:core:model:koverXmlReport
|
||||
:core:navigation:koverXmlReport
|
||||
:core:network:koverXmlReport
|
||||
:core:prefs:koverXmlReport
|
||||
:core:repository:koverXmlReport
|
||||
:core:service:koverXmlReport
|
||||
:core:takserver:koverXmlReport
|
||||
:core:testing:koverXmlReport
|
||||
:core:ui:koverXmlReport
|
||||
- name: shard-feature
|
||||
tasks: >-
|
||||
:feature:connections:allTests
|
||||
:feature:firmware:allTests
|
||||
:feature:intro:allTests
|
||||
:feature:map:allTests
|
||||
:feature:messaging:allTests
|
||||
:feature:node:allTests
|
||||
:feature:settings:allTests
|
||||
kover: >-
|
||||
:feature:connections:koverXmlReport
|
||||
:feature:firmware:koverXmlReport
|
||||
:feature:intro:koverXmlReport
|
||||
:feature:map:koverXmlReport
|
||||
:feature:messaging:koverXmlReport
|
||||
:feature:node:koverXmlReport
|
||||
:feature:settings:koverXmlReport
|
||||
- name: shard-app
|
||||
tasks: >-
|
||||
:app:testFdroidDebugUnitTest
|
||||
:app:testGoogleDebugUnitTest
|
||||
:desktop:test
|
||||
:core:barcode:testFdroidDebugUnitTest
|
||||
:core:barcode:testGoogleDebugUnitTest
|
||||
:mesh_service_example:test
|
||||
kover: >-
|
||||
:app:koverXmlReportFdroidDebug
|
||||
:app:koverXmlReportGoogleDebug
|
||||
:core:barcode:koverXmlReportFdroidDebug
|
||||
:core:barcode:koverXmlReportGoogleDebug
|
||||
:desktop:koverXmlReport
|
||||
:mesh_service_example:koverXmlReportDebug
|
||||
|
||||
- name: Upload unit test results to Codecov
|
||||
if: ${{ !cancelled() && inputs.run_unit_tests }}
|
||||
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: Run Tests & Coverage (${{ matrix.shard.name }})
|
||||
run: |
|
||||
kover_tasks=""
|
||||
if [[ "${{ inputs.run_coverage }}" == "true" ]]; then
|
||||
kover_tasks="${{ matrix.shard.kover }}"
|
||||
fi
|
||||
./gradlew ${{ matrix.shard.tasks }} $kover_tasks -Pci=true --continue --scan
|
||||
|
||||
- name: Upload test results to Codecov
|
||||
if: ${{ !cancelled() }}
|
||||
uses: codecov/codecov-action@v6
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
slug: meshtastic/Meshtastic-Android
|
||||
flags: host-unit
|
||||
flags: ${{ matrix.shard.name }}
|
||||
fail_ci_if_error: false
|
||||
report_type: test_results
|
||||
files: "**/build/test-results/**/*.xml"
|
||||
|
||||
- name: Upload host reports
|
||||
- name: Upload coverage to Codecov
|
||||
if: ${{ !cancelled() }}
|
||||
uses: codecov/codecov-action@v6
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
slug: meshtastic/Meshtastic-Android
|
||||
flags: ${{ matrix.shard.name }}
|
||||
fail_ci_if_error: false
|
||||
files: "**/build/reports/kover/report*.xml"
|
||||
|
||||
- name: Upload shard reports
|
||||
if: ${{ always() && inputs.upload_artifacts }}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: reports-host
|
||||
name: reports-${{ matrix.shard.name }}
|
||||
path: |
|
||||
**/build/reports
|
||||
**/build/test-results
|
||||
retention-days: 7
|
||||
|
||||
# ── Android Build & Instrumented Tests ──────────────────────────────
|
||||
android-check:
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: read
|
||||
timeout-minutes: 60
|
||||
needs: host-check
|
||||
needs: lint-check
|
||||
env:
|
||||
VERSION_CODE: ${{ needs.lint-check.outputs.version_code }}
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
|
|
@ -145,14 +255,14 @@ jobs:
|
|||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: 'recursive'
|
||||
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.host-check.outputs.cache_read_only }}
|
||||
cache_read_only: ${{ needs.lint-check.outputs.cache_read_only }}
|
||||
|
||||
- name: Determine matrix metadata
|
||||
id: matrix_meta
|
||||
|
|
@ -235,7 +345,7 @@ jobs:
|
|||
- name: Report App Size
|
||||
if: ${{ always() && steps.matrix_meta.outputs.is_first_api == 'true' }}
|
||||
run: |
|
||||
echo "### 📦 App Size Report" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### App Size Report" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Artifact | Size |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| --- | --- |" >> $GITHUB_STEP_SUMMARY
|
||||
find app/build/outputs/apk -name "*.apk" -exec du -h {} + | awk '{print "| " $2 " | " $1 " |"}' >> $GITHUB_STEP_SUMMARY
|
||||
|
|
@ -250,26 +360,29 @@ jobs:
|
|||
retention-days: 7
|
||||
if-no-files-found: ignore
|
||||
|
||||
# ── Desktop Build ───────────────────────────────────────────────────
|
||||
build-desktop:
|
||||
name: Build Desktop Debug
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: read
|
||||
timeout-minutes: 60
|
||||
needs: host-check
|
||||
needs: lint-check
|
||||
env:
|
||||
VERSION_CODE: ${{ needs.lint-check.outputs.version_code }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: 'recursive'
|
||||
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.host-check.outputs.cache_read_only }}
|
||||
cache_read_only: ${{ needs.lint-check.outputs.cache_read_only }}
|
||||
|
||||
- name: Build Desktop
|
||||
run: ./gradlew :desktop:packageDistributionForCurrentOS -Pci=true --scan
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue